diff --git a/.ci/teamcity/default/build_plugins.sh b/.ci/teamcity/default/build_plugins.sh index 76c553b4f8fa..4b8759639223 100755 --- a/.ci/teamcity/default/build_plugins.sh +++ b/.ci/teamcity/default/build_plugins.sh @@ -14,6 +14,7 @@ node scripts/build_kibana_platform_plugins \ --scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \ --scan-dir "$XPACK_DIR/test/plugin_api_perf/plugins" \ --scan-dir "$XPACK_DIR/test/licensing_plugin/plugins" \ + --scan-dir "$XPACK_DIR/test/usage_collection/plugins" \ --verbose tc_end_block "Build Platform Plugins" diff --git a/.ci/teamcity/setup_env.sh b/.ci/teamcity/setup_env.sh index 09fcd0357fb7..8f607323102b 100755 --- a/.ci/teamcity/setup_env.sh +++ b/.ci/teamcity/setup_env.sh @@ -48,6 +48,7 @@ else fi tc_set_env FLEET_PACKAGE_REGISTRY_PORT 6104 # Any unused port is fine, used by ingest manager tests +tc_set_env TEST_CORS_SERVER_PORT 6105 # Any unused port is fine, used by ingest manager tests if [[ "$(which google-chrome-stable)" || "$(which google-chrome)" ]]; then echo "Chrome detected, setting DETECT_CHROMEDRIVER_VERSION=true" diff --git a/.teamcity/src/Common.kt b/.teamcity/src/Common.kt index 35bc881b8896..2e5357541bfe 100644 --- a/.teamcity/src/Common.kt +++ b/.teamcity/src/Common.kt @@ -4,7 +4,7 @@ import jetbrains.buildServer.configs.kotlin.v2019_2.DslContext const val ENABLE_REPORTING = false // If set to false, jobs with triggers (scheduled, on commit, etc) will be paused -const val ENABLE_TRIGGERS = false +const val ENABLE_TRIGGERS = true fun getProjectBranch(): String { return DslContext.projectName diff --git a/.teamcity/src/builds/PullRequestCi.kt b/.teamcity/src/builds/PullRequestCi.kt index c38591fe850a..997cf1771cc8 100644 --- a/.teamcity/src/builds/PullRequestCi.kt +++ b/.teamcity/src/builds/PullRequestCi.kt @@ -63,13 +63,15 @@ object PullRequestCi : BuildType({ } features { - commitStatusPublisher { - enabled = isReportingEnabled() - vcsRootExtId = "${Kibana.id}" - publisher = github { - githubUrl = "https://api.github.com" - authType = personalToken { - token = "credentialsJSON:07d22002-12de-4627-91c3-672bdb23b55b" + if(isReportingEnabled()) { + commitStatusPublisher { + enabled = true + vcsRootExtId = "${Kibana.id}" + publisher = github { + githubUrl = "https://api.github.com" + authType = personalToken { + token = "credentialsJSON:07d22002-12de-4627-91c3-672bdb23b55b" + } } } } diff --git a/.teamcity/src/builds/default/DefaultCiGroups.kt b/.teamcity/src/builds/default/DefaultCiGroups.kt index 4f39283149e7..948e2ab5782f 100644 --- a/.teamcity/src/builds/default/DefaultCiGroups.kt +++ b/.teamcity/src/builds/default/DefaultCiGroups.kt @@ -3,7 +3,7 @@ package builds.default import dependsOn import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType -const val DEFAULT_CI_GROUP_COUNT = 11 +const val DEFAULT_CI_GROUP_COUNT = 13 val defaultCiGroups = (1..DEFAULT_CI_GROUP_COUNT).map { DefaultCiGroup(it) } object DefaultCiGroups : BuildType({ diff --git a/dev_docs/assets/kibana_platform_plugin_end_user.png b/dev_docs/assets/kibana_platform_plugin_end_user.png new file mode 100644 index 000000000000..a0e32a35ffe6 Binary files /dev/null and b/dev_docs/assets/kibana_platform_plugin_end_user.png differ diff --git a/dev_docs/assets/platform_plugin_cycle.png b/dev_docs/assets/platform_plugin_cycle.png new file mode 100644 index 000000000000..533a38c4ed9c Binary files /dev/null and b/dev_docs/assets/platform_plugin_cycle.png differ diff --git a/dev_docs/kibana_platform_plugin_intro.mdx b/dev_docs/kibana_platform_plugin_intro.mdx new file mode 100644 index 000000000000..412b8eb93b00 --- /dev/null +++ b/dev_docs/kibana_platform_plugin_intro.mdx @@ -0,0 +1,305 @@ +--- +id: kibPlatformIntro +slug: /kibana-dev-docs/platform-intro +title: Plugins and the Kibana platform +summary: An introduction to the Kibana platform and how to use it to build a plugin. +date: 2021-01-06 +tags: ['kibana','onboarding', 'dev', 'architecture'] +--- + +From an end user perspective, Kibana is a tool for interacting with Elasticsearch, providing an easy way +to visualize and analyze data. + +From a developer perspective, Kibana is a platform that provides a set of tools to build not only the UI you see in Kibana today, but +a wide variety of applications that can be used to explore, visualize, and act upon data in Elasticsearch. The platform provides developers the ability to build applications, or inject extra functionality into +already existing applications. Did you know that almost everything you see in the +Kibana UI is built inside a plugin? If you removed all plugins from Kibana, you'd be left with an empty navigation menu, and a set of +developer tools. The Kibana platform is a blank canvas, just waiting for a developer to come along and create something! + +![Kibana personas](assets/kibana_platform_plugin_end_user.png) + +## Plugins vs The Platform + +The core platform provides the most basic and fundamental tools neccessary for building a plugin, like creating saved objects, +routing, application registration, and notifications. The Core platform is not a plugin itself, although +there are some plugins that provide platform functionality. For example, the + provides basic utilities to search, query, and filter data in Elasticsearch. +This code is not part of Core, but is still fundamental for building a plugin, + and we strongly encourage using this service over querying Elasticsearch directly. + + +We currently have three kinds of public services: + + - platform services provided by `core` + - platform services provided by plugins, that can, and should, be used by every plugin (e.g. ) . + - shared services provided by plugins, that are only relevant for only a few, specific plugins (e.g. "presentation utils"). + +Two common questions we encounter are: + +1. Which services are platform services? +2. What is the difference between platform code supplied by core, and platform code supplied by plugins? + +We don't have great answers to those questions today. Currently, the best answers we have are: + +1. Platform plugins are _usually_ plugins that are managed by the Platform Group, but we are starting to see some exceptions. +2. `core` code contains the most fundamental and stable services needed for plugin development. Everything else goes in a plugin. + +We will continue to focus on adding clarity around these types of services and what developers can expect from each. + + + + + +When the Kibana platform and plugin infrastructure was built, we thought of two types of code: core services, and other plugin services. We planned to keep the most stable and fundamental +code needed to build plugins inside core. + +In reality, we ended up with many platform-like services living outside of core, with no (short term) intention of moving them. We highly encourage plugin developers to use +them, so we consider them part of platform services. + +When we built our platform system, we also thought we'd end up with only a handful of large plugins outside core. Users could turn certain plugins off, to minimize the code + footprint and speed up Kibana. + +In reality, our plugin model ended up being used like micro-services. Plugins are the only form of encapsulation we provide developers, and they liked it! However, we ended + up with a ton of small plugins, that developers never intended to be uninstallable, nor tested in this manner. We are considering ways to provide developers the ability to build services + with the encapsulation + they desire, without the need to build a plugin. + +Another side effect of having many small plugins is that common code often ends up extracted into another plugin. Use case specific utilities are exported, + that are not meant to be used in a general manner. This makes our definition of "platform code" a bit trickier to define. We'd like to say "The platform is made up of + every publically exposed service", but in today's world, that wouldn't be a very accurate picture. + +We recognize the need to better clarify the relationship between core functionality, platform-like plugin functionality, and functionality exposed by other plugins. + It's something we will be working on! + + + +The main difference between core functionality and functionality supplied by plugins, is in how it is accessed. Core is +passed to plugins as the first parameter to their `start` and `setup` lifecycle functions, while plugin supplied functionality is passed as the +second parameter. Plugin dependencies must be declared explicitly inside the `kibana.json` file. Core functionality is always provided. Read the +section on [how plugins interact with eachother and core](#how-plugins-interact-with-each-other-and-core) for more information. + +## The anatomy of a plugin + +Plugins are defined as classes and present themselves to Kibana through a simple wrapper function. A plugin can have browser-side code, server-side code, +or both. There is no architectural difference between a plugin in the browser and a plugin on the server. In both places, you describe your plugin similarly, +and you interact with Core and other plugins in the same way. + +The basic file structure of a Kibana plugin named demo that has both client-side and server-side code would be: + +``` +plugins/ + demo + kibana.json [1] + public + index.ts [2] + plugin.ts [3] + server + index.ts [4] + plugin.ts [5] +``` + +### [1] kibana.json + +`kibana.json` is a static manifest file that is used to identify the plugin and to specify if this plugin has server-side code, browser-side code, or both: + +``` +{ + "id": "demo", + "version": "kibana", + "server": true, + "ui": true +} +``` + +### [2] public/index.ts + +`public/index.ts` is the entry point into the client-side code of this plugin. It must export a function named plugin, which will receive a standard set of + core capabilities as an argument. It should return an instance of its plugin class for Kibana to load. + +``` +import type { PluginInitializerContext } from 'kibana/server'; +import { DemoPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new DemoPlugin(initializerContext); +} +``` + +### [3] public/plugin.ts + +`public/plugin.ts` is the client-side plugin definition itself. Technically speaking, it does not need to be a class or even a separate file from the entry + point, but all plugins at Elastic should be consistent in this way. + + + ```ts +import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server'; + +export class DemoPlugin implements Plugin { + constructor(initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup) { + // called when plugin is setting up during Kibana's startup sequence + } + + public start(core: CoreStart) { + // called after all plugins are set up + } + + public stop() { + // called when plugin is torn down during Kibana's shutdown sequence + } +} + ``` + + +### [4] server/index.ts + +`server/index.ts` is the entry-point into the server-side code of this plugin. It is identical in almost every way to the client-side entry-point: + +### [5] server/plugin.ts + +`server/plugin.ts` is the server-side plugin definition. The shape of this plugin is the same as it’s client-side counter-part: + +```ts +import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from 'kibana/server'; + +export class DemoPlugin implements Plugin { + constructor(initializerContext: PluginInitializerContext) {} + + public setup(core: CoreSetup) { + // called when plugin is setting up during Kibana's startup sequence + } + + public start(core: CoreStart) { + // called after all plugins are set up + } + + public stop() { + // called when plugin is torn down during Kibana's shutdown sequence + } +} +``` + +Kibana does not impose any technical restrictions on how the the internals of a plugin are architected, though there are certain +considerations related to how plugins integrate with core APIs and APIs exposed by other plugins that may greatly impact how they are built. + +## Plugin lifecycles & Core services + +The various independent domains that make up core are represented by a series of services. Those services expose public interfaces that are provided to all plugins. +Services expose different features at different parts of their lifecycle. We describe the lifecycle of core services and plugins with specifically-named functions on the service definition. + +Kibana has three lifecycles: setup, start, and stop. Each plugin’s setup function is called sequentially while Kibana is setting up on the server or when it is being loaded in the browser. The start functions are called sequentially after setup has been completed for all plugins. The stop functions are called sequentially while Kibana is gracefully shutting down the server or when the browser tab or window is being closed. + +The table below explains how each lifecycle relates to the state of Kibana. + +| lifecycle | purpose | server | browser | +| ---------- | ------ | ------- | ----- | +| setup | perform "registration" work to setup environment for runtime |configure REST API endpoint, register saved object types, etc. | configure application routes in SPA, register custom UI elements in extension points, etc. | +| start | bootstrap runtime logic | respond to an incoming request, request Elasticsearch server, etc. | start polling Kibana server, update DOM tree in response to user interactions, etc.| +| stop | cleanup runtime | dispose of active handles before the server shutdown. | store session data in the LocalStorage when the user navigates away from Kibana, etc. | + +Different service interfaces can and will be passed to setup, start, and stop because certain functionality makes sense in the context of a running plugin while other types +of functionality may have restrictions or may only make sense in the context of a plugin that is stopping. + +## How plugin's interact with each other, and Core + +The lifecycle-specific contracts exposed by core services are always passed as the first argument to the equivalent lifecycle function in a plugin. +For example, the core http service exposes a function createRouter to all plugin setup functions. To use this function to register an HTTP route handler, +a plugin just accesses it off of the first argument: + +```ts +import type { CoreSetup } from 'kibana/server'; + +export class DemoPlugin { + public setup(core: CoreSetup) { + const router = core.http.createRouter(); + // handler is called when '/path' resource is requested with `GET` method + router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); + } +} +``` + +Unlike core, capabilities exposed by plugins are not automatically injected into all plugins. +Instead, if a plugin wishes to use the public interface provided by another plugin, it must first declare that plugin as a + dependency in it’s kibana.json manifest file. + +** foobar plugin.ts: ** + +``` +import type { Plugin } from 'kibana/server'; +export interface FoobarPluginSetup { [1] + getFoo(): string; +} + +export interface FoobarPluginStart { [1] + getBar(): string; +} + +export class MyPlugin implements Plugin { + public setup(): FoobarPluginSetup { + return { + getFoo() { + return 'foo'; + }, + }; + } + + public start(): FoobarPluginStart { + return { + getBar() { + return 'bar'; + }, + }; + } +} +``` +[1] We highly encourage plugin authors to explicitly declare public interfaces for their plugins. + + +** demo kibana.json** + +``` +{ + "id": "demo", + "requiredPlugins": ["foobar"], + "server": true, + "ui": true +} +``` + +With that specified in the plugin manifest, the appropriate interfaces are then available via the second argument of setup and/or start: + +```ts +import type { CoreSetup, CoreStart } from 'kibana/server'; +import type { FoobarPluginSetup, FoobarPluginStart } from '../../foobar/server'; + +interface DemoSetupPlugins { [1] + foobar: FoobarPluginSetup; +} + +interface DemoStartPlugins { + foobar: FoobarPluginStart; +} + +export class DemoPlugin { + public setup(core: CoreSetup, plugins: DemoSetupPlugins) { [2] + const { foobar } = plugins; + foobar.getFoo(); // 'foo' + foobar.getBar(); // throws because getBar does not exist + } + + public start(core: CoreStart, plugins: DemoStartPlugins) { [3] + const { foobar } = plugins; + foobar.getFoo(); // throws because getFoo does not exist + foobar.getBar(); // 'bar' + } + + public stop() {} +} +``` + +[1] The interface for plugin’s dependencies must be manually composed. You can do this by importing the appropriate type from the plugin and constructing an interface where the property name is the plugin’s ID. + +[2] These manually constructed types should then be used to specify the type of the second argument to the plugin. + +[3] Notice that the type for the setup and start lifecycles are different. Plugin lifecycle functions can only access the APIs that are exposed during that lifecycle. diff --git a/docs/api/saved-objects.asciidoc b/docs/api/saved-objects.asciidoc index 0d8ceefb47e9..ecf975134c64 100644 --- a/docs/api/saved-objects.asciidoc +++ b/docs/api/saved-objects.asciidoc @@ -10,6 +10,8 @@ The following saved objects APIs are available: * <> to retrieve a single {kib} saved object by ID +* <> to retrieve a single {kib} saved object by ID, using any legacy URL alias if it exists + * <> to retrieve multiple {kib} saved objects by ID * <> to retrieve a paginated set of {kib} saved objects by various conditions @@ -40,4 +42,5 @@ include::saved-objects/delete.asciidoc[] include::saved-objects/export.asciidoc[] include::saved-objects/import.asciidoc[] include::saved-objects/resolve_import_errors.asciidoc[] +include::saved-objects/resolve.asciidoc[] include::saved-objects/rotate_encryption_key.asciidoc[] diff --git a/docs/api/saved-objects/get.asciidoc b/docs/api/saved-objects/get.asciidoc index 6aad9759ef5e..4c8cd020e028 100644 --- a/docs/api/saved-objects/get.asciidoc +++ b/docs/api/saved-objects/get.asciidoc @@ -78,7 +78,7 @@ The API returns the following: "title": "[Flights] Global Flight Dashboard", "hits": 0, "description": "Analyze mock flight data for ES-Air, Logstash Airways, Kibana Airlines and JetBeats", - "panelsJSON": "[{\"panelIndex\":\"1\",\"gridData\":{\"x\":0,\"y\":0,\"w\":32,\"h\":7,\"i\":\"1\"},\"embeddableConfig\":{},\"version\":\"6.3.0\",\"panelRefName\":\"panel_0\"},{\"panelIndex\":\"3\",\"gridData\":{\"x\":17,\"y\":7,\"w\":23,\"h\":12,\"i\":\"3\"},\"embeddableConfig\":{\"vis\":{\"colors\":{\"Average Ticket Price\":\"#0A50A1\",\"Flight Count\":\"#82B5D8\"},\"legendOpen\":false}},\"version\":\"6.3.0\",\"panelRefName\":\"panel_1\"},{\"panelIndex\":\"4\",\"gridData\":{\"x\":0,\"y\":85,\"w\":48,\"h\":15,\"i\":\"4\"},\"embeddableConfig\":{},\"version\":\"6.3.0\",\"panelRefName\":\"panel_2\"},{\"panelIndex\":\"5\",\"gridData\":{\"x\":0,\"y\":7,\"w\":17,\"h\":12,\"i\":\"5\"},\"embeddableConfig\":{\"vis\":{\"colors\":{\"ES-Air\":\"#447EBC\",\"JetBeats\":\"#65C5DB\",\"Kibana Airlines\":\"#BA43A9\",\"Logstash Airways\":\"#E5AC0E\"},\"legendOpen\":false}},\"version\":\"6.3.0\",\"panelRefName\":\"panel_3\"},{\"panelIndex\":\"6\",\"gridData\":{\"x\":24,\"y\":33,\"w\":24,\"h\":14,\"i\":\"6\"},\"embeddableConfig\":{\"vis\":{\"colors\":{\"Carrier Delay\":\"#5195CE\",\"Late Aircraft Delay\":\"#1F78C1\",\"NAS Delay\":\"#70DBED\",\"No Delay\":\"#BADFF4\",\"Security Delay\":\"#052B51\",\"Weather Delay\":\"#6ED0E0\"}}},\"version\":\"6.3.0\",\"panelRefName\":\"panel_4\"},{\"panelIndex\":\"7\",\"gridData\":{\"x\":24,\"y\":19,\"w\":24,\"h\":14,\"i\":\"7\"},\"embeddableConfig\":{},\"version\":\"6.3.0\",\"panelRefName\":\"panel_5\"},{\"panelIndex\":\"10\",\"gridData\":{\"x\":0,\"y\":35,\"w\":24,\"h\":12,\"i\":\"10\"},\"embeddableConfig\":{\"vis\":{\"colors\":{\"Count\":\"#1F78C1\"},\"legendOpen\":false}},\"version\":\"6.3.0\",\"panelRefName\":\"panel_6\"},{\"panelIndex\":\"13\",\"gridData\":{\"x\":10,\"y\":19,\"w\":14,\"h\":8,\"i\":\"13\"},\"embeddableConfig\":{\"vis\":{\"colors\":{\"Count\":\"#1F78C1\"},\"legendOpen\":false}},\"version\":\"6.3.0\",\"panelRefName\":\"panel_7\"},{\"panelIndex\":\"14\",\"gridData\":{\"x\":10,\"y\":27,\"w\":14,\"h\":8,\"i\":\"14\"},\"embeddableConfig\":{\"vis\":{\"colors\":{\"Count\":\"#1F78C1\"},\"legendOpen\":false}},\"version\":\"6.3.0\",\"panelRefName\":\"panel_8\"},{\"panelIndex\":\"18\",\"gridData\":{\"x\":24,\"y\":70,\"w\":24,\"h\":15,\"i\":\"18\"},\"embeddableConfig\":{\"mapCenter\":[27.421687059550266,15.371002131141724],\"mapZoom\":1},\"version\":\"6.3.0\",\"panelRefName\":\"panel_9\"},{\"panelIndex\":\"21\",\"gridData\":{\"x\":0,\"y\":62,\"w\":48,\"h\":8,\"i\":\"21\"},\"embeddableConfig\":{},\"version\":\"6.3.0\",\"panelRefName\":\"panel_10\"},{\"panelIndex\":\"22\",\"gridData\":{\"x\":32,\"y\":0,\"w\":16,\"h\":7,\"i\":\"22\"},\"embeddableConfig\":{},\"version\":\"6.3.0\",\"panelRefName\":\"panel_11\"},{\"panelIndex\":\"23\",\"gridData\":{\"x\":0,\"y\":70,\"w\":24,\"h\":15,\"i\":\"23\"},\"embeddableConfig\":{\"mapCenter\":[42.19556096274418,9.536742995308601e-7],\"mapZoom\":1},\"version\":\"6.3.0\",\"panelRefName\":\"panel_12\"},{\"panelIndex\":\"25\",\"gridData\":{\"x\":0,\"y\":19,\"w\":10,\"h\":8,\"i\":\"25\"},\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,251,255)\",\"100 - 150\":\"rgb(107,174,214)\",\"150 - 200\":\"rgb(33,113,181)\",\"200 - 250\":\"rgb(8,48,107)\",\"50 - 100\":\"rgb(198,219,239)\"},\"legendOpen\":false}},\"version\":\"6.3.0\",\"panelRefName\":\"panel_13\"},{\"panelIndex\":\"27\",\"gridData\":{\"x\":0,\"y\":27,\"w\":10,\"h\":8,\"i\":\"27\"},\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 - 50\":\"rgb(247,251,255)\",\"100 - 150\":\"rgb(107,174,214)\",\"150 - 200\":\"rgb(33,113,181)\",\"200 - 250\":\"rgb(8,48,107)\",\"50 - 100\":\"rgb(198,219,239)\"},\"legendOpen\":false}},\"version\":\"6.3.0\",\"panelRefName\":\"panel_14\"},{\"panelIndex\":\"28\",\"gridData\":{\"x\":0,\"y\":47,\"w\":24,\"h\":15,\"i\":\"28\"},\"embeddableConfig\":{\"vis\":{\"defaultColors\":{\"0 -* Connection #0 to host 69c72adb58fa46c69a01afdf4a6cbfd3.us-west1.gcp.cloud.es.io left intact\n 11\":\"rgb(247,251,255)\",\"11 - 22\":\"rgb(208,225,242)\",\"22 - 33\":\"rgb(148,196,223)\",\"33 - 44\":\"rgb(74,152,201)\",\"44 - 55\":\"rgb(23,100,171)\"},\"legendOpen\":false}},\"version\":\"6.3.0\",\"panelRefName\":\"panel_15\"},{\"panelIndex\":\"29\",\"gridData\":{\"x\":40,\"y\":7,\"w\":8,\"h\":6,\"i\":\"29\"},\"embeddableConfig\":{},\"version\":\"6.3.0\",\"panelRefName\":\"panel_16\"},{\"panelIndex\":\"30\",\"gridData\":{\"x\":40,\"y\":13,\"w\":8,\"h\":6,\"i\":\"30\"},\"embeddableConfig\":{},\"version\":\"6.3.0\",\"panelRefName\":\"panel_17\"},{\"panelIndex\":\"31\",\"gridData\":{\"x\":24,\"y\":47,\"w\":24,\"h\":15,\"i\":\"31\"},\"embeddableConfig\":{},\"version\":\"6.3.0\",\"panelRefName\":\"panel_18\"}]", + "panelsJSON": "[ . . . ]", "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":true}", "version": 1, "timeRestore": true, diff --git a/docs/api/saved-objects/resolve.asciidoc b/docs/api/saved-objects/resolve.asciidoc new file mode 100644 index 000000000000..f2bf31bc5d9e --- /dev/null +++ b/docs/api/saved-objects/resolve.asciidoc @@ -0,0 +1,130 @@ +[[saved-objects-api-resolve]] +=== Resolve object API +++++ +Resolve object +++++ + +experimental[] Retrieve a single {kib} saved object by ID, using any legacy URL alias if it exists. + +Under certain circumstances, when Kibana is upgraded, saved object migrations may necessitate regenerating some object IDs to enable new +features. When an object's ID is regenerated, a legacy URL alias is created for that object, preserving its old ID. In such a scenario, that +object can be retrieved via the Resolve API using either its new ID or its old ID. + +[[saved-objects-api-resolve-request]] +==== Request + +`GET :/api/saved_objects/resolve//` + +`GET :/s//api/saved_objects/resolve//` + +[[saved-objects-api-resolve-params]] +==== Path parameters + +`space_id`:: + (Optional, string) An identifier for the space. If `space_id` is not provided in the URL, the default space is used. + + +`type`:: + (Required, string) Valid options include `visualization`, `dashboard`, `search`, `index-pattern`, `config`, and `timelion-sheet`. + +`id`:: + (Required, string) The ID of the object to retrieve. + +[[saved-objects-api-resolve-codes]] +==== Response code + +`200`:: + Indicates a successful call. + +[[saved-objects-api-resolve-example]] +==== Example + +Retrieve the index pattern object with the `my-pattern` ID: + +[source,sh] +-------------------------------------------------- +$ curl -X GET api/saved_objects/resolve/index-pattern/my-pattern +-------------------------------------------------- +// KIBANA + +The API returns the following: + +[source,sh] +-------------------------------------------------- +{ + "saved_object": { + "id": "my-pattern", + "type": "index-pattern", + "version": 1, + "attributes": { + "title": "my-pattern-*" + } + }, + "outcome": "exactMatch" +} +-------------------------------------------------- + +The `outcome` field may be any of the following: + +* `"exactMatch"` -- One document exactly matched the given ID. +* `"aliasMatch"` -- One document with a legacy URL alias matched the given ID; in this case the `saved_object.id` field is different than the given ID. +* `"conflict"` -- Two documents matched the given ID, one was an exact match and another with a legacy URL alias; in this case the `saved_object` object is the exact match, and the `saved_object.id` field is the same as the given ID. + +Retrieve a dashboard object in the `testspace` by ID: + +[source,sh] +-------------------------------------------------- +$ curl -X GET s/testspace/api/saved_objects/resolve/dashboard/7adfa750-4c81-11e8-b3d7-01146121b73d +-------------------------------------------------- +// KIBANA + +The API returns the following: + +[source,sh] +-------------------------------------------------- +{ + "saved_object": { + "id": "7adfa750-4c81-11e8-b3d7-01146121b73d", + "type": "dashboard", + "updated_at": "2019-07-23T00:11:07.059Z", + "version": "WzQ0LDFd", + "attributes": { + "title": "[Flights] Global Flight Dashboard", + "hits": 0, + "description": "Analyze mock flight data for ES-Air, Logstash Airways, Kibana Airlines and JetBeats", + "panelsJSON": "[ . . . ]", + "optionsJSON": "{\"hidePanelTitles\":false,\"useMargins\":true}", + "version": 1, + "timeRestore": true, + "timeTo": "now", + "timeFrom": "now-24h", + "refreshInterval": { + "display": "15 minutes", + "pause": false, + "section": 2, + "value": 900000 + }, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[],\"highlightAll\":true,\"version\":true}" + } + }, + "references": [ + { + "name": "panel_0", + "type": "visualization", + "id": "aeb212e0-4c84-11e8-b3d7-01146121b73d" + }, + . . . + { + "name": "panel_18", + "type": "visualization", + "id": "ed78a660-53a0-11e8-acbd-0be0ad9d822b" + } + ], + "migrationVersion": { + "dashboard": "7.0.0" + } + }, + "outcome": "conflict" +} +-------------------------------------------------- diff --git a/docs/developer/architecture/core/index.asciidoc b/docs/developer/architecture/core/index.asciidoc index 48595690f978..4a86c90cf8c1 100644 --- a/docs/developer/architecture/core/index.asciidoc +++ b/docs/developer/architecture/core/index.asciidoc @@ -421,29 +421,25 @@ the request handler context: [source,typescript] ---- -import type { CoreSetup, IScopedClusterClient } from 'kibana/server'; +import type { CoreSetup, RequestHandlerContext, IScopedClusterClient } from 'kibana/server'; -export interface MyPluginContext { - client: IScopedClusterClient; -} - -// extend RequestHandlerContext when a dependent plugin imports MyPluginContext from the file -declare module 'kibana/server' { - interface RequestHandlerContext { - myPlugin?: MyPluginContext; - } +interface MyRequestHandlerContext extends RequestHandlerContext { + myPlugin: { + client: IScopedClusterClient; + }; } class MyPlugin { setup(core: CoreSetup) { const client = core.elasticsearch.createClient('myClient'); - core.http.registerRouteHandlerContext('myPlugin', (context, req, res) => { + core.http.registerRouteHandlerContext('myPlugin', (context, req, res) => { return { client: client.asScoped(req) }; }); - const router = core.http.createRouter(); + const router = core.http.createRouter(); router.get( { path: '/api/my-plugin/', validate: … }, async (context, req, res) => { + // context type is inferred as MyPluginContext const data = await context.myPlugin.client.asCurrentUser('endpoint'); } ); diff --git a/docs/development/core/public/kibana-plugin-core-public.md b/docs/development/core/public/kibana-plugin-core-public.md index bfe787f3793d..efd499823ffa 100644 --- a/docs/development/core/public/kibana-plugin-core-public.md +++ b/docs/development/core/public/kibana-plugin-core-public.md @@ -109,12 +109,14 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SavedObjectsFindOptions](./kibana-plugin-core-public.savedobjectsfindoptions.md) | | | [SavedObjectsFindOptionsReference](./kibana-plugin-core-public.savedobjectsfindoptionsreference.md) | | | [SavedObjectsFindResponsePublic](./kibana-plugin-core-public.savedobjectsfindresponsepublic.md) | Return type of the Saved Objects find() method.\*Note\*: this type is different between the Public and Server Saved Objects clients. | +| [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.md) | A warning meant to notify that a specific user action is required to finalize the import of some type of object. The actionUrl must be a path relative to the basePath, and not include it. | | [SavedObjectsImportAmbiguousConflictError](./kibana-plugin-core-public.savedobjectsimportambiguousconflicterror.md) | Represents a failure to import due to a conflict, which can be resolved in different ways with an overwrite. | | [SavedObjectsImportConflictError](./kibana-plugin-core-public.savedobjectsimportconflicterror.md) | Represents a failure to import due to a conflict. | | [SavedObjectsImportFailure](./kibana-plugin-core-public.savedobjectsimportfailure.md) | Represents a failure to import. | | [SavedObjectsImportMissingReferencesError](./kibana-plugin-core-public.savedobjectsimportmissingreferenceserror.md) | Represents a failure to import due to missing references. | | [SavedObjectsImportResponse](./kibana-plugin-core-public.savedobjectsimportresponse.md) | The response describing the result of an import. | | [SavedObjectsImportRetry](./kibana-plugin-core-public.savedobjectsimportretry.md) | Describes a retry operation for importing a saved object. | +| [SavedObjectsImportSimpleWarning](./kibana-plugin-core-public.savedobjectsimportsimplewarning.md) | A simple informative warning that will be displayed to the user. | | [SavedObjectsImportSuccess](./kibana-plugin-core-public.savedobjectsimportsuccess.md) | Represents a successful import. | | [SavedObjectsImportUnknownError](./kibana-plugin-core-public.savedobjectsimportunknownerror.md) | Represents a failure to import due to an unknown reason. | | [SavedObjectsImportUnsupportedTypeError](./kibana-plugin-core-public.savedobjectsimportunsupportedtypeerror.md) | Represents a failure to import due to having an unsupported saved object type. | @@ -163,6 +165,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SavedObjectAttribute](./kibana-plugin-core-public.savedobjectattribute.md) | Type definition for a Saved Object attribute value | | [SavedObjectAttributeSingle](./kibana-plugin-core-public.savedobjectattributesingle.md) | Don't use this type, it's simply a helper type for [SavedObjectAttribute](./kibana-plugin-core-public.savedobjectattribute.md) | | [SavedObjectsClientContract](./kibana-plugin-core-public.savedobjectsclientcontract.md) | SavedObjectsClientContract as implemented by the [SavedObjectsClient](./kibana-plugin-core-public.savedobjectsclient.md) | +| [SavedObjectsImportWarning](./kibana-plugin-core-public.savedobjectsimportwarning.md) | Composite type of all the possible types of import warnings.See [SavedObjectsImportSimpleWarning](./kibana-plugin-core-public.savedobjectsimportsimplewarning.md) and [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.md) for more details. | | [SavedObjectsNamespaceType](./kibana-plugin-core-public.savedobjectsnamespacetype.md) | The namespace type dictates how a saved object can be interacted in relation to namespaces. Each type is mutually exclusive: \* single (default): this type of saved object is namespace-isolated, e.g., it exists in only one namespace. \* multiple: this type of saved object is shareable, e.g., it can exist in one or more namespaces. \* agnostic: this type of saved object is global. | | [StartServicesAccessor](./kibana-plugin-core-public.startservicesaccessor.md) | Allows plugins to get access to APIs available in start inside async handlers, such as [App.mount](./kibana-plugin-core-public.app.mount.md). Promise will not resolve until Core and plugin dependencies have completed start. | | [StringValidation](./kibana-plugin-core-public.stringvalidation.md) | Allows regex objects or a regex string | diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobject.coremigrationversion.md b/docs/development/core/public/kibana-plugin-core-public.savedobject.coremigrationversion.md new file mode 100644 index 000000000000..9060a5d6777f --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobject.coremigrationversion.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObject](./kibana-plugin-core-public.savedobject.md) > [coreMigrationVersion](./kibana-plugin-core-public.savedobject.coremigrationversion.md) + +## SavedObject.coreMigrationVersion property + +A semver value that is used when upgrading objects between Kibana versions. + +Signature: + +```typescript +coreMigrationVersion?: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobject.md b/docs/development/core/public/kibana-plugin-core-public.savedobject.md index eb6059747426..9404927f9495 100644 --- a/docs/development/core/public/kibana-plugin-core-public.savedobject.md +++ b/docs/development/core/public/kibana-plugin-core-public.savedobject.md @@ -15,6 +15,7 @@ export interface SavedObject | Property | Type | Description | | --- | --- | --- | | [attributes](./kibana-plugin-core-public.savedobject.attributes.md) | T | The data for a Saved Object is stored as an object in the attributes property. | +| [coreMigrationVersion](./kibana-plugin-core-public.savedobject.coremigrationversion.md) | string | A semver value that is used when upgrading objects between Kibana versions. | | [error](./kibana-plugin-core-public.savedobject.error.md) | SavedObjectError | | | [id](./kibana-plugin-core-public.savedobject.id.md) | string | The ID of this Saved Object, guaranteed to be unique for all objects of the same type | | [migrationVersion](./kibana-plugin-core-public.savedobject.migrationversion.md) | SavedObjectsMigrationVersion | Information about the migrations that have been applied to this SavedObject. When Kibana starts up, KibanaMigrator detects outdated documents and migrates them based on this value. For each migration that has been applied, the plugin's name is used as a key and the latest migration version as the value. | diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectscreateoptions.coremigrationversion.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectscreateoptions.coremigrationversion.md new file mode 100644 index 000000000000..3c1d068f458b --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectscreateoptions.coremigrationversion.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsCreateOptions](./kibana-plugin-core-public.savedobjectscreateoptions.md) > [coreMigrationVersion](./kibana-plugin-core-public.savedobjectscreateoptions.coremigrationversion.md) + +## SavedObjectsCreateOptions.coreMigrationVersion property + +A semver value that is used when upgrading objects between Kibana versions. + +Signature: + +```typescript +coreMigrationVersion?: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectscreateoptions.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectscreateoptions.md index b1b93407d4ff..a039b9f5b4fe 100644 --- a/docs/development/core/public/kibana-plugin-core-public.savedobjectscreateoptions.md +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectscreateoptions.md @@ -15,6 +15,7 @@ export interface SavedObjectsCreateOptions | Property | Type | Description | | --- | --- | --- | +| [coreMigrationVersion](./kibana-plugin-core-public.savedobjectscreateoptions.coremigrationversion.md) | string | A semver value that is used when upgrading objects between Kibana versions. | | [id](./kibana-plugin-core-public.savedobjectscreateoptions.id.md) | string | (Not recommended) Specify an id instead of having the saved objects service generate one for you. | | [migrationVersion](./kibana-plugin-core-public.savedobjectscreateoptions.migrationversion.md) | SavedObjectsMigrationVersion | Information about the migrations that have been applied to this SavedObject. When Kibana starts up, KibanaMigrator detects outdated documents and migrates them based on this value. For each migration that has been applied, the plugin's name is used as a key and the latest migration version as the value. | | [overwrite](./kibana-plugin-core-public.savedobjectscreateoptions.overwrite.md) | boolean | If a document with the given id already exists, overwrite it's contents (default=false). | diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.actionpath.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.actionpath.md new file mode 100644 index 000000000000..120a9d5f3386 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.actionpath.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.md) > [actionPath](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.actionpath.md) + +## SavedObjectsImportActionRequiredWarning.actionPath property + +The path (without the basePath) that the user should be redirect to to address this warning. + +Signature: + +```typescript +actionPath: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.buttonlabel.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.buttonlabel.md new file mode 100644 index 000000000000..ae7daba4860e --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.buttonlabel.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.md) > [buttonLabel](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.buttonlabel.md) + +## SavedObjectsImportActionRequiredWarning.buttonLabel property + +An optional label to use for the link button. If unspecified, a default label will be used. + +Signature: + +```typescript +buttonLabel?: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.md new file mode 100644 index 000000000000..26d734c39c91 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.md) + +## SavedObjectsImportActionRequiredWarning interface + +A warning meant to notify that a specific user action is required to finalize the import of some type of object. + + The `actionUrl` must be a path relative to the basePath, and not include it. + +Signature: + +```typescript +export interface SavedObjectsImportActionRequiredWarning +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [actionPath](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.actionpath.md) | string | The path (without the basePath) that the user should be redirect to to address this warning. | +| [buttonLabel](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.buttonlabel.md) | string | An optional label to use for the link button. If unspecified, a default label will be used. | +| [message](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.message.md) | string | The translated message to display to the user. | +| [type](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.type.md) | 'action_required' | | + diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.message.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.message.md new file mode 100644 index 000000000000..c0f322892577 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.message.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.md) > [message](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.message.md) + +## SavedObjectsImportActionRequiredWarning.message property + +The translated message to display to the user. + +Signature: + +```typescript +message: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.type.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.type.md new file mode 100644 index 000000000000..ee88f6a0d5d8 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.md) > [type](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.type.md) + +## SavedObjectsImportActionRequiredWarning.type property + +Signature: + +```typescript +type: 'action_required'; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportresponse.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportresponse.md index 2c0b691c9d66..3be800498a9b 100644 --- a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportresponse.md +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportresponse.md @@ -20,4 +20,5 @@ export interface SavedObjectsImportResponse | [success](./kibana-plugin-core-public.savedobjectsimportresponse.success.md) | boolean | | | [successCount](./kibana-plugin-core-public.savedobjectsimportresponse.successcount.md) | number | | | [successResults](./kibana-plugin-core-public.savedobjectsimportresponse.successresults.md) | SavedObjectsImportSuccess[] | | +| [warnings](./kibana-plugin-core-public.savedobjectsimportresponse.warnings.md) | SavedObjectsImportWarning[] | | diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportresponse.warnings.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportresponse.warnings.md new file mode 100644 index 000000000000..2e55a2e30f9c --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportresponse.warnings.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsImportResponse](./kibana-plugin-core-public.savedobjectsimportresponse.md) > [warnings](./kibana-plugin-core-public.savedobjectsimportresponse.warnings.md) + +## SavedObjectsImportResponse.warnings property + +Signature: + +```typescript +warnings: SavedObjectsImportWarning[]; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportsimplewarning.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportsimplewarning.md new file mode 100644 index 000000000000..4d6d984777c8 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportsimplewarning.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsImportSimpleWarning](./kibana-plugin-core-public.savedobjectsimportsimplewarning.md) + +## SavedObjectsImportSimpleWarning interface + +A simple informative warning that will be displayed to the user. + +Signature: + +```typescript +export interface SavedObjectsImportSimpleWarning +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [message](./kibana-plugin-core-public.savedobjectsimportsimplewarning.message.md) | string | The translated message to display to the user | +| [type](./kibana-plugin-core-public.savedobjectsimportsimplewarning.type.md) | 'simple' | | + diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportsimplewarning.message.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportsimplewarning.message.md new file mode 100644 index 000000000000..42c94e14e3d2 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportsimplewarning.message.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsImportSimpleWarning](./kibana-plugin-core-public.savedobjectsimportsimplewarning.md) > [message](./kibana-plugin-core-public.savedobjectsimportsimplewarning.message.md) + +## SavedObjectsImportSimpleWarning.message property + +The translated message to display to the user + +Signature: + +```typescript +message: string; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportsimplewarning.type.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportsimplewarning.type.md new file mode 100644 index 000000000000..86a4cbfa434e --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportsimplewarning.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsImportSimpleWarning](./kibana-plugin-core-public.savedobjectsimportsimplewarning.md) > [type](./kibana-plugin-core-public.savedobjectsimportsimplewarning.type.md) + +## SavedObjectsImportSimpleWarning.type property + +Signature: + +```typescript +type: 'simple'; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportwarning.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportwarning.md new file mode 100644 index 000000000000..a9a9a7077497 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsimportwarning.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SavedObjectsImportWarning](./kibana-plugin-core-public.savedobjectsimportwarning.md) + +## SavedObjectsImportWarning type + +Composite type of all the possible types of import warnings. + +See [SavedObjectsImportSimpleWarning](./kibana-plugin-core-public.savedobjectsimportsimplewarning.md) and [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-public.savedobjectsimportactionrequiredwarning.md) for more details. + +Signature: + +```typescript +export declare type SavedObjectsImportWarning = SavedObjectsImportSimpleWarning | SavedObjectsImportActionRequiredWarning; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.simplesavedobject._constructor_.md b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject._constructor_.md index b1a4357cca7a..8fb005421e87 100644 --- a/docs/development/core/public/kibana-plugin-core-public.simplesavedobject._constructor_.md +++ b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject._constructor_.md @@ -9,7 +9,7 @@ Constructs a new instance of the `SimpleSavedObject` class Signature: ```typescript -constructor(client: SavedObjectsClientContract, { id, type, version, attributes, error, references, migrationVersion }: SavedObjectType); +constructor(client: SavedObjectsClientContract, { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, }: SavedObjectType); ``` ## Parameters @@ -17,5 +17,5 @@ constructor(client: SavedObjectsClientContract, { id, type, version, attributes, | Parameter | Type | Description | | --- | --- | --- | | client | SavedObjectsClientContract | | -| { id, type, version, attributes, error, references, migrationVersion } | SavedObjectType<T> | | +| { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, } | SavedObjectType<T> | | diff --git a/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.coremigrationversion.md b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.coremigrationversion.md new file mode 100644 index 000000000000..8e2217fab6ee --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.coremigrationversion.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [SimpleSavedObject](./kibana-plugin-core-public.simplesavedobject.md) > [coreMigrationVersion](./kibana-plugin-core-public.simplesavedobject.coremigrationversion.md) + +## SimpleSavedObject.coreMigrationVersion property + +Signature: + +```typescript +coreMigrationVersion: SavedObjectType['coreMigrationVersion']; +``` diff --git a/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.md b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.md index e9987f6d5beb..35264a3a4cf0 100644 --- a/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.md +++ b/docs/development/core/public/kibana-plugin-core-public.simplesavedobject.md @@ -18,7 +18,7 @@ export declare class SimpleSavedObject | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)(client, { id, type, version, attributes, error, references, migrationVersion })](./kibana-plugin-core-public.simplesavedobject._constructor_.md) | | Constructs a new instance of the SimpleSavedObject class | +| [(constructor)(client, { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, })](./kibana-plugin-core-public.simplesavedobject._constructor_.md) | | Constructs a new instance of the SimpleSavedObject class | ## Properties @@ -26,6 +26,7 @@ export declare class SimpleSavedObject | --- | --- | --- | --- | | [\_version](./kibana-plugin-core-public.simplesavedobject._version.md) | | SavedObjectType<T>['version'] | | | [attributes](./kibana-plugin-core-public.simplesavedobject.attributes.md) | | T | | +| [coreMigrationVersion](./kibana-plugin-core-public.simplesavedobject.coremigrationversion.md) | | SavedObjectType<T>['coreMigrationVersion'] | | | [error](./kibana-plugin-core-public.simplesavedobject.error.md) | | SavedObjectType<T>['error'] | | | [id](./kibana-plugin-core-public.simplesavedobject.id.md) | | SavedObjectType<T>['id'] | | | [migrationVersion](./kibana-plugin-core-public.simplesavedobject.migrationversion.md) | | SavedObjectType<T>['migrationVersion'] | | diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresources.md b/docs/development/core/server/kibana-plugin-core-server.httpresources.md index cb3170e989e1..25acffc1a040 100644 --- a/docs/development/core/server/kibana-plugin-core-server.httpresources.md +++ b/docs/development/core/server/kibana-plugin-core-server.httpresources.md @@ -16,5 +16,5 @@ export interface HttpResources | Property | Type | Description | | --- | --- | --- | -| [register](./kibana-plugin-core-server.httpresources.register.md) | <P, Q, B>(route: RouteConfig<P, Q, B, 'get'>, handler: HttpResourcesRequestHandler<P, Q, B>) => void | To register a route handler executing passed function to form response. | +| [register](./kibana-plugin-core-server.httpresources.register.md) | <P, Q, B, Context extends RequestHandlerContext = RequestHandlerContext>(route: RouteConfig<P, Q, B, 'get'>, handler: HttpResourcesRequestHandler<P, Q, B, Context>) => void | To register a route handler executing passed function to form response. | diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresources.register.md b/docs/development/core/server/kibana-plugin-core-server.httpresources.register.md index fe3803a6ffe5..ee9569aeb37b 100644 --- a/docs/development/core/server/kibana-plugin-core-server.httpresources.register.md +++ b/docs/development/core/server/kibana-plugin-core-server.httpresources.register.md @@ -9,5 +9,5 @@ To register a route handler executing passed function to form response. Signature: ```typescript -register: (route: RouteConfig, handler: HttpResourcesRequestHandler) => void; +register: (route: RouteConfig, handler: HttpResourcesRequestHandler) => void; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.httpresourcesrequesthandler.md b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrequesthandler.md index 20f930382955..49854ac00386 100644 --- a/docs/development/core/server/kibana-plugin-core-server.httpresourcesrequesthandler.md +++ b/docs/development/core/server/kibana-plugin-core-server.httpresourcesrequesthandler.md @@ -9,7 +9,7 @@ Extended version of [RequestHandler](./kibana-plugin-core-server.requesthandler. Signature: ```typescript -export declare type HttpResourcesRequestHandler

= RequestHandler; +export declare type HttpResourcesRequestHandler

= RequestHandler; ``` ## Example diff --git a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.createrouter.md b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.createrouter.md index 89b932514565..f009dd3fc2b1 100644 --- a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.createrouter.md +++ b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.createrouter.md @@ -9,7 +9,7 @@ Provides ability to declare a handler function for a particular path and HTTP re Signature: ```typescript -createRouter: () => IRouter; +createRouter: () => IRouter; ``` ## Remarks diff --git a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md index 474dc6b7d6f2..dbc2a516fa17 100644 --- a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.md @@ -84,7 +84,7 @@ async (context, request, response) => { | [auth](./kibana-plugin-core-server.httpservicesetup.auth.md) | HttpAuth | Auth status. See [HttpAuth](./kibana-plugin-core-server.httpauth.md) | | [basePath](./kibana-plugin-core-server.httpservicesetup.basepath.md) | IBasePath | Access or manipulate the Kibana base path See [IBasePath](./kibana-plugin-core-server.ibasepath.md). | | [createCookieSessionStorageFactory](./kibana-plugin-core-server.httpservicesetup.createcookiesessionstoragefactory.md) | <T>(cookieOptions: SessionStorageCookieOptions<T>) => Promise<SessionStorageFactory<T>> | Creates cookie based session storage factory [SessionStorageFactory](./kibana-plugin-core-server.sessionstoragefactory.md) | -| [createRouter](./kibana-plugin-core-server.httpservicesetup.createrouter.md) | () => IRouter | Provides ability to declare a handler function for a particular path and HTTP request method. | +| [createRouter](./kibana-plugin-core-server.httpservicesetup.createrouter.md) | <Context extends RequestHandlerContext = RequestHandlerContext>() => IRouter<Context> | Provides ability to declare a handler function for a particular path and HTTP request method. | | [csp](./kibana-plugin-core-server.httpservicesetup.csp.md) | ICspConfig | The CSP config used for Kibana. | | [getServerInfo](./kibana-plugin-core-server.httpservicesetup.getserverinfo.md) | () => HttpServerInfo | Provides common [information](./kibana-plugin-core-server.httpserverinfo.md) about the running http server. | | [registerAuth](./kibana-plugin-core-server.httpservicesetup.registerauth.md) | (handler: AuthenticationHandler) => void | To define custom authentication and/or authorization mechanism for incoming requests. | @@ -92,5 +92,5 @@ async (context, request, response) => { | [registerOnPreAuth](./kibana-plugin-core-server.httpservicesetup.registeronpreauth.md) | (handler: OnPreAuthHandler) => void | To define custom logic to perform for incoming requests before the Auth interceptor performs a check that user has access to requested resources. | | [registerOnPreResponse](./kibana-plugin-core-server.httpservicesetup.registeronpreresponse.md) | (handler: OnPreResponseHandler) => void | To define custom logic to perform for the server response. | | [registerOnPreRouting](./kibana-plugin-core-server.httpservicesetup.registeronprerouting.md) | (handler: OnPreRoutingHandler) => void | To define custom logic to perform for incoming requests before server performs a route lookup. | -| [registerRouteHandlerContext](./kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md) | <T extends keyof RequestHandlerContext>(contextName: T, provider: RequestHandlerContextProvider<T>) => RequestHandlerContextContainer | Register a context provider for a route handler. | +| [registerRouteHandlerContext](./kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md) | <Context extends RequestHandlerContext, ContextName extends keyof Context>(contextName: ContextName, provider: RequestHandlerContextProvider<Context, ContextName>) => RequestHandlerContextContainer | Register a context provider for a route handler. | diff --git a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md index b0dc4d44f755..df3f80580f6d 100644 --- a/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md +++ b/docs/development/core/server/kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md @@ -9,7 +9,7 @@ Register a context provider for a route handler. Signature: ```typescript -registerRouteHandlerContext: (contextName: T, provider: RequestHandlerContextProvider) => RequestHandlerContextContainer; +registerRouteHandlerContext: (contextName: ContextName, provider: RequestHandlerContextProvider) => RequestHandlerContextContainer; ``` ## Example @@ -17,7 +17,10 @@ registerRouteHandlerContext: (contextName ```ts // my-plugin.ts - deps.http.registerRouteHandlerContext( + interface MyRequestHandlerContext extends RequestHandlerContext { + myApp: { search(id: string): Promise }; + } + deps.http.registerRouteHandlerContext( 'myApp', (context, req) => { async function search (id: string) { @@ -28,6 +31,8 @@ registerRouteHandlerContext: (contextName ); // my-route-handler.ts + import type { MyRequestHandlerContext } from './my-plugin.ts'; + const router = createRouter(); router.get({ path: '/', validate: false }, async (context, req, res) => { const response = await context.myApp.search(...); return res.ok(response); diff --git a/docs/development/core/server/kibana-plugin-core-server.icontextcontainer.md b/docs/development/core/server/kibana-plugin-core-server.icontextcontainer.md index 0c2cd7a69901..3b390e3aaa11 100644 --- a/docs/development/core/server/kibana-plugin-core-server.icontextcontainer.md +++ b/docs/development/core/server/kibana-plugin-core-server.icontextcontainer.md @@ -9,7 +9,7 @@ An object that handles registration of context providers and configuring handler Signature: ```typescript -export interface IContextContainer> +export interface IContextContainer ``` ## Remarks diff --git a/docs/development/core/server/kibana-plugin-core-server.icontextcontainer.registercontext.md b/docs/development/core/server/kibana-plugin-core-server.icontextcontainer.registercontext.md index 0813d81e5a72..7f531fa8ba0d 100644 --- a/docs/development/core/server/kibana-plugin-core-server.icontextcontainer.registercontext.md +++ b/docs/development/core/server/kibana-plugin-core-server.icontextcontainer.registercontext.md @@ -9,7 +9,7 @@ Register a new context provider. Signature: ```typescript -registerContext>(pluginOpaqueId: PluginOpaqueId, contextName: TContextName, provider: IContextProvider): this; +registerContext(pluginOpaqueId: PluginOpaqueId, contextName: ContextName, provider: IContextProvider): this; ``` ## Parameters @@ -17,8 +17,8 @@ registerContext>(pluginO | Parameter | Type | Description | | --- | --- | --- | | pluginOpaqueId | PluginOpaqueId | The plugin opaque ID for the plugin that registers this context. | -| contextName | TContextName | The key of the TContext object this provider supplies the value for. | -| provider | IContextProvider<THandler, TContextName> | A [IContextProvider](./kibana-plugin-core-server.icontextprovider.md) to be called each time a new context is created. | +| contextName | ContextName | The key of the TContext object this provider supplies the value for. | +| provider | IContextProvider<Context, ContextName> | A [IContextProvider](./kibana-plugin-core-server.icontextprovider.md) to be called each time a new context is created. | Returns: diff --git a/docs/development/core/server/kibana-plugin-core-server.icontextprovider.md b/docs/development/core/server/kibana-plugin-core-server.icontextprovider.md index 7d124b266bcc..ddd8a0e92f46 100644 --- a/docs/development/core/server/kibana-plugin-core-server.icontextprovider.md +++ b/docs/development/core/server/kibana-plugin-core-server.icontextprovider.md @@ -9,7 +9,7 @@ A function that returns a context value for a specific key of given context type Signature: ```typescript -export declare type IContextProvider, TContextName extends keyof HandlerContextType> = (context: PartialExceptFor, 'core'>, ...rest: HandlerParameters) => Promise[TContextName]> | HandlerContextType[TContextName]; +export declare type IContextProvider = (context: Omit, ...rest: HandlerParameters) => Promise | Context[ContextName]; ``` ## Remarks diff --git a/docs/development/core/server/kibana-plugin-core-server.irouter.delete.md b/docs/development/core/server/kibana-plugin-core-server.irouter.delete.md index d4c4692239d7..a7b6dd5bc294 100644 --- a/docs/development/core/server/kibana-plugin-core-server.irouter.delete.md +++ b/docs/development/core/server/kibana-plugin-core-server.irouter.delete.md @@ -9,5 +9,5 @@ Register a route handler for `DELETE` request. Signature: ```typescript -delete: RouteRegistrar<'delete'>; +delete: RouteRegistrar<'delete', Context>; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.irouter.get.md b/docs/development/core/server/kibana-plugin-core-server.irouter.get.md index 38ed9ea96455..7db694b38da4 100644 --- a/docs/development/core/server/kibana-plugin-core-server.irouter.get.md +++ b/docs/development/core/server/kibana-plugin-core-server.irouter.get.md @@ -9,5 +9,5 @@ Register a route handler for `GET` request. Signature: ```typescript -get: RouteRegistrar<'get'>; +get: RouteRegistrar<'get', Context>; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.irouter.md b/docs/development/core/server/kibana-plugin-core-server.irouter.md index 4bade638a65a..a0a27e828f86 100644 --- a/docs/development/core/server/kibana-plugin-core-server.irouter.md +++ b/docs/development/core/server/kibana-plugin-core-server.irouter.md @@ -9,18 +9,18 @@ Registers route handlers for specified resource path and method. See [RouteConfi Signature: ```typescript -export interface IRouter +export interface IRouter ``` ## Properties | Property | Type | Description | | --- | --- | --- | -| [delete](./kibana-plugin-core-server.irouter.delete.md) | RouteRegistrar<'delete'> | Register a route handler for DELETE request. | -| [get](./kibana-plugin-core-server.irouter.get.md) | RouteRegistrar<'get'> | Register a route handler for GET request. | +| [delete](./kibana-plugin-core-server.irouter.delete.md) | RouteRegistrar<'delete', Context> | Register a route handler for DELETE request. | +| [get](./kibana-plugin-core-server.irouter.get.md) | RouteRegistrar<'get', Context> | Register a route handler for GET request. | | [handleLegacyErrors](./kibana-plugin-core-server.irouter.handlelegacyerrors.md) | RequestHandlerWrapper | Wrap a router handler to catch and converts legacy boom errors to proper custom errors. | -| [patch](./kibana-plugin-core-server.irouter.patch.md) | RouteRegistrar<'patch'> | Register a route handler for PATCH request. | -| [post](./kibana-plugin-core-server.irouter.post.md) | RouteRegistrar<'post'> | Register a route handler for POST request. | -| [put](./kibana-plugin-core-server.irouter.put.md) | RouteRegistrar<'put'> | Register a route handler for PUT request. | +| [patch](./kibana-plugin-core-server.irouter.patch.md) | RouteRegistrar<'patch', Context> | Register a route handler for PATCH request. | +| [post](./kibana-plugin-core-server.irouter.post.md) | RouteRegistrar<'post', Context> | Register a route handler for POST request. | +| [put](./kibana-plugin-core-server.irouter.put.md) | RouteRegistrar<'put', Context> | Register a route handler for PUT request. | | [routerPath](./kibana-plugin-core-server.irouter.routerpath.md) | string | Resulted path | diff --git a/docs/development/core/server/kibana-plugin-core-server.irouter.patch.md b/docs/development/core/server/kibana-plugin-core-server.irouter.patch.md index f835eb980073..b353079630ec 100644 --- a/docs/development/core/server/kibana-plugin-core-server.irouter.patch.md +++ b/docs/development/core/server/kibana-plugin-core-server.irouter.patch.md @@ -9,5 +9,5 @@ Register a route handler for `PATCH` request. Signature: ```typescript -patch: RouteRegistrar<'patch'>; +patch: RouteRegistrar<'patch', Context>; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.irouter.post.md b/docs/development/core/server/kibana-plugin-core-server.irouter.post.md index 312b83d570a4..94c703ad6f33 100644 --- a/docs/development/core/server/kibana-plugin-core-server.irouter.post.md +++ b/docs/development/core/server/kibana-plugin-core-server.irouter.post.md @@ -9,5 +9,5 @@ Register a route handler for `POST` request. Signature: ```typescript -post: RouteRegistrar<'post'>; +post: RouteRegistrar<'post', Context>; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.irouter.put.md b/docs/development/core/server/kibana-plugin-core-server.irouter.put.md index d8a8271b6fc9..702ff3ff61bb 100644 --- a/docs/development/core/server/kibana-plugin-core-server.irouter.put.md +++ b/docs/development/core/server/kibana-plugin-core-server.irouter.put.md @@ -9,5 +9,5 @@ Register a route handler for `PUT` request. Signature: ```typescript -put: RouteRegistrar<'put'>; +put: RouteRegistrar<'put', Context>; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index 06c7983f89a7..82f4a285409c 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -139,7 +139,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SavedObjectAttributes](./kibana-plugin-core-server.savedobjectattributes.md) | The data for a Saved Object is stored as an object in the attributes property. | | [SavedObjectExportBaseOptions](./kibana-plugin-core-server.savedobjectexportbaseoptions.md) | | | [SavedObjectMigrationContext](./kibana-plugin-core-server.savedobjectmigrationcontext.md) | Migration context provided when invoking a [migration handler](./kibana-plugin-core-server.savedobjectmigrationfn.md) | -| [SavedObjectMigrationMap](./kibana-plugin-core-server.savedobjectmigrationmap.md) | A map of [migration functions](./kibana-plugin-core-server.savedobjectmigrationfn.md) to be used for a given type. The map's keys must be valid semver versions.For a given document, only migrations with a higher version number than that of the document will be applied. Migrations are executed in order, starting from the lowest version and ending with the highest one. | +| [SavedObjectMigrationMap](./kibana-plugin-core-server.savedobjectmigrationmap.md) | A map of [migration functions](./kibana-plugin-core-server.savedobjectmigrationfn.md) to be used for a given type. The map's keys must be valid semver versions, and they cannot exceed the current Kibana version.For a given document, only migrations with a higher version number than that of the document will be applied. Migrations are executed in order, starting from the lowest version and ending with the highest one. | | [SavedObjectReference](./kibana-plugin-core-server.savedobjectreference.md) | A reference to another saved object. | | [SavedObjectsAddToNamespacesOptions](./kibana-plugin-core-server.savedobjectsaddtonamespacesoptions.md) | | | [SavedObjectsAddToNamespacesResponse](./kibana-plugin-core-server.savedobjectsaddtonamespacesresponse.md) | | @@ -164,17 +164,21 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SavedObjectsExportByObjectOptions](./kibana-plugin-core-server.savedobjectsexportbyobjectoptions.md) | Options for the [export by objects API](./kibana-plugin-core-server.savedobjectsexporter.exportbyobjects.md) | | [SavedObjectsExportByTypeOptions](./kibana-plugin-core-server.savedobjectsexportbytypeoptions.md) | Options for the [export by type API](./kibana-plugin-core-server.savedobjectsexporter.exportbytypes.md) | | [SavedObjectsExportResultDetails](./kibana-plugin-core-server.savedobjectsexportresultdetails.md) | Structure of the export result details entry | +| [SavedObjectsExportTransformContext](./kibana-plugin-core-server.savedobjectsexporttransformcontext.md) | Context passed down to a [export transform function](./kibana-plugin-core-server.savedobjectsexporttransform.md) | | [SavedObjectsFindOptions](./kibana-plugin-core-server.savedobjectsfindoptions.md) | | | [SavedObjectsFindOptionsReference](./kibana-plugin-core-server.savedobjectsfindoptionsreference.md) | | | [SavedObjectsFindResponse](./kibana-plugin-core-server.savedobjectsfindresponse.md) | Return type of the Saved Objects find() method.\*Note\*: this type is different between the Public and Server Saved Objects clients. | | [SavedObjectsFindResult](./kibana-plugin-core-server.savedobjectsfindresult.md) | | +| [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.md) | A warning meant to notify that a specific user action is required to finalize the import of some type of object. The actionUrl must be a path relative to the basePath, and not include it. | | [SavedObjectsImportAmbiguousConflictError](./kibana-plugin-core-server.savedobjectsimportambiguousconflicterror.md) | Represents a failure to import due to a conflict, which can be resolved in different ways with an overwrite. | | [SavedObjectsImportConflictError](./kibana-plugin-core-server.savedobjectsimportconflicterror.md) | Represents a failure to import due to a conflict. | | [SavedObjectsImportFailure](./kibana-plugin-core-server.savedobjectsimportfailure.md) | Represents a failure to import. | +| [SavedObjectsImportHookResult](./kibana-plugin-core-server.savedobjectsimporthookresult.md) | Result from a [import hook](./kibana-plugin-core-server.savedobjectsimporthook.md) | | [SavedObjectsImportMissingReferencesError](./kibana-plugin-core-server.savedobjectsimportmissingreferenceserror.md) | Represents a failure to import due to missing references. | | [SavedObjectsImportOptions](./kibana-plugin-core-server.savedobjectsimportoptions.md) | Options to control the import operation. | | [SavedObjectsImportResponse](./kibana-plugin-core-server.savedobjectsimportresponse.md) | The response describing the result of an import. | | [SavedObjectsImportRetry](./kibana-plugin-core-server.savedobjectsimportretry.md) | Describes a retry operation for importing a saved object. | +| [SavedObjectsImportSimpleWarning](./kibana-plugin-core-server.savedobjectsimportsimplewarning.md) | A simple informative warning that will be displayed to the user. | | [SavedObjectsImportSuccess](./kibana-plugin-core-server.savedobjectsimportsuccess.md) | Represents a successful import. | | [SavedObjectsImportUnknownError](./kibana-plugin-core-server.savedobjectsimportunknownerror.md) | Represents a failure to import due to an unknown reason. | | [SavedObjectsImportUnsupportedTypeError](./kibana-plugin-core-server.savedobjectsimportunsupportedtypeerror.md) | Represents a failure to import due to having an unsupported saved object type. | @@ -184,10 +188,12 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SavedObjectsMigrationLogger](./kibana-plugin-core-server.savedobjectsmigrationlogger.md) | | | [SavedObjectsMigrationVersion](./kibana-plugin-core-server.savedobjectsmigrationversion.md) | Information about the migrations that have been applied to this SavedObject. When Kibana starts up, KibanaMigrator detects outdated documents and migrates them based on this value. For each migration that has been applied, the plugin's name is used as a key and the latest migration version as the value. | | [SavedObjectsRawDoc](./kibana-plugin-core-server.savedobjectsrawdoc.md) | A raw document as represented directly in the saved object index. | +| [SavedObjectsRawDocParseOptions](./kibana-plugin-core-server.savedobjectsrawdocparseoptions.md) | Options that can be specified when using the saved objects serializer to parse a raw document. | | [SavedObjectsRemoveReferencesToOptions](./kibana-plugin-core-server.savedobjectsremovereferencestooptions.md) | | | [SavedObjectsRemoveReferencesToResponse](./kibana-plugin-core-server.savedobjectsremovereferencestoresponse.md) | | | [SavedObjectsRepositoryFactory](./kibana-plugin-core-server.savedobjectsrepositoryfactory.md) | Factory provided when invoking a [client factory provider](./kibana-plugin-core-server.savedobjectsclientfactoryprovider.md) See [SavedObjectsServiceSetup.setClientFactoryProvider](./kibana-plugin-core-server.savedobjectsservicesetup.setclientfactoryprovider.md) | | [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-core-server.savedobjectsresolveimporterrorsoptions.md) | Options to control the "resolve import" operation. | +| [SavedObjectsResolveResponse](./kibana-plugin-core-server.savedobjectsresolveresponse.md) | | | [SavedObjectsServiceSetup](./kibana-plugin-core-server.savedobjectsservicesetup.md) | Saved Objects is Kibana's data persistence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceSetup API exposes methods for registering Saved Object types, creating and registering Saved Object client wrappers and factories. | | [SavedObjectsServiceStart](./kibana-plugin-core-server.savedobjectsservicestart.md) | Saved Objects is Kibana's data persisentence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceStart API provides a scoped Saved Objects client for interacting with Saved Objects. | | [SavedObjectStatusMeta](./kibana-plugin-core-server.savedobjectstatusmeta.md) | Meta information about the SavedObjectService's status. Available to plugins via [CoreSetup.status](./kibana-plugin-core-server.coresetup.status.md). | @@ -294,7 +300,10 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SavedObjectsClientFactory](./kibana-plugin-core-server.savedobjectsclientfactory.md) | Describes the factory used to create instances of the Saved Objects Client. | | [SavedObjectsClientFactoryProvider](./kibana-plugin-core-server.savedobjectsclientfactoryprovider.md) | Provider to invoke to retrieve a [SavedObjectsClientFactory](./kibana-plugin-core-server.savedobjectsclientfactory.md). | | [SavedObjectsClientWrapperFactory](./kibana-plugin-core-server.savedobjectsclientwrapperfactory.md) | Describes the factory used to create instances of Saved Objects Client Wrappers. | +| [SavedObjectsExportTransform](./kibana-plugin-core-server.savedobjectsexporttransform.md) | Transformation function used to mutate the exported objects of the associated type.A type's export transform function will be executed once per user-initiated export, for all objects of that type. | | [SavedObjectsFieldMapping](./kibana-plugin-core-server.savedobjectsfieldmapping.md) | Describe a [saved object type mapping](./kibana-plugin-core-server.savedobjectstypemappingdefinition.md) field.Please refer to [elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html) For the mapping documentation | +| [SavedObjectsImportHook](./kibana-plugin-core-server.savedobjectsimporthook.md) | A hook associated with a specific saved object type, that will be invoked during the import process. The hook will have access to the objects of the registered type.Currently, the only supported feature for import hooks is to return warnings to be displayed in the UI when the import succeeds. The only interactions the hook can have with the import process is via the hook's response. Mutating the objects inside the hook's code will have no effect. | +| [SavedObjectsImportWarning](./kibana-plugin-core-server.savedobjectsimportwarning.md) | Composite type of all the possible types of import warnings.See [SavedObjectsImportSimpleWarning](./kibana-plugin-core-server.savedobjectsimportsimplewarning.md) and [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.md) for more details. | | [SavedObjectsNamespaceType](./kibana-plugin-core-server.savedobjectsnamespacetype.md) | The namespace type dictates how a saved object can be interacted in relation to namespaces. Each type is mutually exclusive: \* single (default): this type of saved object is namespace-isolated, e.g., it exists in only one namespace. \* multiple: this type of saved object is shareable, e.g., it can exist in one or more namespaces. \* agnostic: this type of saved object is global. | | [SavedObjectUnsanitizedDoc](./kibana-plugin-core-server.savedobjectunsanitizeddoc.md) | Describes Saved Object documents from Kibana < 7.0.0 which don't have a references root property defined. This type should only be used in migrations. | | [ScopeableRequest](./kibana-plugin-core-server.scopeablerequest.md) | A user credentials container. It accommodates the necessary auth credentials to impersonate the current user.See [KibanaRequest](./kibana-plugin-core-server.kibanarequest.md). | diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandler.md b/docs/development/core/server/kibana-plugin-core-server.requesthandler.md index cecef7c92356..0032e52a0e90 100644 --- a/docs/development/core/server/kibana-plugin-core-server.requesthandler.md +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandler.md @@ -9,7 +9,7 @@ A function executed when route path matched requested resource path. Request han Signature: ```typescript -export declare type RequestHandler

= (context: RequestHandlerContext, request: KibanaRequest, response: ResponseFactory) => IKibanaResponse | Promise>; +export declare type RequestHandler

= (context: Context, request: KibanaRequest, response: ResponseFactory) => IKibanaResponse | Promise>; ``` ## Example diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextcontainer.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextcontainer.md index c95a16670b19..6966deb9d7cc 100644 --- a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextcontainer.md +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextcontainer.md @@ -9,5 +9,5 @@ An object that handles registration of http request context providers. Signature: ```typescript -export declare type RequestHandlerContextContainer = IContextContainer>; +export declare type RequestHandlerContextContainer = IContextContainer; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextprovider.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextprovider.md index cd30b3c1f43e..d94facd849ef 100644 --- a/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextprovider.md +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlercontextprovider.md @@ -9,5 +9,5 @@ Context provider for request handler. Extends request context object with provid Signature: ```typescript -export declare type RequestHandlerContextProvider = IContextProvider, TContextName>; +export declare type RequestHandlerContextProvider = IContextProvider; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.requesthandlerwrapper.md b/docs/development/core/server/kibana-plugin-core-server.requesthandlerwrapper.md index a9fe188ee2bf..76c7ee4f2290 100644 --- a/docs/development/core/server/kibana-plugin-core-server.requesthandlerwrapper.md +++ b/docs/development/core/server/kibana-plugin-core-server.requesthandlerwrapper.md @@ -9,7 +9,7 @@ Type-safe wrapper for [RequestHandler](./kibana-plugin-core-server.requesthandle Signature: ```typescript -export declare type RequestHandlerWrapper = (handler: RequestHandler) => RequestHandler; +export declare type RequestHandlerWrapper = (handler: RequestHandler) => RequestHandler; ``` ## Example diff --git a/docs/development/core/server/kibana-plugin-core-server.routeregistrar.md b/docs/development/core/server/kibana-plugin-core-server.routeregistrar.md index 121a8eee1bfc..3ddb350a38b6 100644 --- a/docs/development/core/server/kibana-plugin-core-server.routeregistrar.md +++ b/docs/development/core/server/kibana-plugin-core-server.routeregistrar.md @@ -9,5 +9,5 @@ Route handler common definition Signature: ```typescript -export declare type RouteRegistrar = (route: RouteConfig, handler: RequestHandler) => void; +export declare type RouteRegistrar = (route: RouteConfig, handler: RequestHandler) => void; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobject.coremigrationversion.md b/docs/development/core/server/kibana-plugin-core-server.savedobject.coremigrationversion.md new file mode 100644 index 000000000000..b4d1f3c76945 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobject.coremigrationversion.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObject](./kibana-plugin-core-server.savedobject.md) > [coreMigrationVersion](./kibana-plugin-core-server.savedobject.coremigrationversion.md) + +## SavedObject.coreMigrationVersion property + +A semver value that is used when upgrading objects between Kibana versions. + +Signature: + +```typescript +coreMigrationVersion?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobject.md index 5aefc55736cd..07172487e6fd 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobject.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobject.md @@ -15,6 +15,7 @@ export interface SavedObject | Property | Type | Description | | --- | --- | --- | | [attributes](./kibana-plugin-core-server.savedobject.attributes.md) | T | The data for a Saved Object is stored as an object in the attributes property. | +| [coreMigrationVersion](./kibana-plugin-core-server.savedobject.coremigrationversion.md) | string | A semver value that is used when upgrading objects between Kibana versions. | | [error](./kibana-plugin-core-server.savedobject.error.md) | SavedObjectError | | | [id](./kibana-plugin-core-server.savedobject.id.md) | string | The ID of this Saved Object, guaranteed to be unique for all objects of the same type | | [migrationVersion](./kibana-plugin-core-server.savedobject.migrationversion.md) | SavedObjectsMigrationVersion | Information about the migrations that have been applied to this SavedObject. When Kibana starts up, KibanaMigrator detects outdated documents and migrates them based on this value. For each migration that has been applied, the plugin's name is used as a key and the latest migration version as the value. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.md index eb35bb6a4ea5..0e8fa73039d4 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.md @@ -18,4 +18,5 @@ export interface SavedObjectExportBaseOptions | [excludeExportDetails](./kibana-plugin-core-server.savedobjectexportbaseoptions.excludeexportdetails.md) | boolean | flag to not append [export details](./kibana-plugin-core-server.savedobjectsexportresultdetails.md) to the end of the export stream. | | [includeReferencesDeep](./kibana-plugin-core-server.savedobjectexportbaseoptions.includereferencesdeep.md) | boolean | flag to also include all related saved objects in the export stream. | | [namespace](./kibana-plugin-core-server.savedobjectexportbaseoptions.namespace.md) | string | optional namespace to override the namespace used by the savedObjectsClient. | +| [request](./kibana-plugin-core-server.savedobjectexportbaseoptions.request.md) | KibanaRequest | The http request initiating the export. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.request.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.request.md new file mode 100644 index 000000000000..d425f9b88e81 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectexportbaseoptions.request.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectExportBaseOptions](./kibana-plugin-core-server.savedobjectexportbaseoptions.md) > [request](./kibana-plugin-core-server.savedobjectexportbaseoptions.request.md) + +## SavedObjectExportBaseOptions.request property + +The http request initiating the export. + +Signature: + +```typescript +request: KibanaRequest; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationmap.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationmap.md index 2ab9fcaf428b..c07a41e28d45 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationmap.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationmap.md @@ -4,7 +4,7 @@ ## SavedObjectMigrationMap interface -A map of [migration functions](./kibana-plugin-core-server.savedobjectmigrationfn.md) to be used for a given type. The map's keys must be valid semver versions. +A map of [migration functions](./kibana-plugin-core-server.savedobjectmigrationfn.md) to be used for a given type. The map's keys must be valid semver versions, and they cannot exceed the current Kibana version. For a given document, only migrations with a higher version number than that of the document will be applied. Migrations are executed in order, starting from the lowest version and ending with the highest one. diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.coremigrationversion.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.coremigrationversion.md new file mode 100644 index 000000000000..fb1f485cdf20 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.coremigrationversion.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsBulkCreateObject](./kibana-plugin-core-server.savedobjectsbulkcreateobject.md) > [coreMigrationVersion](./kibana-plugin-core-server.savedobjectsbulkcreateobject.coremigrationversion.md) + +## SavedObjectsBulkCreateObject.coreMigrationVersion property + +A semver value that is used when upgrading objects between Kibana versions. If undefined, this will be automatically set to the current Kibana version when the object is created. If this is set to a non-semver value, or it is set to a semver value greater than the current Kibana version, it will result in an error. + +Signature: + +```typescript +coreMigrationVersion?: string; +``` + +## Remarks + +Do not attempt to set this manually. It should only be used if you retrieved an existing object that had the `coreMigrationVersion` field set and you want to create it again. + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md index 5ac5f6d9807b..6fc01212a2e4 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md @@ -16,6 +16,7 @@ export interface SavedObjectsBulkCreateObject | Property | Type | Description | | --- | --- | --- | | [attributes](./kibana-plugin-core-server.savedobjectsbulkcreateobject.attributes.md) | T | | +| [coreMigrationVersion](./kibana-plugin-core-server.savedobjectsbulkcreateobject.coremigrationversion.md) | string | A semver value that is used when upgrading objects between Kibana versions. If undefined, this will be automatically set to the current Kibana version when the object is created. If this is set to a non-semver value, or it is set to a semver value greater than the current Kibana version, it will result in an error. | | [id](./kibana-plugin-core-server.savedobjectsbulkcreateobject.id.md) | string | | | [initialNamespaces](./kibana-plugin-core-server.savedobjectsbulkcreateobject.initialnamespaces.md) | string[] | Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md).Note: this can only be used for multi-namespace object types. | | [migrationVersion](./kibana-plugin-core-server.savedobjectsbulkcreateobject.migrationversion.md) | SavedObjectsMigrationVersion | Information about the migrations that have been applied to this SavedObject. When Kibana starts up, KibanaMigrator detects outdated documents and migrates them based on this value. For each migration that has been applied, the plugin's name is used as a key and the latest migration version as the value. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.md index 7fb34631c736..da1f4d029ea2 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.md @@ -36,5 +36,6 @@ The constructor for this class is marked as internal. Third-party code should no | [find(options)](./kibana-plugin-core-server.savedobjectsclient.find.md) | | Find all SavedObjects matching the search query | | [get(type, id, options)](./kibana-plugin-core-server.savedobjectsclient.get.md) | | Retrieves a single object | | [removeReferencesTo(type, id, options)](./kibana-plugin-core-server.savedobjectsclient.removereferencesto.md) | | Updates all objects containing a reference to the given {type, id} tuple to remove the said reference. | +| [resolve(type, id, options)](./kibana-plugin-core-server.savedobjectsclient.resolve.md) | | Resolves a single object, using any legacy URL alias if it exists | | [update(type, id, attributes, options)](./kibana-plugin-core-server.savedobjectsclient.update.md) | | Updates an SavedObject | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.resolve.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.resolve.md new file mode 100644 index 000000000000..b9a63f0b8c05 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsclient.resolve.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsClient](./kibana-plugin-core-server.savedobjectsclient.md) > [resolve](./kibana-plugin-core-server.savedobjectsclient.resolve.md) + +## SavedObjectsClient.resolve() method + +Resolves a single object, using any legacy URL alias if it exists + +Signature: + +```typescript +resolve(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | string | The type of SavedObject to retrieve | +| id | string | The ID of the SavedObject to retrieve | +| options | SavedObjectsBaseOptions | | + +Returns: + +`Promise>` + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.coremigrationversion.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.coremigrationversion.md new file mode 100644 index 000000000000..e2a4064ec4f3 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.coremigrationversion.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md) > [coreMigrationVersion](./kibana-plugin-core-server.savedobjectscreateoptions.coremigrationversion.md) + +## SavedObjectsCreateOptions.coreMigrationVersion property + +A semver value that is used when upgrading objects between Kibana versions. If undefined, this will be automatically set to the current Kibana version when the object is created. If this is set to a non-semver value, or it is set to a semver value greater than the current Kibana version, it will result in an error. + +Signature: + +```typescript +coreMigrationVersion?: string; +``` + +## Remarks + +Do not attempt to set this manually. It should only be used if you retrieved an existing object that had the `coreMigrationVersion` field set and you want to create it again. + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md index e6d306784f8a..1805f389d4e7 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md @@ -15,6 +15,7 @@ export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions | Property | Type | Description | | --- | --- | --- | +| [coreMigrationVersion](./kibana-plugin-core-server.savedobjectscreateoptions.coremigrationversion.md) | string | A semver value that is used when upgrading objects between Kibana versions. If undefined, this will be automatically set to the current Kibana version when the object is created. If this is set to a non-semver value, or it is set to a semver value greater than the current Kibana version, it will result in an error. | | [id](./kibana-plugin-core-server.savedobjectscreateoptions.id.md) | string | (not recommended) Specify an id for the document | | [initialNamespaces](./kibana-plugin-core-server.savedobjectscreateoptions.initialnamespaces.md) | string[] | Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md).Note: this can only be used for multi-namespace object types. | | [migrationVersion](./kibana-plugin-core-server.savedobjectscreateoptions.migrationversion.md) | SavedObjectsMigrationVersion | Information about the migrations that have been applied to this SavedObject. When Kibana starts up, KibanaMigrator detects outdated documents and migrates them based on this value. For each migration that has been applied, the plugin's name is used as a key and the latest migration version as the value. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporter._constructor_.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporter._constructor_.md index cc192b03ca7c..5e959bbee7be 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporter._constructor_.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporter._constructor_.md @@ -9,8 +9,9 @@ Constructs a new instance of the `SavedObjectsExporter` class Signature: ```typescript -constructor({ savedObjectsClient, exportSizeLimit, }: { +constructor({ savedObjectsClient, typeRegistry, exportSizeLimit, }: { savedObjectsClient: SavedObjectsClientContract; + typeRegistry: ISavedObjectTypeRegistry; exportSizeLimit: number; }); ``` @@ -19,5 +20,5 @@ constructor({ savedObjectsClient, exportSizeLimit, }: { | Parameter | Type | Description | | --- | --- | --- | -| { savedObjectsClient, exportSizeLimit, } | {
savedObjectsClient: SavedObjectsClientContract;
exportSizeLimit: number;
} | | +| { savedObjectsClient, typeRegistry, exportSizeLimit, } | {
savedObjectsClient: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
exportSizeLimit: number;
} | | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporter.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporter.md index d8d9248f34af..727108b824c8 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporter.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporter.md @@ -15,7 +15,7 @@ export declare class SavedObjectsExporter | Constructor | Modifiers | Description | | --- | --- | --- | -| [(constructor)({ savedObjectsClient, exportSizeLimit, })](./kibana-plugin-core-server.savedobjectsexporter._constructor_.md) | | Constructs a new instance of the SavedObjectsExporter class | +| [(constructor)({ savedObjectsClient, typeRegistry, exportSizeLimit, })](./kibana-plugin-core-server.savedobjectsexporter._constructor_.md) | | Constructs a new instance of the SavedObjectsExporter class | ## Properties diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporterror.invalidtransformerror.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporterror.invalidtransformerror.md new file mode 100644 index 000000000000..5a390bd45042 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporterror.invalidtransformerror.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsExportError](./kibana-plugin-core-server.savedobjectsexporterror.md) > [invalidTransformError](./kibana-plugin-core-server.savedobjectsexporterror.invalidtransformerror.md) + +## SavedObjectsExportError.invalidTransformError() method + +Error returned when a [export tranform](./kibana-plugin-core-server.savedobjectsexporttransform.md) performed an invalid operation during the transform, such as removing objects from the export, or changing an object's type or id. + +Signature: + +```typescript +static invalidTransformError(objectKeys: string[]): SavedObjectsExportError; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| objectKeys | string[] | | + +Returns: + +`SavedObjectsExportError` + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporterror.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporterror.md index bfeaa03a9470..7d5c6e5d89a5 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporterror.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporterror.md @@ -29,5 +29,7 @@ export declare class SavedObjectsExportError extends Error | Method | Modifiers | Description | | --- | --- | --- | | [exportSizeExceeded(limit)](./kibana-plugin-core-server.savedobjectsexporterror.exportsizeexceeded.md) | static | | +| [invalidTransformError(objectKeys)](./kibana-plugin-core-server.savedobjectsexporterror.invalidtransformerror.md) | static | Error returned when a [export tranform](./kibana-plugin-core-server.savedobjectsexporttransform.md) performed an invalid operation during the transform, such as removing objects from the export, or changing an object's type or id. | | [objectFetchError(objects)](./kibana-plugin-core-server.savedobjectsexporterror.objectfetcherror.md) | static | | +| [objectTransformError(objects, cause)](./kibana-plugin-core-server.savedobjectsexporterror.objecttransformerror.md) | static | Error returned when a [export tranform](./kibana-plugin-core-server.savedobjectsexporttransform.md) threw an error | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporterror.objecttransformerror.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporterror.objecttransformerror.md new file mode 100644 index 000000000000..4463e9ff06da --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporterror.objecttransformerror.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsExportError](./kibana-plugin-core-server.savedobjectsexporterror.md) > [objectTransformError](./kibana-plugin-core-server.savedobjectsexporterror.objecttransformerror.md) + +## SavedObjectsExportError.objectTransformError() method + +Error returned when a [export tranform](./kibana-plugin-core-server.savedobjectsexporttransform.md) threw an error + +Signature: + +```typescript +static objectTransformError(objects: SavedObject[], cause: Error): SavedObjectsExportError; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| objects | SavedObject[] | | +| cause | Error | | + +Returns: + +`SavedObjectsExportError` + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporttransform.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporttransform.md new file mode 100644 index 000000000000..50d4c5425e8f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporttransform.md @@ -0,0 +1,86 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsExportTransform](./kibana-plugin-core-server.savedobjectsexporttransform.md) + +## SavedObjectsExportTransform type + +Transformation function used to mutate the exported objects of the associated type. + +A type's export transform function will be executed once per user-initiated export, for all objects of that type. + +Signature: + +```typescript +export declare type SavedObjectsExportTransform = (context: SavedObjectsExportTransformContext, objects: Array>) => SavedObject[] | Promise; +``` + +## Remarks + +Trying to change an object's id or type during the transform will result in a runtime error during the export process. + +## Example 1 + +Registering a transform function changing the object's attributes during the export + +```ts +// src/plugins/my_plugin/server/plugin.ts +import { myType } from './saved_objects'; + +export class Plugin() { + setup: (core: CoreSetup) => { + core.savedObjects.registerType({ + ...myType, + management: { + ...myType.management, + onExport: (ctx, objects) => { + return objects.map((obj) => ({ + ...obj, + attributes: { + ...obj.attributes, + enabled: false, + } + }) + } + }, + }); + } +} + +``` + +## Example 2 + +Registering a transform function adding additional objects to the export + +```ts +// src/plugins/my_plugin/server/plugin.ts +import { myType } from './saved_objects'; + +export class Plugin() { + setup: (core: CoreSetup) => { + const savedObjectStartContractPromise = getStartServices().then( + ([{ savedObjects: savedObjectsStart }]) => savedObjectsStart + ); + + core.savedObjects.registerType({ + ...myType, + management: { + ...myType.management, + onExport: async (ctx, objects) => { + const { getScopedClient } = await savedObjectStartContractPromise; + const client = getScopedClient(ctx.request); + + const depResponse = await client.find({ + type: 'my-nested-object', + hasReference: objs.map(({ id, type }) => ({ id, type })), + }); + + return [...objs, ...depResponse.saved_objects]; + } + }, + }); + } +} + +``` + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporttransformcontext.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporttransformcontext.md new file mode 100644 index 000000000000..271f0048842b --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporttransformcontext.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsExportTransformContext](./kibana-plugin-core-server.savedobjectsexporttransformcontext.md) + +## SavedObjectsExportTransformContext interface + +Context passed down to a [export transform function](./kibana-plugin-core-server.savedobjectsexporttransform.md) + +Signature: + +```typescript +export interface SavedObjectsExportTransformContext +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [request](./kibana-plugin-core-server.savedobjectsexporttransformcontext.request.md) | KibanaRequest | The request that initiated the export request. Can be used to create scoped services or client inside the [transformation](./kibana-plugin-core-server.savedobjectsexporttransform.md) | + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporttransformcontext.request.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporttransformcontext.request.md new file mode 100644 index 000000000000..fe04698899c7 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsexporttransformcontext.request.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsExportTransformContext](./kibana-plugin-core-server.savedobjectsexporttransformcontext.md) > [request](./kibana-plugin-core-server.savedobjectsexporttransformcontext.request.md) + +## SavedObjectsExportTransformContext.request property + +The request that initiated the export request. Can be used to create scoped services or client inside the [transformation](./kibana-plugin-core-server.savedobjectsexporttransform.md) + +Signature: + +```typescript +request: KibanaRequest; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.actionpath.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.actionpath.md new file mode 100644 index 000000000000..4ec70301d2eb --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.actionpath.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.md) > [actionPath](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.actionpath.md) + +## SavedObjectsImportActionRequiredWarning.actionPath property + +The path (without the basePath) that the user should be redirect to to address this warning. + +Signature: + +```typescript +actionPath: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.buttonlabel.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.buttonlabel.md new file mode 100644 index 000000000000..7fb5d53c487a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.buttonlabel.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.md) > [buttonLabel](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.buttonlabel.md) + +## SavedObjectsImportActionRequiredWarning.buttonLabel property + +An optional label to use for the link button. If unspecified, a default label will be used. + +Signature: + +```typescript +buttonLabel?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.md new file mode 100644 index 000000000000..ba1e905344af --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.md) + +## SavedObjectsImportActionRequiredWarning interface + +A warning meant to notify that a specific user action is required to finalize the import of some type of object. + + The `actionUrl` must be a path relative to the basePath, and not include it. + +Signature: + +```typescript +export interface SavedObjectsImportActionRequiredWarning +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [actionPath](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.actionpath.md) | string | The path (without the basePath) that the user should be redirect to to address this warning. | +| [buttonLabel](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.buttonlabel.md) | string | An optional label to use for the link button. If unspecified, a default label will be used. | +| [message](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.message.md) | string | The translated message to display to the user. | +| [type](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.type.md) | 'action_required' | | + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.message.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.message.md new file mode 100644 index 000000000000..1ab9afd4bad9 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.message.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.md) > [message](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.message.md) + +## SavedObjectsImportActionRequiredWarning.message property + +The translated message to display to the user. + +Signature: + +```typescript +message: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.type.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.type.md new file mode 100644 index 000000000000..d8f22ef17d8f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.md) > [type](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.type.md) + +## SavedObjectsImportActionRequiredWarning.type property + +Signature: + +```typescript +type: 'action_required'; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporthook.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporthook.md new file mode 100644 index 000000000000..8d50ef94577d --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporthook.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportHook](./kibana-plugin-core-server.savedobjectsimporthook.md) + +## SavedObjectsImportHook type + +A hook associated with a specific saved object type, that will be invoked during the import process. The hook will have access to the objects of the registered type. + +Currently, the only supported feature for import hooks is to return warnings to be displayed in the UI when the import succeeds. + + The only interactions the hook can have with the import process is via the hook's response. Mutating the objects inside the hook's code will have no effect. + +Signature: + +```typescript +export declare type SavedObjectsImportHook = (objects: Array>) => SavedObjectsImportHookResult | Promise; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporthookresult.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporthookresult.md new file mode 100644 index 000000000000..9756ce7fac35 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporthookresult.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportHookResult](./kibana-plugin-core-server.savedobjectsimporthookresult.md) + +## SavedObjectsImportHookResult interface + +Result from a [import hook](./kibana-plugin-core-server.savedobjectsimporthook.md) + +Signature: + +```typescript +export interface SavedObjectsImportHookResult +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [warnings](./kibana-plugin-core-server.savedobjectsimporthookresult.warnings.md) | SavedObjectsImportWarning[] | An optional list of warnings to display in the UI when the import succeeds. | + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporthookresult.warnings.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporthookresult.warnings.md new file mode 100644 index 000000000000..682b384f8d36 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimporthookresult.warnings.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportHookResult](./kibana-plugin-core-server.savedobjectsimporthookresult.md) > [warnings](./kibana-plugin-core-server.savedobjectsimporthookresult.warnings.md) + +## SavedObjectsImportHookResult.warnings property + +An optional list of warnings to display in the UI when the import succeeds. + +Signature: + +```typescript +warnings?: SavedObjectsImportWarning[]; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportresponse.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportresponse.md index 94d24e946b5b..55f651197490 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportresponse.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportresponse.md @@ -20,4 +20,5 @@ export interface SavedObjectsImportResponse | [success](./kibana-plugin-core-server.savedobjectsimportresponse.success.md) | boolean | | | [successCount](./kibana-plugin-core-server.savedobjectsimportresponse.successcount.md) | number | | | [successResults](./kibana-plugin-core-server.savedobjectsimportresponse.successresults.md) | SavedObjectsImportSuccess[] | | +| [warnings](./kibana-plugin-core-server.savedobjectsimportresponse.warnings.md) | SavedObjectsImportWarning[] | | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportresponse.warnings.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportresponse.warnings.md new file mode 100644 index 000000000000..88cccf7f527e --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportresponse.warnings.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportResponse](./kibana-plugin-core-server.savedobjectsimportresponse.md) > [warnings](./kibana-plugin-core-server.savedobjectsimportresponse.warnings.md) + +## SavedObjectsImportResponse.warnings property + +Signature: + +```typescript +warnings: SavedObjectsImportWarning[]; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportsimplewarning.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportsimplewarning.md new file mode 100644 index 000000000000..52d46e4f8db8 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportsimplewarning.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportSimpleWarning](./kibana-plugin-core-server.savedobjectsimportsimplewarning.md) + +## SavedObjectsImportSimpleWarning interface + +A simple informative warning that will be displayed to the user. + +Signature: + +```typescript +export interface SavedObjectsImportSimpleWarning +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [message](./kibana-plugin-core-server.savedobjectsimportsimplewarning.message.md) | string | The translated message to display to the user | +| [type](./kibana-plugin-core-server.savedobjectsimportsimplewarning.type.md) | 'simple' | | + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportsimplewarning.message.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportsimplewarning.message.md new file mode 100644 index 000000000000..1e3ac7ec1136 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportsimplewarning.message.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportSimpleWarning](./kibana-plugin-core-server.savedobjectsimportsimplewarning.md) > [message](./kibana-plugin-core-server.savedobjectsimportsimplewarning.message.md) + +## SavedObjectsImportSimpleWarning.message property + +The translated message to display to the user + +Signature: + +```typescript +message: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportsimplewarning.type.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportsimplewarning.type.md new file mode 100644 index 000000000000..660b1b39d6c3 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportsimplewarning.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportSimpleWarning](./kibana-plugin-core-server.savedobjectsimportsimplewarning.md) > [type](./kibana-plugin-core-server.savedobjectsimportsimplewarning.type.md) + +## SavedObjectsImportSimpleWarning.type property + +Signature: + +```typescript +type: 'simple'; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportwarning.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportwarning.md new file mode 100644 index 000000000000..257751f16601 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsimportwarning.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsImportWarning](./kibana-plugin-core-server.savedobjectsimportwarning.md) + +## SavedObjectsImportWarning type + +Composite type of all the possible types of import warnings. + +See [SavedObjectsImportSimpleWarning](./kibana-plugin-core-server.savedobjectsimportsimplewarning.md) and [SavedObjectsImportActionRequiredWarning](./kibana-plugin-core-server.savedobjectsimportactionrequiredwarning.md) for more details. + +Signature: + +```typescript +export declare type SavedObjectsImportWarning = SavedObjectsImportSimpleWarning | SavedObjectsImportActionRequiredWarning; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrawdocparseoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrawdocparseoptions.md new file mode 100644 index 000000000000..708d1bc9c514 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrawdocparseoptions.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsRawDocParseOptions](./kibana-plugin-core-server.savedobjectsrawdocparseoptions.md) + +## SavedObjectsRawDocParseOptions interface + +Options that can be specified when using the saved objects serializer to parse a raw document. + +Signature: + +```typescript +export interface SavedObjectsRawDocParseOptions +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [namespaceTreatment](./kibana-plugin-core-server.savedobjectsrawdocparseoptions.namespacetreatment.md) | 'strict' | 'lax' | Optional setting to allow for lax handling of the raw document ID and namespace field. This is needed when a previously single-namespace object type is converted to a multi-namespace object type, and it is only intended to be used during upgrade migrations.If not specified, the default treatment is strict. | + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrawdocparseoptions.namespacetreatment.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrawdocparseoptions.namespacetreatment.md new file mode 100644 index 000000000000..c315d78aaf41 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrawdocparseoptions.namespacetreatment.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsRawDocParseOptions](./kibana-plugin-core-server.savedobjectsrawdocparseoptions.md) > [namespaceTreatment](./kibana-plugin-core-server.savedobjectsrawdocparseoptions.namespacetreatment.md) + +## SavedObjectsRawDocParseOptions.namespaceTreatment property + +Optional setting to allow for lax handling of the raw document ID and namespace field. This is needed when a previously single-namespace object type is converted to a multi-namespace object type, and it is only intended to be used during upgrade migrations. + +If not specified, the default treatment is `strict`. + +Signature: + +```typescript +namespaceTreatment?: 'strict' | 'lax'; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md index c7e5b0476bad..4d13fea12572 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.md @@ -28,5 +28,6 @@ export declare class SavedObjectsRepository | [get(type, id, options)](./kibana-plugin-core-server.savedobjectsrepository.get.md) | | Gets a single object | | [incrementCounter(type, id, counterFields, options)](./kibana-plugin-core-server.savedobjectsrepository.incrementcounter.md) | | Increments all the specified counter fields (by one by default). Creates the document if one doesn't exist for the given id. | | [removeReferencesTo(type, id, options)](./kibana-plugin-core-server.savedobjectsrepository.removereferencesto.md) | | Updates all objects containing a reference to the given {type, id} tuple to remove the said reference. | +| [resolve(type, id, options)](./kibana-plugin-core-server.savedobjectsrepository.resolve.md) | | Resolves a single object, using any legacy URL alias if it exists | | [update(type, id, attributes, options)](./kibana-plugin-core-server.savedobjectsrepository.update.md) | | Updates an object | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.resolve.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.resolve.md new file mode 100644 index 000000000000..7d0a1c7d204b --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsrepository.resolve.md @@ -0,0 +1,28 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsRepository](./kibana-plugin-core-server.savedobjectsrepository.md) > [resolve](./kibana-plugin-core-server.savedobjectsrepository.resolve.md) + +## SavedObjectsRepository.resolve() method + +Resolves a single object, using any legacy URL alias if it exists + +Signature: + +```typescript +resolve(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| type | string | | +| id | string | | +| options | SavedObjectsBaseOptions | | + +Returns: + +`Promise>` + +{promise} - { saved\_object, outcome } + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsresolveresponse.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsresolveresponse.md new file mode 100644 index 000000000000..cfb309da0a71 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsresolveresponse.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsResolveResponse](./kibana-plugin-core-server.savedobjectsresolveresponse.md) + +## SavedObjectsResolveResponse interface + + +Signature: + +```typescript +export interface SavedObjectsResolveResponse +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [outcome](./kibana-plugin-core-server.savedobjectsresolveresponse.outcome.md) | 'exactMatch' | 'aliasMatch' | 'conflict' | The outcome for a successful resolve call is one of the following values:\* 'exactMatch' -- One document exactly matched the given ID. \* 'aliasMatch' -- One document with a legacy URL alias matched the given ID; in this case the saved_object.id field is different than the given ID. \* 'conflict' -- Two documents matched the given ID, one was an exact match and another with a legacy URL alias; in this case the saved_object object is the exact match, and the saved_object.id field is the same as the given ID. | +| [saved\_object](./kibana-plugin-core-server.savedobjectsresolveresponse.saved_object.md) | SavedObject<T> | | + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsresolveresponse.outcome.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsresolveresponse.outcome.md new file mode 100644 index 000000000000..eadd85b17537 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsresolveresponse.outcome.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsResolveResponse](./kibana-plugin-core-server.savedobjectsresolveresponse.md) > [outcome](./kibana-plugin-core-server.savedobjectsresolveresponse.outcome.md) + +## SavedObjectsResolveResponse.outcome property + +The outcome for a successful `resolve` call is one of the following values: + +\* `'exactMatch'` -- One document exactly matched the given ID. \* `'aliasMatch'` -- One document with a legacy URL alias matched the given ID; in this case the `saved_object.id` field is different than the given ID. \* `'conflict'` -- Two documents matched the given ID, one was an exact match and another with a legacy URL alias; in this case the `saved_object` object is the exact match, and the `saved_object.id` field is the same as the given ID. + +Signature: + +```typescript +outcome: 'exactMatch' | 'aliasMatch' | 'conflict'; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsresolveresponse.saved_object.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsresolveresponse.saved_object.md new file mode 100644 index 000000000000..c184312675f7 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsresolveresponse.saved_object.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsResolveResponse](./kibana-plugin-core-server.savedobjectsresolveresponse.md) > [saved\_object](./kibana-plugin-core-server.savedobjectsresolveresponse.saved_object.md) + +## SavedObjectsResolveResponse.saved\_object property + +Signature: + +```typescript +saved_object: SavedObject; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.generaterawlegacyurlaliasid.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.generaterawlegacyurlaliasid.md new file mode 100644 index 000000000000..d33f42ee2cf5 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.generaterawlegacyurlaliasid.md @@ -0,0 +1,26 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsSerializer](./kibana-plugin-core-server.savedobjectsserializer.md) > [generateRawLegacyUrlAliasId](./kibana-plugin-core-server.savedobjectsserializer.generaterawlegacyurlaliasid.md) + +## SavedObjectsSerializer.generateRawLegacyUrlAliasId() method + +Given a saved object type and id, generates the compound id that is stored in the raw document for its legacy URL alias. + +Signature: + +```typescript +generateRawLegacyUrlAliasId(namespace: string, type: string, id: string): string; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| namespace | string | | +| type | string | | +| id | string | | + +Returns: + +`string` + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.israwsavedobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.israwsavedobject.md index b9033b00624c..1094cc25ab55 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.israwsavedobject.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.israwsavedobject.md @@ -9,14 +9,15 @@ Determines whether or not the raw document can be converted to a saved object. Signature: ```typescript -isRawSavedObject(rawDoc: SavedObjectsRawDoc): boolean; +isRawSavedObject(doc: SavedObjectsRawDoc, options?: SavedObjectsRawDocParseOptions): boolean; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| rawDoc | SavedObjectsRawDoc | | +| doc | SavedObjectsRawDoc | | +| options | SavedObjectsRawDocParseOptions | | Returns: diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.md index 129e6d8bf90f..c7fa5fc85c61 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.md @@ -23,7 +23,8 @@ The constructor for this class is marked as internal. Third-party code should no | Method | Modifiers | Description | | --- | --- | --- | | [generateRawId(namespace, type, id)](./kibana-plugin-core-server.savedobjectsserializer.generaterawid.md) | | Given a saved object type and id, generates the compound id that is stored in the raw document. | -| [isRawSavedObject(rawDoc)](./kibana-plugin-core-server.savedobjectsserializer.israwsavedobject.md) | | Determines whether or not the raw document can be converted to a saved object. | -| [rawToSavedObject(doc)](./kibana-plugin-core-server.savedobjectsserializer.rawtosavedobject.md) | | Converts a document from the format that is stored in elasticsearch to the saved object client format. | +| [generateRawLegacyUrlAliasId(namespace, type, id)](./kibana-plugin-core-server.savedobjectsserializer.generaterawlegacyurlaliasid.md) | | Given a saved object type and id, generates the compound id that is stored in the raw document for its legacy URL alias. | +| [isRawSavedObject(doc, options)](./kibana-plugin-core-server.savedobjectsserializer.israwsavedobject.md) | | Determines whether or not the raw document can be converted to a saved object. | +| [rawToSavedObject(doc, options)](./kibana-plugin-core-server.savedobjectsserializer.rawtosavedobject.md) | | Converts a document from the format that is stored in elasticsearch to the saved object client format. | | [savedObjectToRaw(savedObj)](./kibana-plugin-core-server.savedobjectsserializer.savedobjecttoraw.md) | | Converts a document from the saved object client format to the format that is stored in elasticsearch. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.rawtosavedobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.rawtosavedobject.md index dc9a2ef85839..3fc386f26314 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.rawtosavedobject.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsserializer.rawtosavedobject.md @@ -9,7 +9,7 @@ Converts a document from the format that is stored in elasticsearch to the saved Signature: ```typescript -rawToSavedObject(doc: SavedObjectsRawDoc): SavedObjectSanitizedDoc; +rawToSavedObject(doc: SavedObjectsRawDoc, options?: SavedObjectsRawDocParseOptions): SavedObjectSanitizedDoc; ``` ## Parameters @@ -17,6 +17,7 @@ rawToSavedObject(doc: SavedObjectsRawDoc): SavedObjectSanitizedDoc; | Parameter | Type | Description | | --- | --- | --- | | doc | SavedObjectsRawDoc | | +| options | SavedObjectsRawDocParseOptions | | Returns: diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.converttomultinamespacetypeversion.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.converttomultinamespacetypeversion.md new file mode 100644 index 000000000000..064bd0b35699 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.converttomultinamespacetypeversion.md @@ -0,0 +1,42 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsType](./kibana-plugin-core-server.savedobjectstype.md) > [convertToMultiNamespaceTypeVersion](./kibana-plugin-core-server.savedobjectstype.converttomultinamespacetypeversion.md) + +## SavedObjectsType.convertToMultiNamespaceTypeVersion property + +If defined, objects of this type will be converted to multi-namespace objects when migrating to this version. + +Requirements: + +1. This string value must be a valid semver version 2. This type must have previously specified [\`namespaceType: 'single'\`](./kibana-plugin-core-server.savedobjectsnamespacetype.md) 3. This type must also specify [\`namespaceType: 'multiple'\`](./kibana-plugin-core-server.savedobjectsnamespacetype.md) + +Example of a single-namespace type in 7.10: + +```ts +{ + name: 'foo', + hidden: false, + namespaceType: 'single', + mappings: {...} +} + +``` +Example after converting to a multi-namespace type in 7.11: + +```ts +{ + name: 'foo', + hidden: false, + namespaceType: 'multiple', + mappings: {...}, + convertToMultiNamespaceTypeVersion: '7.11.0' +} + +``` +Note: a migration function can be optionally specified for the same version. + +Signature: + +```typescript +convertToMultiNamespaceTypeVersion?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md index e5c3fa2b3e92..eacad53be39f 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstype.md @@ -19,6 +19,28 @@ This is only internal for now, and will only be public when we expose the regist | Property | Type | Description | | --- | --- | --- | | [convertToAliasScript](./kibana-plugin-core-server.savedobjectstype.converttoaliasscript.md) | string | If defined, will be used to convert the type to an alias. | +| [convertToMultiNamespaceTypeVersion](./kibana-plugin-core-server.savedobjectstype.converttomultinamespacetypeversion.md) | string | If defined, objects of this type will be converted to multi-namespace objects when migrating to this version.Requirements:1. This string value must be a valid semver version 2. This type must have previously specified [\`namespaceType: 'single'\`](./kibana-plugin-core-server.savedobjectsnamespacetype.md) 3. This type must also specify [\`namespaceType: 'multiple'\`](./kibana-plugin-core-server.savedobjectsnamespacetype.md)Example of a single-namespace type in 7.10: +```ts +{ + name: 'foo', + hidden: false, + namespaceType: 'single', + mappings: {...} +} + +``` +Example after converting to a multi-namespace type in 7.11: +```ts +{ + name: 'foo', + hidden: false, + namespaceType: 'multiple', + mappings: {...}, + convertToMultiNamespaceTypeVersion: '7.11.0' +} + +``` +Note: a migration function can be optionally specified for the same version. | | [hidden](./kibana-plugin-core-server.savedobjectstype.hidden.md) | boolean | Is the type hidden by default. If true, repositories will not have access to this type unless explicitly declared as an extraType when creating the repository.See [createInternalRepository](./kibana-plugin-core-server.savedobjectsservicestart.createinternalrepository.md). | | [indexPattern](./kibana-plugin-core-server.savedobjectstype.indexpattern.md) | string | If defined, the type instances will be stored in the given index instead of the default one. | | [management](./kibana-plugin-core-server.savedobjectstype.management.md) | SavedObjectsTypeManagementDefinition | An optional [saved objects management section](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.md) definition for the type. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md index 9d87e51767ca..e9cc2b12108d 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.md @@ -22,4 +22,6 @@ export interface SavedObjectsTypeManagementDefinition | [getTitle](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.gettitle.md) | (savedObject: SavedObject<any>) => string | Function returning the title to display in the management table. If not defined, will use the object's type and id to generate a label. | | [icon](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.icon.md) | string | The eui icon name to display in the management table. If not defined, the default icon will be used. | | [importableAndExportable](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.importableandexportable.md) | boolean | Is the type importable or exportable. Defaults to false. | +| [onExport](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.onexport.md) | SavedObjectsExportTransform | An optional export transform function that can be used transform the objects of the registered type during the export process.It can be used to either mutate the exported objects, or add additional objects (of any type) to the export list.See [the transform type documentation](./kibana-plugin-core-server.savedobjectsexporttransform.md) for more info and examples. | +| [onImport](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.onimport.md) | SavedObjectsImportHook | An optional [import hook](./kibana-plugin-core-server.savedobjectsimporthook.md) to use when importing given type.Import hooks are executed during the savedObjects import process and allow to interact with the imported objects. See the [hook documentation](./kibana-plugin-core-server.savedobjectsimporthook.md) for more info. | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.onexport.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.onexport.md new file mode 100644 index 000000000000..6302b36a73c6 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.onexport.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsTypeManagementDefinition](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.md) > [onExport](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.onexport.md) + +## SavedObjectsTypeManagementDefinition.onExport property + +An optional export transform function that can be used transform the objects of the registered type during the export process. + +It can be used to either mutate the exported objects, or add additional objects (of any type) to the export list. + +See [the transform type documentation](./kibana-plugin-core-server.savedobjectsexporttransform.md) for more info and examples. + +Signature: + +```typescript +onExport?: SavedObjectsExportTransform; +``` + +## Remarks + +`importableAndExportable` must be `true` to specify this property. + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.onimport.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.onimport.md new file mode 100644 index 000000000000..f6634c01c66b --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstypemanagementdefinition.onimport.md @@ -0,0 +1,55 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsTypeManagementDefinition](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.md) > [onImport](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.onimport.md) + +## SavedObjectsTypeManagementDefinition.onImport property + +An optional [import hook](./kibana-plugin-core-server.savedobjectsimporthook.md) to use when importing given type. + +Import hooks are executed during the savedObjects import process and allow to interact with the imported objects. See the [hook documentation](./kibana-plugin-core-server.savedobjectsimporthook.md) for more info. + +Signature: + +```typescript +onImport?: SavedObjectsImportHook; +``` + +## Remarks + +`importableAndExportable` must be `true` to specify this property. + +## Example + +Registering a hook displaying a warning about a specific type of object + +```ts +// src/plugins/my_plugin/server/plugin.ts +import { myType } from './saved_objects'; + +export class Plugin() { + setup: (core: CoreSetup) => { + core.savedObjects.registerType({ + ...myType, + management: { + ...myType.management, + onImport: (objects) => { + if(someActionIsNeeded(objects)) { + return { + warnings: [ + { + type: 'action_required', + message: 'Objects need to be manually enabled after import', + actionPath: '/app/my-app/require-activation', + }, + ] + } + } + return {}; + } + }, + }); + } +} + +``` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md index 7466e4b9cf65..5fc29ca5031b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md @@ -4,6 +4,8 @@ ## IIndexPattern.getFormatterForField property +Look up a formatter for a given field + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md index ba77e659f083..3a78395b4275 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.md @@ -4,6 +4,8 @@ ## IIndexPattern interface +IIndexPattern allows for an IndexPattern OR an index pattern saved object too ambiguous, should be avoided + Signature: ```typescript @@ -16,11 +18,11 @@ export interface IIndexPattern | --- | --- | --- | | [fieldFormatMap](./kibana-plugin-plugins-data-public.iindexpattern.fieldformatmap.md) | Record<string, SerializedFieldFormat<unknown> | undefined> | | | [fields](./kibana-plugin-plugins-data-public.iindexpattern.fields.md) | IFieldType[] | | -| [getFormatterForField](./kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md) | (field: IndexPatternField | IndexPatternField['spec'] | IFieldType) => FieldFormat | | +| [getFormatterForField](./kibana-plugin-plugins-data-public.iindexpattern.getformatterforfield.md) | (field: IndexPatternField | IndexPatternField['spec'] | IFieldType) => FieldFormat | Look up a formatter for a given field | | [id](./kibana-plugin-plugins-data-public.iindexpattern.id.md) | string | | | [timeFieldName](./kibana-plugin-plugins-data-public.iindexpattern.timefieldname.md) | string | | | [title](./kibana-plugin-plugins-data-public.iindexpattern.title.md) | string | | -| [type](./kibana-plugin-plugins-data-public.iindexpattern.type.md) | string | | +| [type](./kibana-plugin-plugins-data-public.iindexpattern.type.md) | string | Type is used for identifying rollup indices, otherwise left undefined | ## Methods diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.type.md index ea75c20b403c..d517163090c8 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.type.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iindexpattern.type.md @@ -4,6 +4,8 @@ ## IIndexPattern.type property +Type is used for identifying rollup indices, otherwise left undefined + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md new file mode 100644 index 000000000000..5640395139ba --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [addRuntimeField](./kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md) + +## IndexPattern.addRuntimeField() method + +Add a runtime field - Appended to existing mapped field or a new field is created as appropriate + +Signature: + +```typescript +addRuntimeField(name: string, runtimeField: RuntimeField): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | +| runtimeField | RuntimeField | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fieldattrs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fieldattrs.md deleted file mode 100644 index c2e0b9bb855f..000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.fieldattrs.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [fieldAttrs](./kibana-plugin-plugins-data-public.indexpattern.fieldattrs.md) - -## IndexPattern.fieldAttrs property - -Signature: - -```typescript -fieldAttrs: FieldAttrs; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md index b318427012c0..48d94b84497b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md @@ -20,6 +20,7 @@ getAsSavedObjectBody(): { type: string | undefined; typeMeta: string | undefined; allowNoIndex: true | undefined; + runtimeFieldMap: string | undefined; }; ``` Returns: @@ -35,5 +36,6 @@ getAsSavedObjectBody(): { type: string | undefined; typeMeta: string | undefined; allowNoIndex: true | undefined; + runtimeFieldMap: string | undefined; }` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md index 84aeb9ffeb21..37d31a35167d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.getcomputedfields.md @@ -14,6 +14,7 @@ getComputedFields(): { field: any; format: string; }[]; + runtimeFields: Record; }; ``` Returns: @@ -25,5 +26,6 @@ getComputedFields(): { field: any; format: string; }[]; + runtimeFields: Record; }` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.intervalname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.intervalname.md index 762b4a37bfd2..81e7fb9c1d57 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.intervalname.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.intervalname.md @@ -4,6 +4,11 @@ ## IndexPattern.intervalName property +> Warning: This API is now obsolete. +> +> Deprecated. used by time range index patterns +> + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md index b640ef1b8960..53d173d39f50 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.md @@ -22,7 +22,6 @@ export declare class IndexPattern implements IIndexPattern | --- | --- | --- | --- | | [allowNoIndex](./kibana-plugin-plugins-data-public.indexpattern.allownoindex.md) | | boolean | prevents errors when index pattern exists before indices | | [deleteFieldFormat](./kibana-plugin-plugins-data-public.indexpattern.deletefieldformat.md) | | (fieldName: string) => void | | -| [fieldAttrs](./kibana-plugin-plugins-data-public.indexpattern.fieldattrs.md) | | FieldAttrs | | | [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpattern.fieldformatmap.md) | | Record<string, any> | | | [fields](./kibana-plugin-plugins-data-public.indexpattern.fields.md) | | IIndexPatternFieldList & {
toSpec: () => IndexPatternFieldMap;
} | | | [flattenHit](./kibana-plugin-plugins-data-public.indexpattern.flattenhit.md) | | (hit: Record<string, any>, deep?: boolean) => Record<string, any> | | @@ -38,14 +37,15 @@ export declare class IndexPattern implements IIndexPattern | [sourceFilters](./kibana-plugin-plugins-data-public.indexpattern.sourcefilters.md) | | SourceFilter[] | | | [timeFieldName](./kibana-plugin-plugins-data-public.indexpattern.timefieldname.md) | | string | undefined | | | [title](./kibana-plugin-plugins-data-public.indexpattern.title.md) | | string | | -| [type](./kibana-plugin-plugins-data-public.indexpattern.type.md) | | string | undefined | | -| [typeMeta](./kibana-plugin-plugins-data-public.indexpattern.typemeta.md) | | TypeMeta | | -| [version](./kibana-plugin-plugins-data-public.indexpattern.version.md) | | string | undefined | | +| [type](./kibana-plugin-plugins-data-public.indexpattern.type.md) | | string | undefined | Type is used to identify rollup index patterns | +| [typeMeta](./kibana-plugin-plugins-data-public.indexpattern.typemeta.md) | | TypeMeta | Only used by rollup indices, used by rollup specific endpoint to load field list | +| [version](./kibana-plugin-plugins-data-public.indexpattern.version.md) | | string | undefined | SavedObject version | ## Methods | Method | Modifiers | Description | | --- | --- | --- | +| [addRuntimeField(name, runtimeField)](./kibana-plugin-plugins-data-public.indexpattern.addruntimefield.md) | | Add a runtime field - Appended to existing mapped field or a new field is created as appropriate | | [addScriptedField(name, script, fieldType)](./kibana-plugin-plugins-data-public.indexpattern.addscriptedfield.md) | | Add scripted field to field list | | [getAggregationRestrictions()](./kibana-plugin-plugins-data-public.indexpattern.getaggregationrestrictions.md) | | | | [getAsSavedObjectBody()](./kibana-plugin-plugins-data-public.indexpattern.getassavedobjectbody.md) | | Returns index pattern as saved object body for saving | @@ -59,9 +59,10 @@ export declare class IndexPattern implements IIndexPattern | [getTimeField()](./kibana-plugin-plugins-data-public.indexpattern.gettimefield.md) | | | | [isTimeBased()](./kibana-plugin-plugins-data-public.indexpattern.istimebased.md) | | | | [isTimeNanosBased()](./kibana-plugin-plugins-data-public.indexpattern.istimenanosbased.md) | | | +| [removeRuntimeField(name)](./kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md) | | Remove a runtime field - removed from mapped field or removed unmapped field as appropriate | | [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-public.indexpattern.removescriptedfield.md) | | Remove scripted field from field list | | [setFieldAttrs(fieldName, attrName, value)](./kibana-plugin-plugins-data-public.indexpattern.setfieldattrs.md) | | | | [setFieldCount(fieldName, count)](./kibana-plugin-plugins-data-public.indexpattern.setfieldcount.md) | | | | [setFieldCustomLabel(fieldName, customLabel)](./kibana-plugin-plugins-data-public.indexpattern.setfieldcustomlabel.md) | | | -| [toSpec()](./kibana-plugin-plugins-data-public.indexpattern.tospec.md) | | | +| [toSpec()](./kibana-plugin-plugins-data-public.indexpattern.tospec.md) | | Create static representation of index pattern | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md new file mode 100644 index 000000000000..7a5228fece78 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPattern](./kibana-plugin-plugins-data-public.indexpattern.md) > [removeRuntimeField](./kibana-plugin-plugins-data-public.indexpattern.removeruntimefield.md) + +## IndexPattern.removeRuntimeField() method + +Remove a runtime field - removed from mapped field or removed unmapped field as appropriate + +Signature: + +```typescript +removeRuntimeField(name: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.tospec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.tospec.md index d1a78eea660c..d8153530e5c1 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.tospec.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.tospec.md @@ -4,6 +4,8 @@ ## IndexPattern.toSpec() method +Create static representation of index pattern + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.type.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.type.md index 7a10d058b9c6..0f9572d1bad2 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.type.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.type.md @@ -4,6 +4,8 @@ ## IndexPattern.type property +Type is used to identify rollup index patterns + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.typemeta.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.typemeta.md index ea8533a8d837..ce316ff9638a 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.typemeta.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.typemeta.md @@ -4,6 +4,8 @@ ## IndexPattern.typeMeta property +Only used by rollup indices, used by rollup specific endpoint to load field list + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.version.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.version.md index 99d3bc4e7a04..2083bd65e9b0 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.version.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpattern.version.md @@ -4,6 +4,8 @@ ## IndexPattern.version property +SavedObject version + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md index 1bbede565894..41a4d3c55694 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.md @@ -4,6 +4,8 @@ ## IndexPatternAttributes interface +Interface for an index pattern saved object + Signature: ```typescript @@ -19,6 +21,7 @@ export interface IndexPatternAttributes | [fieldFormatMap](./kibana-plugin-plugins-data-public.indexpatternattributes.fieldformatmap.md) | string | | | [fields](./kibana-plugin-plugins-data-public.indexpatternattributes.fields.md) | string | | | [intervalName](./kibana-plugin-plugins-data-public.indexpatternattributes.intervalname.md) | string | | +| [runtimeFieldMap](./kibana-plugin-plugins-data-public.indexpatternattributes.runtimefieldmap.md) | string | | | [sourceFilters](./kibana-plugin-plugins-data-public.indexpatternattributes.sourcefilters.md) | string | | | [timeFieldName](./kibana-plugin-plugins-data-public.indexpatternattributes.timefieldname.md) | string | | | [title](./kibana-plugin-plugins-data-public.indexpatternattributes.title.md) | string | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimefieldmap.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimefieldmap.md new file mode 100644 index 000000000000..0df7a9841e41 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternattributes.runtimefieldmap.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) > [runtimeFieldMap](./kibana-plugin-plugins-data-public.indexpatternattributes.runtimefieldmap.md) + +## IndexPatternAttributes.runtimeFieldMap property + +Signature: + +```typescript +runtimeFieldMap?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.ismapped.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.ismapped.md new file mode 100644 index 000000000000..653a1f2b39c2 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.ismapped.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [isMapped](./kibana-plugin-plugins-data-public.indexpatternfield.ismapped.md) + +## IndexPatternField.isMapped property + +Is the field part of the index mapping? + +Signature: + +```typescript +get isMapped(): boolean | undefined; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md index c8118770ed39..05c807b1cd84 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.md @@ -27,9 +27,11 @@ export declare class IndexPatternField implements IFieldType | [displayName](./kibana-plugin-plugins-data-public.indexpatternfield.displayname.md) | | string | | | [esTypes](./kibana-plugin-plugins-data-public.indexpatternfield.estypes.md) | | string[] | undefined | | | [filterable](./kibana-plugin-plugins-data-public.indexpatternfield.filterable.md) | | boolean | | +| [isMapped](./kibana-plugin-plugins-data-public.indexpatternfield.ismapped.md) | | boolean | undefined | Is the field part of the index mapping? | | [lang](./kibana-plugin-plugins-data-public.indexpatternfield.lang.md) | | string | undefined | Script field language | | [name](./kibana-plugin-plugins-data-public.indexpatternfield.name.md) | | string | | | [readFromDocValues](./kibana-plugin-plugins-data-public.indexpatternfield.readfromdocvalues.md) | | boolean | | +| [runtimeField](./kibana-plugin-plugins-data-public.indexpatternfield.runtimefield.md) | | RuntimeField | undefined | | | [script](./kibana-plugin-plugins-data-public.indexpatternfield.script.md) | | string | undefined | Script field code | | [scripted](./kibana-plugin-plugins-data-public.indexpatternfield.scripted.md) | | boolean | | | [searchable](./kibana-plugin-plugins-data-public.indexpatternfield.searchable.md) | | boolean | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.runtimefield.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.runtimefield.md new file mode 100644 index 000000000000..ad3b81eb23ed --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternfield.runtimefield.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternField](./kibana-plugin-plugins-data-public.indexpatternfield.md) > [runtimeField](./kibana-plugin-plugins-data-public.indexpatternfield.runtimefield.md) + +## IndexPatternField.runtimeField property + +Signature: + +```typescript +get runtimeField(): RuntimeField | undefined; + +set runtimeField(runtimeField: RuntimeField | undefined); +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.id.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.id.md index 55eadbf36c66..807f77784168 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.id.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.id.md @@ -4,6 +4,8 @@ ## IndexPatternSpec.id property +saved object id + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md index 98748661256d..90c5ee566623 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md @@ -4,6 +4,11 @@ ## IndexPatternSpec.intervalName property +> Warning: This API is now obsolete. +> +> Deprecated. Was used by time range based index patterns +> + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md index 9357ad7d5077..ae514e3fc6a8 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.md @@ -4,6 +4,8 @@ ## IndexPatternSpec interface +Static index pattern format Serialized data object, representing index pattern attributes and state + Signature: ```typescript @@ -18,12 +20,13 @@ export interface IndexPatternSpec | [fieldAttrs](./kibana-plugin-plugins-data-public.indexpatternspec.fieldattrs.md) | FieldAttrs | | | [fieldFormats](./kibana-plugin-plugins-data-public.indexpatternspec.fieldformats.md) | Record<string, SerializedFieldFormat> | | | [fields](./kibana-plugin-plugins-data-public.indexpatternspec.fields.md) | IndexPatternFieldMap | | -| [id](./kibana-plugin-plugins-data-public.indexpatternspec.id.md) | string | | +| [id](./kibana-plugin-plugins-data-public.indexpatternspec.id.md) | string | saved object id | | [intervalName](./kibana-plugin-plugins-data-public.indexpatternspec.intervalname.md) | string | | +| [runtimeFieldMap](./kibana-plugin-plugins-data-public.indexpatternspec.runtimefieldmap.md) | Record<string, RuntimeField> | | | [sourceFilters](./kibana-plugin-plugins-data-public.indexpatternspec.sourcefilters.md) | SourceFilter[] | | | [timeFieldName](./kibana-plugin-plugins-data-public.indexpatternspec.timefieldname.md) | string | | | [title](./kibana-plugin-plugins-data-public.indexpatternspec.title.md) | string | | | [type](./kibana-plugin-plugins-data-public.indexpatternspec.type.md) | string | | | [typeMeta](./kibana-plugin-plugins-data-public.indexpatternspec.typemeta.md) | TypeMeta | | -| [version](./kibana-plugin-plugins-data-public.indexpatternspec.version.md) | string | | +| [version](./kibana-plugin-plugins-data-public.indexpatternspec.version.md) | string | saved object version string | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimefieldmap.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimefieldmap.md new file mode 100644 index 000000000000..e208760ff188 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.runtimefieldmap.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) > [runtimeFieldMap](./kibana-plugin-plugins-data-public.indexpatternspec.runtimefieldmap.md) + +## IndexPatternSpec.runtimeFieldMap property + +Signature: + +```typescript +runtimeFieldMap?: Record; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.version.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.version.md index 43f7cf0226fb..60975b94e963 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.version.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternspec.version.md @@ -4,6 +4,8 @@ ## IndexPatternSpec.version property +saved object version string + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.create.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.create.md index d7152ba617cc..c8e845eb1d1b 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.create.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.create.md @@ -23,3 +23,5 @@ create(spec: IndexPatternSpec, skipFetchFields?: boolean): Promise `Promise` +IndexPattern + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.find.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.find.md index f642965c5da8..929322fc4794 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.find.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.find.md @@ -4,6 +4,8 @@ ## IndexPatternsService.find property +Find and load index patterns by title + Signature: ```typescript diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md index 30ce1fa1de38..1511de18cab5 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatternsservice.md @@ -23,7 +23,7 @@ export declare class IndexPatternsService | [clearCache](./kibana-plugin-plugins-data-public.indexpatternsservice.clearcache.md) | | (id?: string | undefined) => void | Clear index pattern list cache | | [ensureDefaultIndexPattern](./kibana-plugin-plugins-data-public.indexpatternsservice.ensuredefaultindexpattern.md) | | EnsureDefaultIndexPattern | | | [fieldArrayToMap](./kibana-plugin-plugins-data-public.indexpatternsservice.fieldarraytomap.md) | | (fields: FieldSpec[], fieldAttrs?: FieldAttrs | undefined) => Record<string, FieldSpec> | Converts field array to map | -| [find](./kibana-plugin-plugins-data-public.indexpatternsservice.find.md) | | (search: string, size?: number) => Promise<IndexPattern[]> | | +| [find](./kibana-plugin-plugins-data-public.indexpatternsservice.find.md) | | (search: string, size?: number) => Promise<IndexPattern[]> | Find and load index patterns by title | | [get](./kibana-plugin-plugins-data-public.indexpatternsservice.get.md) | | (id: string) => Promise<IndexPattern> | Get an index pattern by id. Cache optimized | | [getCache](./kibana-plugin-plugins-data-public.indexpatternsservice.getcache.md) | | () => Promise<SavedObject<IndexPatternSavedObjectAttrs>[] | null | undefined> | | | [getDefault](./kibana-plugin-plugins-data-public.indexpatternsservice.getdefault.md) | | () => Promise<IndexPattern | null> | Get default index pattern | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 6a3e7662e59b..65a722868b37 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -65,12 +65,12 @@ | [IEsSearchRequest](./kibana-plugin-plugins-data-public.iessearchrequest.md) | | | [IFieldSubType](./kibana-plugin-plugins-data-public.ifieldsubtype.md) | | | [IFieldType](./kibana-plugin-plugins-data-public.ifieldtype.md) | | -| [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) | | +| [IIndexPattern](./kibana-plugin-plugins-data-public.iindexpattern.md) | IIndexPattern allows for an IndexPattern OR an index pattern saved object too ambiguous, should be avoided | | [IIndexPatternFieldList](./kibana-plugin-plugins-data-public.iindexpatternfieldlist.md) | | | [IKibanaSearchRequest](./kibana-plugin-plugins-data-public.ikibanasearchrequest.md) | | | [IKibanaSearchResponse](./kibana-plugin-plugins-data-public.ikibanasearchresponse.md) | | -| [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) | | -| [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) | | +| [IndexPatternAttributes](./kibana-plugin-plugins-data-public.indexpatternattributes.md) | Interface for an index pattern saved object | +| [IndexPatternSpec](./kibana-plugin-plugins-data-public.indexpatternspec.md) | Static index pattern format Serialized data object, representing index pattern attributes and state | | [IndexPatternTypeMeta](./kibana-plugin-plugins-data-public.indexpatterntypemeta.md) | | | [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) | | | [ISearchSetup](./kibana-plugin-plugins-data-public.isearchsetup.md) | The setup contract exposed by the Search plugin exposes the search strategy extension point. | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md index 22dc92c27567..4b3c915b49c2 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md @@ -35,6 +35,17 @@ search: { siblingPipelineType: string; termsAggFilter: string[]; toAbsoluteDates: typeof toAbsoluteDates; + boundsDescendingRaw: ({ + bound: number; + interval: import("moment").Duration; + boundLabel: string; + intervalLabel: string; + } | { + bound: import("moment").Duration; + interval: import("moment").Duration; + boundLabel: string; + intervalLabel: string; + })[]; }; getRequestInspectorStats: typeof getRequestInspectorStats; getResponseInspectorStats: typeof getResponseInspectorStats; diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md index 3bc2a2054177..496e1ae9677d 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md @@ -15,13 +15,13 @@ Using `createSearchSource`, the instance can be re-created. ```typescript serialize(): { searchSourceJSON: string; - references: import("src/core/server").SavedObjectReference[]; + references: import("../../../../../core/types").SavedObjectReference[]; }; ``` Returns: `{ searchSourceJSON: string; - references: import("src/core/server").SavedObjectReference[]; + references: import("../../../../../core/types").SavedObjectReference[]; }` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.dataapirequesthandlercontext.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.dataapirequesthandlercontext.md new file mode 100644 index 000000000000..8b7b025d8018 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.dataapirequesthandlercontext.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [DataApiRequestHandlerContext](./kibana-plugin-plugins-data-server.dataapirequesthandlercontext.md) + +## DataApiRequestHandlerContext interface + +Signature: + +```typescript +export interface DataApiRequestHandlerContext extends ISearchClient +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [session](./kibana-plugin-plugins-data-server.dataapirequesthandlercontext.session.md) | IScopedSessionService | | + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.dataapirequesthandlercontext.session.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.dataapirequesthandlercontext.session.md new file mode 100644 index 000000000000..9a6e3f55d392 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.dataapirequesthandlercontext.session.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [DataApiRequestHandlerContext](./kibana-plugin-plugins-data-server.dataapirequesthandlercontext.md) > [session](./kibana-plugin-plugins-data-server.dataapirequesthandlercontext.session.md) + +## DataApiRequestHandlerContext.session property + +Signature: + +```typescript +session: IScopedSessionService; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md new file mode 100644 index 000000000000..ebd7f46d3598 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [addRuntimeField](./kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md) + +## IndexPattern.addRuntimeField() method + +Add a runtime field - Appended to existing mapped field or a new field is created as appropriate + +Signature: + +```typescript +addRuntimeField(name: string, runtimeField: RuntimeField): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | +| runtimeField | RuntimeField | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.fieldattrs.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.fieldattrs.md deleted file mode 100644 index c8bad55dee2e..000000000000 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.fieldattrs.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [fieldAttrs](./kibana-plugin-plugins-data-server.indexpattern.fieldattrs.md) - -## IndexPattern.fieldAttrs property - -Signature: - -```typescript -fieldAttrs: FieldAttrs; -``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md index 7d70af4b535f..668d563ff04c 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md @@ -20,6 +20,7 @@ getAsSavedObjectBody(): { type: string | undefined; typeMeta: string | undefined; allowNoIndex: true | undefined; + runtimeFieldMap: string | undefined; }; ``` Returns: @@ -35,5 +36,6 @@ getAsSavedObjectBody(): { type: string | undefined; typeMeta: string | undefined; allowNoIndex: true | undefined; + runtimeFieldMap: string | undefined; }` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md index eab6ae9bf903..0030adf1261e 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.getcomputedfields.md @@ -14,6 +14,7 @@ getComputedFields(): { field: any; format: string; }[]; + runtimeFields: Record; }; ``` Returns: @@ -25,5 +26,6 @@ getComputedFields(): { field: any; format: string; }[]; + runtimeFields: Record; }` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.intervalname.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.intervalname.md index caaa6929235f..c14452007579 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.intervalname.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.intervalname.md @@ -4,6 +4,11 @@ ## IndexPattern.intervalName property +> Warning: This API is now obsolete. +> +> Deprecated. used by time range index patterns +> + Signature: ```typescript diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md index 54f020e57cf4..97d1cd911526 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.md @@ -22,7 +22,6 @@ export declare class IndexPattern implements IIndexPattern | --- | --- | --- | --- | | [allowNoIndex](./kibana-plugin-plugins-data-server.indexpattern.allownoindex.md) | | boolean | prevents errors when index pattern exists before indices | | [deleteFieldFormat](./kibana-plugin-plugins-data-server.indexpattern.deletefieldformat.md) | | (fieldName: string) => void | | -| [fieldAttrs](./kibana-plugin-plugins-data-server.indexpattern.fieldattrs.md) | | FieldAttrs | | | [fieldFormatMap](./kibana-plugin-plugins-data-server.indexpattern.fieldformatmap.md) | | Record<string, any> | | | [fields](./kibana-plugin-plugins-data-server.indexpattern.fields.md) | | IIndexPatternFieldList & {
toSpec: () => IndexPatternFieldMap;
} | | | [flattenHit](./kibana-plugin-plugins-data-server.indexpattern.flattenhit.md) | | (hit: Record<string, any>, deep?: boolean) => Record<string, any> | | @@ -38,14 +37,15 @@ export declare class IndexPattern implements IIndexPattern | [sourceFilters](./kibana-plugin-plugins-data-server.indexpattern.sourcefilters.md) | | SourceFilter[] | | | [timeFieldName](./kibana-plugin-plugins-data-server.indexpattern.timefieldname.md) | | string | undefined | | | [title](./kibana-plugin-plugins-data-server.indexpattern.title.md) | | string | | -| [type](./kibana-plugin-plugins-data-server.indexpattern.type.md) | | string | undefined | | -| [typeMeta](./kibana-plugin-plugins-data-server.indexpattern.typemeta.md) | | TypeMeta | | -| [version](./kibana-plugin-plugins-data-server.indexpattern.version.md) | | string | undefined | | +| [type](./kibana-plugin-plugins-data-server.indexpattern.type.md) | | string | undefined | Type is used to identify rollup index patterns | +| [typeMeta](./kibana-plugin-plugins-data-server.indexpattern.typemeta.md) | | TypeMeta | Only used by rollup indices, used by rollup specific endpoint to load field list | +| [version](./kibana-plugin-plugins-data-server.indexpattern.version.md) | | string | undefined | SavedObject version | ## Methods | Method | Modifiers | Description | | --- | --- | --- | +| [addRuntimeField(name, runtimeField)](./kibana-plugin-plugins-data-server.indexpattern.addruntimefield.md) | | Add a runtime field - Appended to existing mapped field or a new field is created as appropriate | | [addScriptedField(name, script, fieldType)](./kibana-plugin-plugins-data-server.indexpattern.addscriptedfield.md) | | Add scripted field to field list | | [getAggregationRestrictions()](./kibana-plugin-plugins-data-server.indexpattern.getaggregationrestrictions.md) | | | | [getAsSavedObjectBody()](./kibana-plugin-plugins-data-server.indexpattern.getassavedobjectbody.md) | | Returns index pattern as saved object body for saving | @@ -59,9 +59,10 @@ export declare class IndexPattern implements IIndexPattern | [getTimeField()](./kibana-plugin-plugins-data-server.indexpattern.gettimefield.md) | | | | [isTimeBased()](./kibana-plugin-plugins-data-server.indexpattern.istimebased.md) | | | | [isTimeNanosBased()](./kibana-plugin-plugins-data-server.indexpattern.istimenanosbased.md) | | | +| [removeRuntimeField(name)](./kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md) | | Remove a runtime field - removed from mapped field or removed unmapped field as appropriate | | [removeScriptedField(fieldName)](./kibana-plugin-plugins-data-server.indexpattern.removescriptedfield.md) | | Remove scripted field from field list | | [setFieldAttrs(fieldName, attrName, value)](./kibana-plugin-plugins-data-server.indexpattern.setfieldattrs.md) | | | | [setFieldCount(fieldName, count)](./kibana-plugin-plugins-data-server.indexpattern.setfieldcount.md) | | | | [setFieldCustomLabel(fieldName, customLabel)](./kibana-plugin-plugins-data-server.indexpattern.setfieldcustomlabel.md) | | | -| [toSpec()](./kibana-plugin-plugins-data-server.indexpattern.tospec.md) | | | +| [toSpec()](./kibana-plugin-plugins-data-server.indexpattern.tospec.md) | | Create static representation of index pattern | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md new file mode 100644 index 000000000000..da8e7e40a7fa --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPattern](./kibana-plugin-plugins-data-server.indexpattern.md) > [removeRuntimeField](./kibana-plugin-plugins-data-server.indexpattern.removeruntimefield.md) + +## IndexPattern.removeRuntimeField() method + +Remove a runtime field - removed from mapped field or removed unmapped field as appropriate + +Signature: + +```typescript +removeRuntimeField(name: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | | + +Returns: + +`void` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.tospec.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.tospec.md index 5d76b8f00853..7c3c392cf6df 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.tospec.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.tospec.md @@ -4,6 +4,8 @@ ## IndexPattern.toSpec() method +Create static representation of index pattern + Signature: ```typescript diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.type.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.type.md index 01154ab5444d..cc64e413ef4c 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.type.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.type.md @@ -4,6 +4,8 @@ ## IndexPattern.type property +Type is used to identify rollup index patterns + Signature: ```typescript diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.typemeta.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.typemeta.md index b16bcec404d9..b759900a186c 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.typemeta.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.typemeta.md @@ -4,6 +4,8 @@ ## IndexPattern.typeMeta property +Only used by rollup indices, used by rollup specific endpoint to load field list + Signature: ```typescript diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.version.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.version.md index e4297d838911..583a0c5ab6c5 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.version.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpattern.version.md @@ -4,6 +4,8 @@ ## IndexPattern.version property +SavedObject version + Signature: ```typescript diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md index b9b9f955c7ab..20af97ecc876 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.md @@ -4,6 +4,8 @@ ## IndexPatternAttributes interface +Interface for an index pattern saved object + Signature: ```typescript @@ -19,6 +21,7 @@ export interface IndexPatternAttributes | [fieldFormatMap](./kibana-plugin-plugins-data-server.indexpatternattributes.fieldformatmap.md) | string | | | [fields](./kibana-plugin-plugins-data-server.indexpatternattributes.fields.md) | string | | | [intervalName](./kibana-plugin-plugins-data-server.indexpatternattributes.intervalname.md) | string | | +| [runtimeFieldMap](./kibana-plugin-plugins-data-server.indexpatternattributes.runtimefieldmap.md) | string | | | [sourceFilters](./kibana-plugin-plugins-data-server.indexpatternattributes.sourcefilters.md) | string | | | [timeFieldName](./kibana-plugin-plugins-data-server.indexpatternattributes.timefieldname.md) | string | | | [title](./kibana-plugin-plugins-data-server.indexpatternattributes.title.md) | string | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimefieldmap.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimefieldmap.md new file mode 100644 index 000000000000..1e0dff2ad0e4 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.indexpatternattributes.runtimefieldmap.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) > [runtimeFieldMap](./kibana-plugin-plugins-data-server.indexpatternattributes.runtimefieldmap.md) + +## IndexPatternAttributes.runtimeFieldMap property + +Signature: + +```typescript +runtimeFieldMap?: string; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index 1b4cf5585cb3..84c7875c26ce 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -45,13 +45,14 @@ | --- | --- | | [AggFunctionsMapping](./kibana-plugin-plugins-data-server.aggfunctionsmapping.md) | A global list of the expression function definitions for each agg type function. | | [AggParamOption](./kibana-plugin-plugins-data-server.aggparamoption.md) | | +| [DataApiRequestHandlerContext](./kibana-plugin-plugins-data-server.dataapirequesthandlercontext.md) | | | [EsQueryConfig](./kibana-plugin-plugins-data-server.esqueryconfig.md) | | | [FieldDescriptor](./kibana-plugin-plugins-data-server.fielddescriptor.md) | | | [FieldFormatConfig](./kibana-plugin-plugins-data-server.fieldformatconfig.md) | | | [IEsSearchRequest](./kibana-plugin-plugins-data-server.iessearchrequest.md) | | | [IFieldSubType](./kibana-plugin-plugins-data-server.ifieldsubtype.md) | | | [IFieldType](./kibana-plugin-plugins-data-server.ifieldtype.md) | | -| [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) | | +| [IndexPatternAttributes](./kibana-plugin-plugins-data-server.indexpatternattributes.md) | Interface for an index pattern saved object | | [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) | | | [ISearchSetup](./kibana-plugin-plugins-data-server.isearchsetup.md) | | | [ISearchStart](./kibana-plugin-plugins-data-server.isearchstart.md) | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md index 88f85eb7a7d0..af7abb076d7e 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md @@ -9,10 +9,10 @@ ```typescript start(core: CoreStart): { fieldFormats: { - fieldFormatServiceFactory: (uiSettings: import("src/core/server").IUiSettingsClient) => Promise; + fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; }; search: ISearchStart>; }; @@ -28,10 +28,10 @@ start(core: CoreStart): { `{ fieldFormats: { - fieldFormatServiceFactory: (uiSettings: import("src/core/server").IUiSettingsClient) => Promise; + fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; }; search: ISearchStart>; }` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md new file mode 100644 index 000000000000..0fdf36bc719e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) > [ast](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md) + +## ExpressionsInspectorAdapter.ast property + +Signature: + +```typescript +get ast(): any; +``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md new file mode 100644 index 000000000000..671270a5c78c --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) > [logAST](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md) + +## ExpressionsInspectorAdapter.logAST() method + +Signature: + +```typescript +logAST(ast: any): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ast | any | | + +Returns: + +`void` + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md new file mode 100644 index 000000000000..23d542a0f69e --- /dev/null +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) + +## ExpressionsInspectorAdapter class + +Signature: + +```typescript +export declare class ExpressionsInspectorAdapter extends EventEmitter +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [ast](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.ast.md) | | any | | + +## Methods + +| Method | Modifiers | Description | +| --- | --- | --- | +| [logAST(ast)](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.logast.md) | | | + diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md index 1b97c9e11f83..e3eb7a34175e 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md @@ -16,6 +16,7 @@ | [ExpressionRenderer](./kibana-plugin-plugins-expressions-public.expressionrenderer.md) | | | [ExpressionRendererRegistry](./kibana-plugin-plugins-expressions-public.expressionrendererregistry.md) | | | [ExpressionRenderHandler](./kibana-plugin-plugins-expressions-public.expressionrenderhandler.md) | | +| [ExpressionsInspectorAdapter](./kibana-plugin-plugins-expressions-public.expressionsinspectoradapter.md) | | | [ExpressionsPublicPlugin](./kibana-plugin-plugins-expressions-public.expressionspublicplugin.md) | | | [ExpressionsService](./kibana-plugin-plugins-expressions-public.expressionsservice.md) | ExpressionsService class is used for multiple purposes:1. It implements the same Expressions service that can be used on both: (1) server-side and (2) browser-side. 2. It implements the same Expressions service that users can fork/clone, thus have their own instance of the Expressions plugin. 3. ExpressionsService defines the public contracts of \*setup\* and \*start\* Kibana Platform life-cycles for ease-of-use on server-side and browser-side. 4. ExpressionsService creates a bound version of all exported contract functions. 5. Functions are bound the way there are:\`\`\`ts registerFunction = (...args: Parameters<Executor\['registerFunction'\]> ): ReturnType<Executor\['registerFunction'\]> => this.executor.registerFunction(...args); \`\`\`so that JSDoc appears in developers IDE when they use those plugins.expressions.registerFunction(. | | [ExpressionType](./kibana-plugin-plugins-expressions-public.expressiontype.md) | | diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 6dd76f782d66..26f095c59c64 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -205,6 +205,9 @@ the username and password that the {kib} server uses to perform maintenance on the {kib} index at startup. {kib} users still need to authenticate with {es}, which is proxied through the {kib} server. +| `enterpriseSearch.host` + | The URL of your Enterprise Search instance + | `interpreter.enableInVisualize` | Enables use of interpreter in Visualize. *Default: `true`* diff --git a/docs/user/alerting/alert-types.asciidoc b/docs/user/alerting/alert-types.asciidoc index 7de5ff56228c..7c5a957d1cf7 100644 --- a/docs/user/alerting/alert-types.asciidoc +++ b/docs/user/alerting/alert-types.asciidoc @@ -1,6 +1,6 @@ [role="xpack"] [[alert-types]] -== Alert types +== Standard stack alert types {kib} supplies alert types in two ways: some are built into {kib} (these are known as stack alerts), while domain-specific alert types are registered by {kib} apps such as <>, <>, and <>. diff --git a/docs/user/alerting/defining-alerts.asciidoc b/docs/user/alerting/defining-alerts.asciidoc index 94cca7f91494..ed0e2966b228 100644 --- a/docs/user/alerting/defining-alerts.asciidoc +++ b/docs/user/alerting/defining-alerts.asciidoc @@ -59,11 +59,19 @@ Each action type exposes different properties. For example an email action allow [role="screenshot"] image::images/alert-flyout-action-details.png[UI for defining an email action] -Using the https://mustache.github.io/[Mustache] template syntax `{{variable name}}`, you can pass alert values at the time a condition is detected to an action. Note that using two curly braces will escape any HTML. Should you need to preserve HTML, use three curly braces (`{{{`). Available variables differ by alert type, and a list can be accessed using the "add variable" button. +Using the https://mustache.github.io/[Mustache] template syntax `{{variable name}}`, you can pass alert values at the time a condition is detected to an action. Available variables differ by alert type, and the list of available variables can be accessed using the "add variable" button. [role="screenshot"] image::images/alert-flyout-action-variables.png[Passing alert values to an action] +Some cases exist where the variable values will be "escaped", when used in a context where escaping is needed: + +- For the <> connector, the `message` action configuration property escapes any characters that would be interpreted as Markdown. +- For the <> connector, the `message` action configuration property escapes any characters that would be interpreted as Slack Markdown. +- For the <> connector, the `body` action configuration property escapes any characters that are invalid in JSON string values. + +Mustache also supports "triple braces" of the form `{{{variable name}}}`, which indicates no escaping should be done at all. Care should be used when using this form, as it could end up rendering the variable content in such a way as to make the resulting parameter invalid or formatted incorrectly. + You can attach more than one action. Clicking the "Add action" button will prompt you to select another alert type and repeat the above steps again. [role="screenshot"] diff --git a/docs/user/alerting/geo-alert-types.asciidoc b/docs/user/alerting/geo-alert-types.asciidoc new file mode 100644 index 000000000000..c04cf4bca432 --- /dev/null +++ b/docs/user/alerting/geo-alert-types.asciidoc @@ -0,0 +1,127 @@ +[role="xpack"] +[[geo-alert-types]] +== Geo alert types + +experimental[] Two additional stack alerts are available: +<> and <>. To enable, +add the following configuration to your `kibana.yml`: + +```yml +xpack.stack_alerts.enableGeoAlerting: true +``` + +As with other stack alerts, you need `all` access to the *Stack Alerts* feature +to be able to create and edit either of the geo alerts. +See <> for more information on configuring roles that provide access to this feature. + +[float] +=== Geo alert requirements + +To create either a *Tracking threshold* or a *Tracking containment* alert, the +following requirements must be present: + +- *Tracks index or index pattern*: An index containing a `geo_point` field, `date` field, +and some form of entity identifier. An entity identifier is a `keyword` or `number` +field that consistently identifies the entity to be tracked. The data in this index should be dynamically +updating so that there are entity movements to alert upon. +- *Boundaries index or index pattern*: An index containing `geo_shape` data, such as boundary data and bounding box data. +This data is presumed to be static (not updating). Shape data matching the query is +harvested once when the alert is created and anytime after when the alert is re-enabled +after disablement. + +By design, current interval entity locations (_current_ is determined by `date` in +the *Tracked index or index pattern*) are queried to determine if they are contained +within any monitored boundaries. Entity +data should be somewhat "real time", meaning the dates of new documents aren’t older +than the current time minus the amount of the interval. If data older than +`now - ` is ingested, it won't trigger an alert. + +[float] +=== Creating a geo alert +Both *threshold* and *containment* alerts can be created by clicking the *Create* +button in the <>. +Complete the <>. +Select <> to generate an alert when an entity crosses a boundary, and you desire the +ability to highlight lines of crossing on a custom map. +Select +<> if an entity should send out constant alerts +while contained within a boundary (this feature is optional) or if the alert is generally +just more focused around activity when an entity exists within a shape. + +[role="screenshot"] +image::images/alert-types-tracking-select.png[Choosing a tracking alert type] + +[NOTE] +================================================== +With recent advances in the alerting framework, most of the features +available in Tracking threshold alerts can be replicated with just +a little more work in Tracking containment alerts. The capabilities of Tracking +threshold alerts may be deprecated or folded into Tracking containment alerts +in the future. +================================================== + +[float] +[[alert-type-tracking-threshold]] +=== Tracking threshold +The Tracking threshold alert type runs an {es} query over indices, comparing the latest +entity locations with their previous locations. In the event that an entity has crossed a +boundary from the selected boundary index, an alert may be generated. + +[float] +==== Defining the conditions +Tracking threshold has a *Delayed evaluation offset* and 4 clauses that define the +condition to detect, as well as 2 Kuery bars used to provide additional filtering +context for each of the indices. + +[role="screenshot"] +image::images/alert-types-tracking-threshold-conditions.png[Five clauses define the condition to detect] + + +Delayed evaluation offset:: If a data source lags or is intermittent, you may supply +an optional value to evaluate alert conditions following a fixed delay. For instance, if data +is consistently indexed 5-10 minutes following its original timestamp, a *Delayed evaluation +offset* of `10 minutes` would ensure that alertable instances are still captured. +Index (entity):: This clause requires an *index or index pattern*, a *time field* that will be used for the *time window*, and a *`geo_point` field* for tracking. +By:: This clause specifies the field to use in the previously provided +*index or index pattern* for tracking Entities. An entity is a `keyword` +or `number` field that consistently identifies the entity to be tracked. +When entity:: This clause specifies which crossing option to track. The values +*Entered*, *Exited*, and *Crossed* can be selected to indicate which crossing conditions +should trigger an alert. *Entered* alerts on entry into a boundary, *Exited* alerts on exit +from a boundary, and *Crossed* alerts on all boundary crossings whether they be entrances +or exits. +Index (Boundary):: This clause requires an *index or index pattern*, a *`geo_shape` field* +identifying boundaries, and an optional *Human-readable boundary name* for better alerting +messages. + +[float] +[[alert-type-tracking-containment]] +=== Tracking containment +The Tracking containment alert type runs an {es} query over indices, determining if any +documents are currently contained within any boundaries from the specified boundary index. +In the event that an entity is contained within a boundary, an alert may be generated. + +[float] +==== Defining the conditions +Tracking containment alerts have 3 clauses that define the condition to detect, +as well as 2 Kuery bars used to provide additional filtering context for each of the indices. + +[role="screenshot"] +image::images/alert-types-tracking-containment-conditions.png[Five clauses define the condition to detect] + +Index (entity):: This clause requires an *index or index pattern*, a *time field* that will be used for the *time window*, and a *`geo_point` field* for tracking. +When entity:: This clause specifies which crossing option to track. The values +*Entered*, *Exited*, and *Crossed* can be selected to indicate which crossing conditions +should trigger an alert. *Entered* alerts on entry into a boundary, *Exited* alerts on exit +from a boundary, and *Crossed* alerts on all boundary crossings whether they be entrances +or exits. +Index (Boundary):: This clause requires an *index or index pattern*, a *`geo_shape` field* +identifying boundaries, and an optional *Human-readable boundary name* for better alerting +messages. + +Conditions for how an alert is tracked can be specified uniquely for each individual action. +An alert can be triggered either when a containment condition is met or when an entity +is no longer contained. + +[role="screenshot"] +image::images/alert-types-tracking-containment-action-options.png[Five clauses define the condition to detect] diff --git a/docs/user/alerting/images/alert-types-tracking-containment-action-options.png b/docs/user/alerting/images/alert-types-tracking-containment-action-options.png new file mode 100644 index 000000000000..c0a045f82738 Binary files /dev/null and b/docs/user/alerting/images/alert-types-tracking-containment-action-options.png differ diff --git a/docs/user/alerting/images/alert-types-tracking-containment-conditions.png b/docs/user/alerting/images/alert-types-tracking-containment-conditions.png new file mode 100644 index 000000000000..32c17d2245d2 Binary files /dev/null and b/docs/user/alerting/images/alert-types-tracking-containment-conditions.png differ diff --git a/docs/user/alerting/images/alert-types-tracking-select.png b/docs/user/alerting/images/alert-types-tracking-select.png new file mode 100644 index 000000000000..445a5202ffd0 Binary files /dev/null and b/docs/user/alerting/images/alert-types-tracking-select.png differ diff --git a/docs/user/alerting/images/alert-types-tracking-threshold-conditions.png b/docs/user/alerting/images/alert-types-tracking-threshold-conditions.png new file mode 100644 index 000000000000..e51ce3e4b855 Binary files /dev/null and b/docs/user/alerting/images/alert-types-tracking-threshold-conditions.png differ diff --git a/docs/user/alerting/index.asciidoc b/docs/user/alerting/index.asciidoc index caef0c6e7332..25e87801f84a 100644 --- a/docs/user/alerting/index.asciidoc +++ b/docs/user/alerting/index.asciidoc @@ -2,4 +2,5 @@ include::alerting-getting-started.asciidoc[] include::defining-alerts.asciidoc[] include::action-types.asciidoc[] include::alert-types.asciidoc[] +include::geo-alert-types.asciidoc[] include::alerting-production-considerations.asciidoc[] diff --git a/docs/user/security/audit-logging.asciidoc b/docs/user/security/audit-logging.asciidoc index acb0f94cf878..12a87b1422c5 100644 --- a/docs/user/security/audit-logging.asciidoc +++ b/docs/user/security/audit-logging.asciidoc @@ -194,6 +194,10 @@ Refer to the corresponding {es} logs for potential write errors. | `success` | User has accessed a saved object. | `failure` | User is not authorized to access a saved object. +.2+| `saved_object_resolve` +| `success` | User has accessed a saved object. +| `failure` | User is not authorized to access a saved object. + .2+| `saved_object_find` | `success` | User has accessed a saved object as part of a search operation. | `failure` | User is not authorized to search for saved objects. diff --git a/examples/expressions_explorer/README.md b/examples/expressions_explorer/README.md new file mode 100644 index 000000000000..ead0ca758f8e --- /dev/null +++ b/examples/expressions_explorer/README.md @@ -0,0 +1,8 @@ +## expressions explorer + +This example expressions explorer app shows how to: + - to run expression + - to render expression output + - emit events from expression renderer and handle them + +To run this example, use the command `yarn start --run-examples`. \ No newline at end of file diff --git a/examples/expressions_explorer/kibana.json b/examples/expressions_explorer/kibana.json new file mode 100644 index 000000000000..038b7eea0ef2 --- /dev/null +++ b/examples/expressions_explorer/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "expressionsExplorer", + "version": "0.0.1", + "kibanaVersion": "kibana", + "server": false, + "ui": true, + "requiredPlugins": ["expressions", "inspector", "uiActions", "developerExamples"], + "optionalPlugins": [], + "requiredBundles": [] +} diff --git a/examples/expressions_explorer/public/actions/navigate_action.ts b/examples/expressions_explorer/public/actions/navigate_action.ts new file mode 100644 index 000000000000..d29a9e6b345b --- /dev/null +++ b/examples/expressions_explorer/public/actions/navigate_action.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { createAction } from '../../../../src/plugins/ui_actions/public'; + +export const ACTION_NAVIGATE = 'ACTION_NAVIGATE'; + +export const createNavigateAction = () => + createAction({ + id: ACTION_NAVIGATE, + type: ACTION_NAVIGATE, + getDisplayName: () => 'Navigate', + execute: async (event: any) => { + window.location.href = event.href; + }, + }); diff --git a/examples/expressions_explorer/public/actions/navigate_trigger.ts b/examples/expressions_explorer/public/actions/navigate_trigger.ts new file mode 100644 index 000000000000..eacbd968eaa9 --- /dev/null +++ b/examples/expressions_explorer/public/actions/navigate_trigger.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { Trigger } from '../../../../src/plugins/ui_actions/public'; + +export const NAVIGATE_TRIGGER_ID = 'NAVIGATE_TRIGGER_ID'; + +export const navigateTrigger: Trigger = { + id: NAVIGATE_TRIGGER_ID, +}; diff --git a/examples/expressions_explorer/public/actions_and_expressions.tsx b/examples/expressions_explorer/public/actions_and_expressions.tsx new file mode 100644 index 000000000000..6e2eebcde4a0 --- /dev/null +++ b/examples/expressions_explorer/public/actions_and_expressions.tsx @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { useState } from 'react'; +import { + EuiFlexItem, + EuiFlexGroup, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageHeader, + EuiPageHeaderSection, + EuiPanel, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { + ExpressionsStart, + ReactExpressionRenderer, + ExpressionsInspectorAdapter, +} from '../../../src/plugins/expressions/public'; +import { ExpressionEditor } from './editor/expression_editor'; +import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; +import { NAVIGATE_TRIGGER_ID } from './actions/navigate_trigger'; + +interface Props { + expressions: ExpressionsStart; + actions: UiActionsStart; +} + +export function ActionsExpressionsExample({ expressions, actions }: Props) { + const [expression, updateExpression] = useState( + 'button name="click me" href="http://www.google.com"' + ); + + const expressionChanged = (value: string) => { + updateExpression(value); + }; + + const inspectorAdapters = { + expression: new ExpressionsInspectorAdapter(), + }; + + const handleEvents = (event: any) => { + if (event.id !== 'NAVIGATE') return; + // enrich event context with some extra data + event.baseUrl = 'http://www.google.com'; + + actions.executeTriggerActions(NAVIGATE_TRIGGER_ID, event.value); + }; + + return ( + + + + +

Actions from expression renderers

+ + + + + + + + + Here you can play with sample `button` which takes a url as configuration and + displays a button which emits custom BUTTON_CLICK trigger to which we have attached + a custom action which performs the navigation. + + + + + + + + + + + + + { + return
{message}
; + }} + /> +
+
+
+
+
+ + ); +} diff --git a/examples/expressions_explorer/public/app.tsx b/examples/expressions_explorer/public/app.tsx new file mode 100644 index 000000000000..d72cf08128a5 --- /dev/null +++ b/examples/expressions_explorer/public/app.tsx @@ -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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { + EuiPage, + EuiPageHeader, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiSpacer, + EuiText, + EuiLink, +} from '@elastic/eui'; +import { AppMountParameters } from '../../../src/core/public'; +import { ExpressionsStart } from '../../../src/plugins/expressions/public'; +import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; +import { RunExpressionsExample } from './run_expressions'; +import { RenderExpressionsExample } from './render_expressions'; +import { ActionsExpressionsExample } from './actions_and_expressions'; +import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; + +interface Props { + expressions: ExpressionsStart; + inspector: InspectorStart; + actions: UiActionsStart; +} + +const ExpressionsExplorer = ({ expressions, inspector, actions }: Props) => { + return ( + + + Expressions Explorer + + + +

+ There are a couple of ways to run the expressions. Below some of the options are + demonstrated. You can read more about it{' '} + + here + +

+
+ + + + + + + + + + + + +
+
+
+
+ ); +}; + +export const renderApp = (props: Props, { element }: AppMountParameters) => { + ReactDOM.render(, element); + + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/examples/expressions_explorer/public/editor/expression_editor.tsx b/examples/expressions_explorer/public/editor/expression_editor.tsx new file mode 100644 index 000000000000..e3dbb5998b92 --- /dev/null +++ b/examples/expressions_explorer/public/editor/expression_editor.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React from 'react'; +import { EuiCodeEditor } from '@elastic/eui'; + +interface Props { + value: string; + onChange: (value: string) => void; +} + +export function ExpressionEditor({ value, onChange }: Props) { + return ( + {}} + aria-label="Code Editor" + /> + ); +} diff --git a/examples/expressions_explorer/public/functions/button.ts b/examples/expressions_explorer/public/functions/button.ts new file mode 100644 index 000000000000..8c39aa2743b3 --- /dev/null +++ b/examples/expressions_explorer/public/functions/button.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from '../../../../src/plugins/expressions/common'; + +interface Arguments { + href: string; + name: string; +} + +export type ExpressionFunctionButton = ExpressionFunctionDefinition< + 'button', + unknown, + Arguments, + unknown +>; + +export const buttonFn: ExpressionFunctionButton = { + name: 'button', + args: { + href: { + help: i18n.translate('expressions.functions.font.args.href', { + defaultMessage: 'Link to which to navigate', + }), + types: ['string'], + required: true, + }, + name: { + help: i18n.translate('expressions.functions.font.args.name', { + defaultMessage: 'Name of the button', + }), + types: ['string'], + default: 'button', + }, + }, + help: 'Configures the button', + fn: (input: unknown, args: Arguments) => { + return { + type: 'render', + as: 'button', + value: args, + }; + }, +}; diff --git a/examples/state_containers_examples/common/index.ts b/examples/expressions_explorer/public/index.ts similarity index 70% rename from examples/state_containers_examples/common/index.ts rename to examples/expressions_explorer/public/index.ts index 0d0bc48fca45..a6dbbc9198f4 100644 --- a/examples/state_containers_examples/common/index.ts +++ b/examples/expressions_explorer/public/index.ts @@ -6,5 +6,6 @@ * Public License, v 1. */ -export const PLUGIN_ID = 'stateContainersExampleWithDataServices'; -export const PLUGIN_NAME = 'State containers example - with data services'; +import { ExpressionsExplorerPlugin } from './plugin'; + +export const plugin = () => new ExpressionsExplorerPlugin(); diff --git a/examples/expressions_explorer/public/inspector/ast_debug_view.tsx b/examples/expressions_explorer/public/inspector/ast_debug_view.tsx new file mode 100644 index 000000000000..d860ff30bd8e --- /dev/null +++ b/examples/expressions_explorer/public/inspector/ast_debug_view.tsx @@ -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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { useState } from 'react'; +import { EuiTreeView, EuiDescriptionList, EuiCodeBlock, EuiText, EuiSpacer } from '@elastic/eui'; + +interface Props { + ast: any; +} + +const decorateAst = (ast: any, nodeClicked: any) => { + return ast.chain.map((link: any) => { + return { + id: link.function + Math.random(), + label: link.function, + callback: () => { + nodeClicked(link.debug); + }, + children: Object.keys(link.arguments).reduce((result: any, key: string) => { + if (typeof link.arguments[key] === 'object') { + // result[key] = decorateAst(link.arguments[key]); + } + return result; + }, []), + }; + }); +}; + +const prepareNode = (key: string, value: any) => { + if (key === 'args') { + return ( + + {JSON.stringify(value, null, '\t')} + + ); + } else if (key === 'output' || key === 'input') { + return ( + + {JSON.stringify(value, null, '\t')} + + ); + } else if (key === 'success') { + return value ? 'true' : 'false'; + } else return {value}; +}; + +export function AstDebugView({ ast }: Props) { + const [nodeInfo, setNodeInfo] = useState([] as any[]); + const items = decorateAst(ast, (node: any) => { + setNodeInfo( + Object.keys(node).map((key) => ({ + title: key, + description: prepareNode(key, node[key]), + })) + ); + }); + + return ( +
+ List of executed expression functions: + + + Details of selected function: + +
+ ); +} diff --git a/examples/expressions_explorer/public/inspector/expressions_inspector_view.tsx b/examples/expressions_explorer/public/inspector/expressions_inspector_view.tsx new file mode 100644 index 000000000000..1233735072d0 --- /dev/null +++ b/examples/expressions_explorer/public/inspector/expressions_inspector_view.tsx @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { InspectorViewProps, Adapters } from '../../../../src/plugins/inspector/public'; +import { AstDebugView } from './ast_debug_view'; + +interface ExpressionsInspectorViewComponentState { + ast: any; + adapters: Adapters; +} + +class ExpressionsInspectorViewComponent extends Component< + InspectorViewProps, + ExpressionsInspectorViewComponentState +> { + static propTypes = { + adapters: PropTypes.object.isRequired, + title: PropTypes.string.isRequired, + }; + + state = {} as ExpressionsInspectorViewComponentState; + + static getDerivedStateFromProps( + nextProps: Readonly, + state: ExpressionsInspectorViewComponentState + ) { + if (state && nextProps.adapters === state.adapters) { + return null; + } + + const { ast } = nextProps.adapters.expression; + + return { + adapters: nextProps.adapters, + ast, + }; + } + + onUpdateData = (ast: any) => { + this.setState({ + ast, + }); + }; + + componentDidMount() { + this.props.adapters.expression!.on('change', this.onUpdateData); + } + + componentWillUnmount() { + this.props.adapters.expression!.removeListener('change', this.onUpdateData); + } + + static renderNoData() { + return ( + + + + } + body={ + +

+ +

+
+ } + /> + ); + } + + render() { + if (!this.state.ast) { + return ExpressionsInspectorViewComponent.renderNoData(); + } + + return ; + } +} + +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export default ExpressionsInspectorViewComponent; diff --git a/examples/expressions_explorer/public/inspector/expressions_inspector_view_wrapper.tsx b/examples/expressions_explorer/public/inspector/expressions_inspector_view_wrapper.tsx new file mode 100644 index 000000000000..b10c82e5df30 --- /dev/null +++ b/examples/expressions_explorer/public/inspector/expressions_inspector_view_wrapper.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { lazy } from 'react'; + +const ExpressionsInspectorViewComponent = lazy(() => import('./expressions_inspector_view')); + +export const getExpressionsInspectorViewComponentWrapper = () => { + return (props: any) => { + return ; + }; +}; diff --git a/examples/expressions_explorer/public/inspector/index.ts b/examples/expressions_explorer/public/inspector/index.ts new file mode 100644 index 000000000000..ec87a1240ac7 --- /dev/null +++ b/examples/expressions_explorer/public/inspector/index.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { Adapters, InspectorViewDescription } from '../../../../src/plugins/inspector/public'; +import { getExpressionsInspectorViewComponentWrapper } from './expressions_inspector_view_wrapper'; + +export const getExpressionsInspectorViewDescription = (): InspectorViewDescription => ({ + title: i18n.translate('data.inspector.table.dataTitle', { + defaultMessage: 'Expression', + }), + order: 100, + help: i18n.translate('data.inspector.table..dataDescriptionTooltip', { + defaultMessage: 'View the expression behind the visualization', + }), + shouldShow(adapters: Adapters) { + return Boolean(adapters.expression); + }, + component: getExpressionsInspectorViewComponentWrapper(), +}); diff --git a/examples/expressions_explorer/public/plugin.tsx b/examples/expressions_explorer/public/plugin.tsx new file mode 100644 index 000000000000..9643389ad881 --- /dev/null +++ b/examples/expressions_explorer/public/plugin.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { Plugin, CoreSetup, AppMountParameters, AppNavLinkStatus } from '../../../src/core/public'; +import { DeveloperExamplesSetup } from '../../developer_examples/public'; +import { ExpressionsSetup, ExpressionsStart } from '../../../src/plugins/expressions/public'; +import { + Setup as InspectorSetup, + Start as InspectorStart, +} from '../../../src/plugins/inspector/public'; +import { getExpressionsInspectorViewDescription } from './inspector'; +import { UiActionsStart, UiActionsSetup } from '../../../src/plugins/ui_actions/public'; +import { NAVIGATE_TRIGGER_ID, navigateTrigger } from './actions/navigate_trigger'; +import { ACTION_NAVIGATE, createNavigateAction } from './actions/navigate_action'; +import { buttonRenderer } from './renderers/button'; +import { buttonFn } from './functions/button'; + +interface StartDeps { + expressions: ExpressionsStart; + inspector: InspectorStart; + uiActions: UiActionsStart; +} + +interface SetupDeps { + uiActions: UiActionsSetup; + expressions: ExpressionsSetup; + inspector: InspectorSetup; + developerExamples: DeveloperExamplesSetup; +} + +export class ExpressionsExplorerPlugin implements Plugin { + public setup(core: CoreSetup, deps: SetupDeps) { + // register custom inspector adapter & view + deps.inspector.registerView(getExpressionsInspectorViewDescription()); + + // register custom actions + deps.uiActions.registerTrigger(navigateTrigger); + deps.uiActions.registerAction(createNavigateAction()); + deps.uiActions.attachAction(NAVIGATE_TRIGGER_ID, ACTION_NAVIGATE); + + // register custom functions and renderers + deps.expressions.registerRenderer(buttonRenderer); + deps.expressions.registerFunction(buttonFn); + + core.application.register({ + id: 'expressionsExplorer', + title: 'Expressions Explorer', + navLinkStatus: AppNavLinkStatus.hidden, + async mount(params: AppMountParameters) { + const [, depsStart] = await core.getStartServices(); + const { renderApp } = await import('./app'); + return renderApp( + { + expressions: depsStart.expressions, + inspector: depsStart.inspector, + actions: depsStart.uiActions, + }, + params + ); + }, + }); + + deps.developerExamples.register({ + appId: 'expressionsExplorer', + title: 'Expressions', + description: `Expressions is a plugin that allows to execute Kibana expressions and render content using expression renderers. This example plugin showcases various usage scenarios.`, + links: [ + { + label: 'README', + href: 'https://github.com/elastic/kibana/blob/master/src/plugins/expressions/README.md', + iconType: 'logoGithub', + size: 's', + target: '_blank', + }, + ], + }); + } + + public start() {} + + public stop() {} +} diff --git a/examples/expressions_explorer/public/render_expressions.tsx b/examples/expressions_explorer/public/render_expressions.tsx new file mode 100644 index 000000000000..ffbe558f3021 --- /dev/null +++ b/examples/expressions_explorer/public/render_expressions.tsx @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { useState } from 'react'; +import { + EuiFlexItem, + EuiFlexGroup, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageHeader, + EuiPageHeaderSection, + EuiPanel, + EuiText, + EuiTitle, + EuiButton, +} from '@elastic/eui'; +import { + ExpressionsStart, + ReactExpressionRenderer, + ExpressionsInspectorAdapter, +} from '../../../src/plugins/expressions/public'; +import { ExpressionEditor } from './editor/expression_editor'; +import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; + +interface Props { + expressions: ExpressionsStart; + inspector: InspectorStart; +} + +export function RenderExpressionsExample({ expressions, inspector }: Props) { + const [expression, updateExpression] = useState('markdown "## expressions explorer rendering"'); + + const expressionChanged = (value: string) => { + updateExpression(value); + }; + + const inspectorAdapters = { + expression: new ExpressionsInspectorAdapter(), + }; + + return ( + + + + +

Render expressions

+
+
+
+ + + + + + In the below editor you can enter your expression and render it. Using + ReactExpressionRenderer component makes that very easy. + + + + { + inspector.open(inspectorAdapters); + }} + > + Open Inspector + + + + + + + + + + + + + { + return
{message}
; + }} + /> +
+
+
+
+
+
+ ); +} diff --git a/examples/expressions_explorer/public/renderers/button.tsx b/examples/expressions_explorer/public/renderers/button.tsx new file mode 100644 index 000000000000..32f1f31894dc --- /dev/null +++ b/examples/expressions_explorer/public/renderers/button.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import ReactDOM from 'react-dom'; +import React from 'react'; +import { EuiButton } from '@elastic/eui'; +import { ExpressionRenderDefinition } from '../../../../src/plugins/expressions/common/expression_renderers'; + +export const buttonRenderer: ExpressionRenderDefinition = { + name: 'button', + displayName: 'Button', + reuseDomNode: true, + render(domNode, config, handlers) { + const buttonClick = () => { + handlers.event({ + id: 'NAVIGATE', + value: { + href: config.href, + }, + }); + }; + + const renderDebug = () => ( +
+ + {config.name} + +
+ ); + + ReactDOM.render(renderDebug(), domNode, () => handlers.done()); + + handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); + }, +}; diff --git a/examples/expressions_explorer/public/run_expressions.tsx b/examples/expressions_explorer/public/run_expressions.tsx new file mode 100644 index 000000000000..efbdbc2d4183 --- /dev/null +++ b/examples/expressions_explorer/public/run_expressions.tsx @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { useState, useEffect, useMemo } from 'react'; +import { + EuiCodeBlock, + EuiFlexItem, + EuiFlexGroup, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageHeader, + EuiPageHeaderSection, + EuiPanel, + EuiText, + EuiTitle, + EuiButton, +} from '@elastic/eui'; +import { + ExpressionsStart, + ExpressionsInspectorAdapter, +} from '../../../src/plugins/expressions/public'; +import { ExpressionEditor } from './editor/expression_editor'; +import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; + +interface Props { + expressions: ExpressionsStart; + inspector: InspectorStart; +} + +export function RunExpressionsExample({ expressions, inspector }: Props) { + const [expression, updateExpression] = useState('markdown "## expressions explorer"'); + const [result, updateResult] = useState({}); + + const expressionChanged = (value: string) => { + updateExpression(value); + }; + + const inspectorAdapters = useMemo( + () => ({ + expression: new ExpressionsInspectorAdapter(), + }), + [] + ); + + useEffect(() => { + const runExpression = async () => { + const execution = expressions.execute(expression, null, { + debug: true, + inspectorAdapters, + }); + + const data: any = await execution.getData(); + updateResult(data); + }; + + runExpression(); + }, [expression, expressions, inspectorAdapters]); + + return ( + + + + +

Run expressions

+
+
+
+ + + + + + In the below editor you can enter your expression and execute it. Using + expressions.execute allows you to easily run the expression. + + + + { + inspector.open(inspectorAdapters); + }} + > + Open Inspector + + + + + + + + + + + + + + {JSON.stringify(result, null, '\t')} + + + + + + +
+ ); +} diff --git a/examples/expressions_explorer/tsconfig.json b/examples/expressions_explorer/tsconfig.json new file mode 100644 index 000000000000..b4449819b25a --- /dev/null +++ b/examples/expressions_explorer/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "../../typings/**/*", + ], + "exclude": [], + "references": [ + { "path": "../../src/core/tsconfig.json" }, + { "path": "../../src/plugins/kibana_react/tsconfig.json" }, + ] +} diff --git a/examples/search_examples/server/plugin.ts b/examples/search_examples/server/plugin.ts index 605d4b0ec829..e7ee311c8d65 100644 --- a/examples/search_examples/server/plugin.ts +++ b/examples/search_examples/server/plugin.ts @@ -6,13 +6,16 @@ * Public License, v 1. */ -import { +import type { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger, -} from '../../../src/core/server'; + RequestHandlerContext, +} from 'src/core/server'; + +import type { DataApiRequestHandlerContext } from 'src/plugins/data/server'; import { SearchExamplesPluginSetup, @@ -42,12 +45,14 @@ export class SearchExamplesPlugin deps: SearchExamplesPluginSetupDeps ) { this.logger.debug('search_examples: Setup'); - const router = core.http.createRouter(); + const router = core.http.createRouter< + RequestHandlerContext & { search: DataApiRequestHandlerContext } + >(); core.getStartServices().then(([_, depsStart]) => { const myStrategy = mySearchStrategyProvider(depsStart.data); deps.data.search.registerSearchStrategy('myStrategy', myStrategy); - registerRoutes(router, depsStart.data); + registerRoutes(router); }); return {}; diff --git a/examples/search_examples/server/routes/register_routes.ts b/examples/search_examples/server/routes/register_routes.ts index 87d2e9613773..d7a18509b9a7 100644 --- a/examples/search_examples/server/routes/register_routes.ts +++ b/examples/search_examples/server/routes/register_routes.ts @@ -6,10 +6,12 @@ * Public License, v 1. */ -import { IRouter } from 'kibana/server'; -import { PluginStart as DataPluginStart } from 'src/plugins/data/server'; +import type { IRouter, RequestHandlerContext } from 'kibana/server'; +import { DataApiRequestHandlerContext } from 'src/plugins/data/server'; import { registerServerSearchRoute } from './server_search_route'; -export function registerRoutes(router: IRouter, data: DataPluginStart) { - registerServerSearchRoute(router, data); +export function registerRoutes( + router: IRouter +) { + registerServerSearchRoute(router); } diff --git a/examples/search_examples/server/routes/server_search_route.ts b/examples/search_examples/server/routes/server_search_route.ts index c16ba55b8abe..99a1aba99d8e 100644 --- a/examples/search_examples/server/routes/server_search_route.ts +++ b/examples/search_examples/server/routes/server_search_route.ts @@ -6,13 +6,16 @@ * Public License, v 1. */ -import { PluginStart as DataPluginStart, IEsSearchRequest } from 'src/plugins/data/server'; +import { IEsSearchRequest } from 'src/plugins/data/server'; import { schema } from '@kbn/config-schema'; import { IEsSearchResponse } from 'src/plugins/data/common'; -import { IRouter } from '../../../../src/core/server'; +import type { DataApiRequestHandlerContext } from 'src/plugins/data/server'; +import type { IRouter, RequestHandlerContext } from 'src/core/server'; import { SERVER_SEARCH_ROUTE_PATH } from '../../common'; -export function registerServerSearchRoute(router: IRouter, data: DataPluginStart) { +export function registerServerSearchRoute( + router: IRouter +) { router.get( { path: SERVER_SEARCH_ROUTE_PATH, diff --git a/examples/state_containers_examples/README.md b/examples/state_containers_examples/README.md index c4c6642789bd..015959a2f781 100644 --- a/examples/state_containers_examples/README.md +++ b/examples/state_containers_examples/README.md @@ -2,7 +2,7 @@ This example app shows how to: - Use state containers to manage your application state - - Integrate with browser history and hash history routing + - Integrate with browser history or hash history routing - Sync your state container with the URL To run this example, use the command `yarn start --run-examples`. diff --git a/examples/state_containers_examples/kibana.json b/examples/state_containers_examples/kibana.json index 58346af8f1d1..0f0a3a805ecb 100644 --- a/examples/state_containers_examples/kibana.json +++ b/examples/state_containers_examples/kibana.json @@ -2,9 +2,9 @@ "id": "stateContainersExamples", "version": "0.0.1", "kibanaVersion": "kibana", - "server": true, + "server": false, "ui": true, "requiredPlugins": ["navigation", "data", "developerExamples"], "optionalPlugins": [], - "requiredBundles": ["kibanaUtils", "kibanaReact"] + "requiredBundles": ["kibanaUtils"] } diff --git a/examples/state_containers_examples/public/common/example_page.tsx b/examples/state_containers_examples/public/common/example_page.tsx new file mode 100644 index 000000000000..203b226158d0 --- /dev/null +++ b/examples/state_containers_examples/public/common/example_page.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React, { PropsWithChildren } from 'react'; +import { EuiPage, EuiPageSideBar, EuiSideNav } from '@elastic/eui'; +import { CoreStart } from '../../../../src/core/public'; + +export interface ExampleLink { + title: string; + appId: string; +} + +interface NavProps { + navigateToApp: CoreStart['application']['navigateToApp']; + exampleLinks: ExampleLink[]; +} + +const SideNav: React.FC = ({ navigateToApp, exampleLinks }: NavProps) => { + const navItems = exampleLinks.map((example) => ({ + id: example.appId, + name: example.title, + onClick: () => navigateToApp(example.appId), + 'data-test-subj': example.appId, + })); + + return ( + + ); +}; + +interface Props { + navigateToApp: CoreStart['application']['navigateToApp']; + exampleLinks: ExampleLink[]; +} + +export const StateContainersExamplesPage: React.FC = ({ + navigateToApp, + children, + exampleLinks, +}: PropsWithChildren) => { + return ( + + + + + {children} + + ); +}; diff --git a/examples/state_containers_examples/public/plugin.ts b/examples/state_containers_examples/public/plugin.ts index 752c0935c5dd..a775c3d65fd7 100644 --- a/examples/state_containers_examples/public/plugin.ts +++ b/examples/state_containers_examples/public/plugin.ts @@ -8,8 +8,8 @@ import { AppMountParameters, CoreSetup, Plugin, AppNavLinkStatus } from '../../../src/core/public'; import { AppPluginDependencies } from './with_data_services/types'; -import { PLUGIN_ID, PLUGIN_NAME } from '../common'; import { DeveloperExamplesSetup } from '../../developer_examples/public'; +import image from './state_sync.png'; interface SetupDeps { developerExamples: DeveloperExamplesSetup; @@ -17,97 +17,95 @@ interface SetupDeps { export class StateContainersExamplesPlugin implements Plugin { public setup(core: CoreSetup, { developerExamples }: SetupDeps) { + const examples = { + stateContainersExampleBrowserHistory: { + title: 'Todo App (browser history)', + }, + stateContainersExampleHashHistory: { + title: 'Todo App (hash history)', + }, + stateContainersExampleWithDataServices: { + title: 'Search bar integration', + }, + }; + + const exampleLinks = Object.keys(examples).map((id: string) => ({ + appId: id, + title: examples[id as keyof typeof examples].title, + })); + core.application.register({ id: 'stateContainersExampleBrowserHistory', - title: 'State containers example - browser history routing', + title: examples.stateContainersExampleBrowserHistory.title, navLinkStatus: AppNavLinkStatus.hidden, async mount(params: AppMountParameters) { const { renderApp, History } = await import('./todo/app'); - return renderApp(params, { - appInstanceId: '1', - appTitle: 'Routing with browser history', - historyType: History.Browser, - }); + const [coreStart] = await core.getStartServices(); + return renderApp( + params, + { + appTitle: examples.stateContainersExampleBrowserHistory.title, + historyType: History.Browser, + }, + { navigateToApp: coreStart.application.navigateToApp, exampleLinks } + ); }, }); core.application.register({ id: 'stateContainersExampleHashHistory', - title: 'State containers example - hash history routing', + title: examples.stateContainersExampleHashHistory.title, navLinkStatus: AppNavLinkStatus.hidden, async mount(params: AppMountParameters) { const { renderApp, History } = await import('./todo/app'); - return renderApp(params, { - appInstanceId: '2', - appTitle: 'Routing with hash history', - historyType: History.Hash, - }); + const [coreStart] = await core.getStartServices(); + return renderApp( + params, + { + appTitle: examples.stateContainersExampleHashHistory.title, + historyType: History.Hash, + }, + { navigateToApp: coreStart.application.navigateToApp, exampleLinks } + ); }, }); core.application.register({ - id: PLUGIN_ID, - title: PLUGIN_NAME, + id: 'stateContainersExampleWithDataServices', + title: examples.stateContainersExampleWithDataServices.title, navLinkStatus: AppNavLinkStatus.hidden, async mount(params: AppMountParameters) { - // Load application bundle const { renderApp } = await import('./with_data_services/application'); - // Get start services as specified in kibana.json const [coreStart, depsStart] = await core.getStartServices(); - // Render the application - return renderApp(coreStart, depsStart as AppPluginDependencies, params); + return renderApp(coreStart, depsStart as AppPluginDependencies, params, { exampleLinks }); }, }); developerExamples.register({ - appId: 'stateContainersExampleBrowserHistory', - title: 'State containers using browser history', - description: `An example todo app that uses browser history and state container utilities like createStateContainerReactHelpers, - createStateContainer, createKbnUrlStateStorage, createSessionStorageStateStorage, - syncStates and getStateFromKbnUrl to keep state in sync with the URL. Change some parameters, navigate away and then back, and the - state should be preserved.`, + appId: exampleLinks[0].appId, + title: 'State Management', + description: 'Examples of using state containers and state syncing utils.', + image, links: [ { - label: 'README', + label: 'State containers README', href: - 'https://github.com/elastic/kibana/tree/master/src/plugins/kibana_utils/docs/state_containers/README.md', + 'https://github.com/elastic/kibana/tree/master/src/plugins/kibana_utils/docs/state_containers', iconType: 'logoGithub', size: 's', target: '_blank', }, - ], - }); - - developerExamples.register({ - appId: 'stateContainersExampleHashHistory', - title: 'State containers using hash history', - description: `An example todo app that uses hash history and state container utilities like createStateContainerReactHelpers, - createStateContainer, createKbnUrlStateStorage, createSessionStorageStateStorage, - syncStates and getStateFromKbnUrl to keep state in sync with the URL. Change some parameters, navigate away and then back, and the - state should be preserved.`, - links: [ { - label: 'README', + label: 'State sync utils README', href: - 'https://github.com/elastic/kibana/tree/master/src/plugins/kibana_utils/docs/state_containers/README.md', + 'https://github.com/elastic/kibana/tree/master/src/plugins/kibana_utils/docs/state_sync', iconType: 'logoGithub', size: 's', target: '_blank', }, - ], - }); - - developerExamples.register({ - appId: PLUGIN_ID, - title: 'Sync state from a query bar with the url', - description: `Shows how to use data.syncQueryStateWitUrl in combination with state container utilities from kibana_utils to - show a query bar that stores state in the url and is kept in sync. - `, - links: [ { - label: 'README', - href: - 'https://github.com/elastic/kibana/blob/master/src/plugins/data/public/query/state_sync/README.md', - iconType: 'logoGithub', + label: 'Kibana navigation best practices', + href: 'https://www.elastic.co/guide/en/kibana/master/kibana-navigation.html', + iconType: 'logoKibana', size: 's', target: '_blank', }, diff --git a/examples/state_containers_examples/public/state_sync.png b/examples/state_containers_examples/public/state_sync.png new file mode 100644 index 000000000000..fc8eb0dc10f6 Binary files /dev/null and b/examples/state_containers_examples/public/state_sync.png differ diff --git a/examples/state_containers_examples/public/todo/app.tsx b/examples/state_containers_examples/public/todo/app.tsx index ff4d65009a36..f43ace6acee2 100644 --- a/examples/state_containers_examples/public/todo/app.tsx +++ b/examples/state_containers_examples/public/todo/app.tsx @@ -6,14 +6,14 @@ * Public License, v 1. */ -import { AppMountParameters } from 'kibana/public'; +import { AppMountParameters, CoreStart } from 'kibana/public'; import ReactDOM from 'react-dom'; import React from 'react'; import { createHashHistory } from 'history'; import { TodoAppPage } from './todo'; +import { StateContainersExamplesPage, ExampleLink } from '../common/example_page'; export interface AppOptions { - appInstanceId: string; appTitle: string; historyType: History; } @@ -23,30 +23,21 @@ export enum History { Hash, } +export interface Deps { + navigateToApp: CoreStart['application']['navigateToApp']; + exampleLinks: ExampleLink[]; +} + export const renderApp = ( { appBasePath, element, history: platformHistory }: AppMountParameters, - { appInstanceId, appTitle, historyType }: AppOptions + { appTitle, historyType }: AppOptions, + { navigateToApp, exampleLinks }: Deps ) => { const history = historyType === History.Browser ? platformHistory : createHashHistory(); ReactDOM.render( - { - const stripTrailingSlash = (path: string) => - path.charAt(path.length - 1) === '/' ? path.substr(0, path.length - 1) : path; - const currentAppUrl = stripTrailingSlash(history.createHref(history.location)); - if (historyType === History.Browser) { - // browser history - return currentAppUrl === '' && !history.location.search && !history.location.hash; - } else { - // hashed history - return currentAppUrl === '#' && !history.location.search; - } - }} - />, + + + , element ); diff --git a/examples/state_containers_examples/public/todo/todo.tsx b/examples/state_containers_examples/public/todo/todo.tsx index ba0b7d213f9f..efe45f15c809 100644 --- a/examples/state_containers_examples/public/todo/todo.tsx +++ b/examples/state_containers_examples/public/todo/todo.tsx @@ -6,7 +6,7 @@ * Public License, v 1. */ -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { Link, Route, Router, Switch, useLocation } from 'react-router-dom'; import { History } from 'history'; import { @@ -18,21 +18,21 @@ import { EuiPageContentBody, EuiPageHeader, EuiPageHeaderSection, + EuiSpacer, + EuiText, EuiTitle, } from '@elastic/eui'; import { + BaseState, BaseStateContainer, - INullableBaseStateContainer, createKbnUrlStateStorage, - createSessionStorageStateStorage, createStateContainer, - createStateContainerReactHelpers, - PureTransition, - syncStates, getStateFromKbnUrl, - BaseState, + INullableBaseStateContainer, + StateContainer, + syncState, + useContainerSelector, } from '../../../../src/plugins/kibana_utils/public'; -import { useUrlTracker } from '../../../../src/plugins/kibana_react/public'; import { defaultState, pureTransitions, @@ -40,42 +40,24 @@ import { TodoState, } from '../../../../src/plugins/kibana_utils/demos/state_containers/todomvc'; -interface GlobalState { - text: string; -} -interface GlobalStateAction { - setText: PureTransition; -} -const defaultGlobalState: GlobalState = { text: '' }; -const globalStateContainer = createStateContainer( - defaultGlobalState, - { - setText: (state) => (text) => ({ ...state, text }), - } -); - -const GlobalStateHelpers = createStateContainerReactHelpers(); - -const container = createStateContainer(defaultState, pureTransitions); -const { Provider, connect, useTransitions, useState } = createStateContainerReactHelpers< - typeof container ->(); - interface TodoAppProps { filter: 'completed' | 'not-completed' | null; + stateContainer: StateContainer; } -const TodoApp: React.FC = ({ filter }) => { - const { setText } = GlobalStateHelpers.useTransitions(); - const { text } = GlobalStateHelpers.useState(); - const { edit: editTodo, delete: deleteTodo, add: addTodo } = useTransitions(); - const todos = useState().todos; - const filteredTodos = todos.filter((todo) => { - if (!filter) return true; - if (filter === 'completed') return todo.completed; - if (filter === 'not-completed') return !todo.completed; - return true; - }); +const TodoApp: React.FC = ({ filter, stateContainer }) => { + const { edit: editTodo, delete: deleteTodo, add: addTodo } = stateContainer.transitions; + const todos = useContainerSelector(stateContainer, (state) => state.todos); + const filteredTodos = useMemo( + () => + todos.filter((todo) => { + if (!filter) return true; + if (filter === 'completed') return todo.completed; + if (filter === 'not-completed') return !todo.completed; + return true; + }), + [todos, filter] + ); const location = useLocation(); return ( <> @@ -144,158 +126,115 @@ const TodoApp: React.FC = ({ filter }) => { > -
- - setText(e.target.value)} /> -
); }; -const TodoAppConnected = GlobalStateHelpers.connect(() => ({}))( - connect(() => ({}))(TodoApp) -); - export const TodoAppPage: React.FC<{ history: History; - appInstanceId: string; appTitle: string; appBasePath: string; - isInitialRoute: () => boolean; }> = (props) => { const initialAppUrl = React.useRef(window.location.href); - const [useHashedUrl, setUseHashedUrl] = React.useState(false); + const stateContainer = React.useMemo( + () => createStateContainer(defaultState, pureTransitions), + [] + ); - /** - * Replicates what src/legacy/ui/public/chrome/api/nav.ts did - * Persists the url in sessionStorage and tries to restore it on "componentDidMount" - */ - useUrlTracker(`lastUrlTracker:${props.appInstanceId}`, props.history, (urlToRestore) => { - // shouldRestoreUrl: - // App decides if it should restore url or not - // In this specific case, restore only if navigated to initial route - if (props.isInitialRoute()) { - // navigated to the base path, so should restore the url - return true; - } else { - // navigated to specific route, so should not restore the url - return false; - } - }); + // Most of kibana apps persist state in the URL in two ways: + // * Rison encoded. + // * Hashed URL: In the URL only the hash from the state is stored. The state itself is stored in + // the sessionStorage. See `state:storeInSessionStorage` advanced option for more context. + // This example shows how to use both of them + const [useHashedUrl, setUseHashedUrl] = React.useState(false); useEffect(() => { - // have to sync with history passed to react-router - // history v5 will be singleton and this will not be needed + // storage to sync our app state with + // in this case we want to sync state with query params in the URL serialised in rison format + // similar like Discover or Dashboard apps do const kbnUrlStateStorage = createKbnUrlStateStorage({ useHash: useHashedUrl, history: props.history, }); - const sessionStorageStateStorage = createSessionStorageStateStorage(); - - /** - * Restoring global state: - * State restoration similar to what GlobalState in legacy world did - * It restores state both from url and from session storage - */ - const globalStateKey = `_g`; - const globalStateFromInitialUrl = getStateFromKbnUrl( - globalStateKey, - initialAppUrl.current - ); - const globalStateFromCurrentUrl = kbnUrlStateStorage.get(globalStateKey); - const globalStateFromSessionStorage = sessionStorageStateStorage.get( - globalStateKey - ); + // key to store state in the storage. In this case in the key of the query param in the URL + const appStateKey = `_todo`; - const initialGlobalState: GlobalState = { - ...defaultGlobalState, - ...globalStateFromCurrentUrl, - ...globalStateFromSessionStorage, - ...globalStateFromInitialUrl, - }; - globalStateContainer.set(initialGlobalState); - kbnUrlStateStorage.set(globalStateKey, initialGlobalState, { replace: true }); - sessionStorageStateStorage.set(globalStateKey, initialGlobalState); - - /** - * Restoring app local state: - * State restoration similar to what AppState in legacy world did - * It restores state both from url - */ - const appStateKey = `_todo-${props.appInstanceId}`; + // take care of initial state. Make sure state in memory is the same as in the URL before starting any syncing const initialAppState: TodoState = getStateFromKbnUrl(appStateKey, initialAppUrl.current) || kbnUrlStateStorage.get(appStateKey) || defaultState; - container.set(initialAppState); + stateContainer.set(initialAppState); kbnUrlStateStorage.set(appStateKey, initialAppState, { replace: true }); - // start syncing only when made sure, that state in synced - const { stop, start } = syncStates([ - { - stateContainer: withDefaultState(container, defaultState), - storageKey: appStateKey, - stateStorage: kbnUrlStateStorage, - }, - { - stateContainer: withDefaultState(globalStateContainer, defaultGlobalState), - storageKey: globalStateKey, - stateStorage: kbnUrlStateStorage, - }, - { - stateContainer: withDefaultState(globalStateContainer, defaultGlobalState), - storageKey: globalStateKey, - stateStorage: sessionStorageStateStorage, - }, - ]); + // start syncing state between state container and the URL + const { stop, start } = syncState({ + stateContainer: withDefaultState(stateContainer, defaultState), + storageKey: appStateKey, + stateStorage: kbnUrlStateStorage, + }); start(); return () => { stop(); - - // reset state containers - container.set(defaultState); - globalStateContainer.set(defaultGlobalState); }; - }, [props.appInstanceId, props.history, useHashedUrl]); + }, [stateContainer, props.history, useHashedUrl]); return ( - - - - - - -

- State sync example. Instance: ${props.appInstanceId}. {props.appTitle} -

-
- setUseHashedUrl(!useHashedUrl)}> - {useHashedUrl ? 'Use Expanded State' : 'Use Hashed State'} - -
-
- - - - - - - - - - - - - - - -
-
-
+ + + + +

{props.appTitle}

+
+ + +

+ This is a simple TODO app that uses state containers and state syncing utils. It + stores state in the URL similar like Discover or Dashboard apps do.
+ Play with the app and see how the state is persisted in the URL. +
Undo/Redo with browser history also works. +

+
+
+
+ + + + + + + + + + + + + + + +

Most of kibana apps persist state in the URL in two ways:

+
    +
  1. Expanded state in rison format
  2. +
  3. + Just a state hash.
    + In the URL only the hash from the state is stored. The state itself is stored in + the sessionStorage. See `state:storeInSessionStorage` advanced option for more + context. +
  4. +
+

You can switch between these two mods:

+
+ + setUseHashedUrl(!useHashedUrl)}> + {useHashedUrl ? 'Use Expanded State' : 'Use Hashed State'} + +
+
+
); }; diff --git a/examples/state_containers_examples/public/with_data_services/components/app.tsx b/examples/state_containers_examples/public/with_data_services/app.tsx similarity index 58% rename from examples/state_containers_examples/public/with_data_services/components/app.tsx rename to examples/state_containers_examples/public/with_data_services/app.tsx index b526032a5bec..fc84e1e952aa 100644 --- a/examples/state_containers_examples/public/with_data_services/components/app.tsx +++ b/examples/state_containers_examples/public/with_data_services/app.tsx @@ -6,50 +6,47 @@ * Public License, v 1. */ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { History } from 'history'; -import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { Router } from 'react-router-dom'; import { EuiFieldText, - EuiPage, EuiPageBody, EuiPageContent, EuiPageHeader, + EuiText, EuiTitle, } from '@elastic/eui'; +import { CoreStart } from 'kibana/public'; +import { NavigationPublicPluginStart } from '../../../../src/plugins/navigation/public'; -import { CoreStart } from '../../../../../src/core/public'; -import { NavigationPublicPluginStart } from '../../../../../src/plugins/navigation/public'; import { connectToQueryState, - syncQueryStateWithUrl, DataPublicPluginStart, - IIndexPattern, - QueryState, - Filter, esFilters, + Filter, + IIndexPattern, Query, -} from '../../../../../src/plugins/data/public'; + QueryState, + syncQueryStateWithUrl, +} from '../../../../src/plugins/data/public'; import { - BaseState, BaseStateContainer, createStateContainer, - createStateContainerReactHelpers, IKbnUrlStateStorage, - ReduxLikeStateContainer, syncState, -} from '../../../../../src/plugins/kibana_utils/public'; -import { PLUGIN_ID, PLUGIN_NAME } from '../../../common'; + useContainerState, +} from '../../../../src/plugins/kibana_utils/public'; +import { ExampleLink, StateContainersExamplesPage } from '../common/example_page'; interface StateDemoAppDeps { - notifications: CoreStart['notifications']; - http: CoreStart['http']; + navigateToApp: CoreStart['application']['navigateToApp']; navigation: NavigationPublicPluginStart; data: DataPublicPluginStart; history: History; kbnUrlStateStorage: IKbnUrlStateStorage; + exampleLinks: ExampleLink[]; } interface AppState { @@ -61,85 +58,74 @@ const defaultAppState: AppState = { name: '', filters: [], }; -const { - Provider: AppStateContainerProvider, - useState: useAppState, - useContainer: useAppStateContainer, -} = createStateContainerReactHelpers>(); -const App = ({ navigation, data, history, kbnUrlStateStorage }: StateDemoAppDeps) => { - const appStateContainer = useAppStateContainer(); - const appState = useAppState(); +export const App = ({ + navigation, + data, + history, + kbnUrlStateStorage, + exampleLinks, + navigateToApp, +}: StateDemoAppDeps) => { + const appStateContainer = useMemo(() => createStateContainer(defaultAppState), []); + const appState = useContainerState(appStateContainer); useGlobalStateSyncing(data.query, kbnUrlStateStorage); useAppStateSyncing(appStateContainer, data.query, kbnUrlStateStorage); const indexPattern = useIndexPattern(data); if (!indexPattern) - return
No index pattern found. Please create an index patter before loading...
; + return ( +
+ No index pattern found. Please create an index pattern before trying this example... +
+ ); - // Render the application DOM. // Note that `navigation.ui.TopNavMenu` is a stateful component exported on the `navigation` plugin's start contract. return ( - - + + <> - - - - - -

- -

-
-
- - appStateContainer.set({ ...appState, name: e.target.value })} - aria-label="My name" - /> - -
-
+ + + +

Integration with search bar

+
+
+ +

+ This examples shows how you can use state containers, state syncing utils and + helpers from data plugin to sync your app state and search bar state with the URL. +

+
+ + + + +

+ In addition to state from query bar also sync your arbitrary application state: +

+
+ appStateContainer.set({ ...appState, name: e.target.value })} + aria-label="My name" + /> +
+
-
-
- ); -}; - -export const StateDemoApp = (props: StateDemoAppDeps) => { - const appStateContainer = useCreateStateContainer(defaultAppState); - - return ( - - - + + ); }; -function useCreateStateContainer( - defaultState: State -): ReduxLikeStateContainer { - const stateContainerRef = useRef | null>(null); - if (!stateContainerRef.current) { - stateContainerRef.current = createStateContainer(defaultState); - } - return stateContainerRef.current; -} - function useIndexPattern(data: DataPublicPluginStart) { const [indexPattern, setIndexPattern] = useState(); useEffect(() => { diff --git a/examples/state_containers_examples/public/with_data_services/application.tsx b/examples/state_containers_examples/public/with_data_services/application.tsx index d50c203a2a07..4235446dd06e 100644 --- a/examples/state_containers_examples/public/with_data_services/application.tsx +++ b/examples/state_containers_examples/public/with_data_services/application.tsx @@ -10,24 +10,26 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { AppMountParameters, CoreStart } from '../../../../src/core/public'; import { AppPluginDependencies } from './types'; -import { StateDemoApp } from './components/app'; +import { App } from './app'; import { createKbnUrlStateStorage } from '../../../../src/plugins/kibana_utils/public/'; +import { ExampleLink } from '../common/example_page'; export const renderApp = ( - { notifications, http }: CoreStart, + { notifications, application }: CoreStart, { navigation, data }: AppPluginDependencies, - { element, history }: AppMountParameters + { element, history }: AppMountParameters, + { exampleLinks }: { exampleLinks: ExampleLink[] } ) => { const kbnUrlStateStorage = createKbnUrlStateStorage({ useHash: false, history }); ReactDOM.render( - , element ); diff --git a/examples/state_containers_examples/server/plugin.ts b/examples/state_containers_examples/server/plugin.ts deleted file mode 100644 index 04ab4d7a0fed..000000000000 --- a/examples/state_containers_examples/server/plugin.ts +++ /dev/null @@ -1,45 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { - PluginInitializerContext, - CoreSetup, - CoreStart, - Plugin, - Logger, -} from '../../../src/core/server'; - -import { StateDemoPluginSetup, StateDemoPluginStart } from './types'; -import { defineRoutes } from './routes'; - -export class StateDemoServerPlugin implements Plugin { - private readonly logger: Logger; - - constructor(initializerContext: PluginInitializerContext) { - this.logger = initializerContext.logger.get(); - } - - public setup(core: CoreSetup) { - this.logger.debug('State_demo: Ssetup'); - const router = core.http.createRouter(); - - // Register server side APIs - defineRoutes(router); - - return {}; - } - - public start(core: CoreStart) { - this.logger.debug('State_demo: Started'); - return {}; - } - - public stop() {} -} - -export { StateDemoServerPlugin as Plugin }; diff --git a/examples/state_containers_examples/server/routes/index.ts b/examples/state_containers_examples/server/routes/index.ts deleted file mode 100644 index f7c7a6abe880..000000000000 --- a/examples/state_containers_examples/server/routes/index.ts +++ /dev/null @@ -1,25 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { IRouter } from '../../../../src/core/server'; - -export function defineRoutes(router: IRouter) { - router.get( - { - path: '/api/state_demo/example', - validate: false, - }, - async (context, request, response) => { - return response.ok({ - body: { - time: new Date().toISOString(), - }, - }); - } - ); -} diff --git a/package.json b/package.json index 81854ed7f595..2c5428c65e29 100644 --- a/package.json +++ b/package.json @@ -592,6 +592,7 @@ "base64-js": "^1.3.1", "base64url": "^3.0.1", "broadcast-channel": "^3.0.3", + "callsites": "^3.1.0", "chai": "3.5.0", "chance": "1.0.18", "chromedriver": "^87.0.3", @@ -827,7 +828,7 @@ "url-loader": "^2.2.0", "use-resize-observer": "^6.0.0", "val-loader": "^1.1.1", - "vega": "^5.18.0", + "vega": "^5.19.1", "vega-lite": "^4.17.0", "vega-schema-url-parser": "^2.1.0", "vega-tooltip": "^0.25.0", diff --git a/packages/kbn-test/src/functional_test_runner/lib/providers/verbose_instance.ts b/packages/kbn-test/src/functional_test_runner/lib/providers/verbose_instance.ts index 248b55d85d8f..cc2ecad82fb1 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/providers/verbose_instance.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/providers/verbose_instance.ts @@ -65,7 +65,11 @@ export function createVerboseInstance( } const { returned } = result; - if (returned && typeof returned.then === 'function') { + if ( + returned && + typeof returned.then === 'function' && + typeof returned.finally === 'function' + ) { return returned.finally(() => { log.indent(-2); }); diff --git a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts index a138673d69eb..2a238cdeb538 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts @@ -12,7 +12,32 @@ import { decorateSnapshotUi, expectSnapshot } from './decorate_snapshot_ui'; import path from 'path'; import fs from 'fs'; +const createMockTest = ({ + title = 'Test', + passed = true, +}: { title?: string; passed?: boolean } = {}) => { + return { + fullTitle: () => title, + isPassed: () => passed, + parent: {}, + } as Test; +}; + describe('decorateSnapshotUi', () => { + const snapshotFolder = path.resolve(__dirname, '__snapshots__'); + const snapshotFile = path.resolve(snapshotFolder, 'decorate_snapshot_ui.test.snap'); + + const cleanup = () => { + if (fs.existsSync(snapshotFile)) { + fs.unlinkSync(snapshotFile); + fs.rmdirSync(snapshotFolder); + } + }; + + beforeEach(cleanup); + + afterAll(cleanup); + describe('when running a test', () => { let lifecycle: Lifecycle; beforeEach(() => { @@ -21,15 +46,7 @@ describe('decorateSnapshotUi', () => { }); it('passes when the snapshot matches the actual value', async () => { - const test: Test = { - title: 'Test', - file: 'foo.ts', - parent: { - file: 'foo.ts', - tests: [], - suites: [], - }, - } as any; + const test = createMockTest(); await lifecycle.beforeEachTest.trigger(test); @@ -39,15 +56,7 @@ describe('decorateSnapshotUi', () => { }); it('throws when the snapshot does not match the actual value', async () => { - const test: Test = { - title: 'Test', - file: 'foo.ts', - parent: { - file: 'foo.ts', - tests: [], - suites: [], - }, - } as any; + const test = createMockTest(); await lifecycle.beforeEachTest.trigger(test); @@ -57,27 +66,10 @@ describe('decorateSnapshotUi', () => { }); it('writes a snapshot to an external file if it does not exist', async () => { - const test: Test = { - title: 'Test', - file: __filename, - isPassed: () => true, - } as any; - - // @ts-expect-error - test.parent = { - file: __filename, - tests: [test], - suites: [], - }; + const test: Test = createMockTest(); await lifecycle.beforeEachTest.trigger(test); - const snapshotFile = path.resolve( - __dirname, - '__snapshots__', - 'decorate_snapshot_ui.test.snap' - ); - expect(fs.existsSync(snapshotFile)).toBe(false); expect(() => { @@ -87,10 +79,48 @@ describe('decorateSnapshotUi', () => { await lifecycle.afterTestSuite.trigger(test.parent); expect(fs.existsSync(snapshotFile)).toBe(true); + }); + }); - fs.unlinkSync(snapshotFile); + describe('when writing multiple snapshots to a single file', () => { + let lifecycle: Lifecycle; + beforeEach(() => { + lifecycle = new Lifecycle(); + decorateSnapshotUi({ lifecycle, updateSnapshots: false, isCi: false }); + }); + + beforeEach(() => { + fs.mkdirSync(path.resolve(__dirname, '__snapshots__')); + fs.writeFileSync( + snapshotFile, + `// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[\`Test1 1\`] = \`"foo"\`; + +exports[\`Test2 1\`] = \`"bar"\`; + `, + { encoding: 'utf-8' } + ); + }); + + it('compares to an existing snapshot', async () => { + const test1 = createMockTest({ title: 'Test1' }); + + await lifecycle.beforeEachTest.trigger(test1); + + expect(() => { + expectSnapshot('foo').toMatch(); + }).not.toThrow(); + + const test2 = createMockTest({ title: 'Test2' }); - fs.rmdirSync(path.resolve(__dirname, '__snapshots__')); + await lifecycle.beforeEachTest.trigger(test2); + + expect(() => { + expectSnapshot('foo').toMatch(); + }).toThrow(); + + await lifecycle.afterTestSuite.trigger(test1.parent); }); }); @@ -102,15 +132,7 @@ describe('decorateSnapshotUi', () => { }); it("doesn't throw if the value does not match", async () => { - const test: Test = { - title: 'Test', - file: 'foo.ts', - parent: { - file: 'foo.ts', - tests: [], - suites: [], - }, - } as any; + const test = createMockTest(); await lifecycle.beforeEachTest.trigger(test); @@ -128,15 +150,7 @@ describe('decorateSnapshotUi', () => { }); it('throws on new snapshots', async () => { - const test: Test = { - title: 'Test', - file: 'foo.ts', - parent: { - file: 'foo.ts', - tests: [], - suites: [], - }, - } as any; + const test = createMockTest(); await lifecycle.beforeEachTest.trigger(test); @@ -144,5 +158,82 @@ describe('decorateSnapshotUi', () => { expectSnapshot('bar').toMatchInline(); }).toThrow(); }); + + describe('when adding to an existing file', () => { + beforeEach(() => { + fs.mkdirSync(path.resolve(__dirname, '__snapshots__')); + fs.writeFileSync( + snapshotFile, + `// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[\`Test 1\`] = \`"foo"\`; + +exports[\`Test2 1\`] = \`"bar"\`; + `, + { encoding: 'utf-8' } + ); + }); + + it('does not throw on an existing test', async () => { + const test = createMockTest({ title: 'Test' }); + + await lifecycle.beforeEachTest.trigger(test); + + expect(() => { + expectSnapshot('foo').toMatch(); + }).not.toThrow(); + }); + + it('throws on a new test', async () => { + const test = createMockTest({ title: 'New test' }); + + await lifecycle.beforeEachTest.trigger(test); + + expect(() => { + expectSnapshot('foo').toMatch(); + }).toThrow(); + }); + + it('does not throw when all snapshots are used ', async () => { + const test = createMockTest({ title: 'Test' }); + + await lifecycle.beforeEachTest.trigger(test); + + expect(() => { + expectSnapshot('foo').toMatch(); + }).not.toThrow(); + + const test2 = createMockTest({ title: 'Test2' }); + + await lifecycle.beforeEachTest.trigger(test2); + + expect(() => { + expectSnapshot('bar').toMatch(); + }).not.toThrow(); + + const afterTestSuite = lifecycle.afterTestSuite.trigger({}); + + await expect(afterTestSuite).resolves.toBe(undefined); + }); + + it('throws on unused snapshots', async () => { + const test = createMockTest({ title: 'Test' }); + + await lifecycle.beforeEachTest.trigger(test); + + expect(() => { + expectSnapshot('foo').toMatch(); + }).not.toThrow(); + + const afterTestSuite = lifecycle.afterTestSuite.trigger({}); + + await expect(afterTestSuite).rejects.toMatchInlineSnapshot(` + [Error: 1 obsolete snapshot(s) found: + Test2 1. + + Run tests again with \`--updateSnapshots\` to remove them.] + `); + }); + }); }); }); diff --git a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts index c43b50de3afd..2111f1a6e5e9 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts @@ -15,9 +15,10 @@ import { import path from 'path'; import prettier from 'prettier'; import babelTraverse from '@babel/traverse'; -import { flatten, once } from 'lodash'; +import { once } from 'lodash'; +import callsites from 'callsites'; import { Lifecycle } from '../lifecycle'; -import { Test, Suite } from '../../fake_mocha_types'; +import { Test } from '../../fake_mocha_types'; type ISnapshotState = InstanceType; @@ -28,40 +29,17 @@ interface SnapshotContext { currentTestName: string; } -let testContext: { - file: string; - snapshotTitle: string; - snapshotContext: SnapshotContext; -} | null = null; - -let registered: boolean = false; - -function getSnapshotMeta(currentTest: Test) { - // Make sure snapshot title is unique per-file, rather than entire - // suite. This allows reuse of tests, for instance to compare - // results for different configurations. - - const titles = [currentTest.title]; - const file = currentTest.file; - - let test: Suite | undefined = currentTest?.parent; - - while (test && test.file === file) { - titles.push(test.title); - test = test.parent; - } - - const snapshotTitle = titles.reverse().join(' '); - - if (!file || !snapshotTitle) { - throw new Error(`file or snapshotTitle not available in Mocha test context`); - } - - return { - file, - snapshotTitle, - }; -} +const globalState: { + updateSnapshot: SnapshotUpdateState; + registered: boolean; + currentTest: Test | null; + snapshots: Array<{ tests: Test[]; file: string; snapshotState: ISnapshotState }>; +} = { + updateSnapshot: 'none', + registered: false, + currentTest: null, + snapshots: [], +}; const modifyStackTracePrepareOnce = once(() => { const originalPrepareStackTrace = Error.prepareStackTrace; @@ -72,7 +50,7 @@ const modifyStackTracePrepareOnce = once(() => { Error.prepareStackTrace = (error, structuredStackTrace) => { let filteredStrackTrace: NodeJS.CallSite[] = structuredStackTrace; - if (registered) { + if (globalState.registered) { filteredStrackTrace = filteredStrackTrace.filter((callSite) => { // check for both compiled and uncompiled files return !callSite.getFileName()?.match(/decorate_snapshot_ui\.(js|ts)/); @@ -94,21 +72,16 @@ export function decorateSnapshotUi({ updateSnapshots: boolean; isCi: boolean; }) { - let snapshotStatesByFilePath: Record< - string, - { snapshotState: ISnapshotState; testsInFile: Test[] } - > = {}; - - registered = true; - - let updateSnapshot: SnapshotUpdateState; + globalState.registered = true; + globalState.snapshots.length = 0; + globalState.currentTest = null; if (isCi) { // make sure snapshots that have not been committed // are not written to file on CI, passing the test - updateSnapshot = 'none'; + globalState.updateSnapshot = 'none'; } else { - updateSnapshot = updateSnapshots ? 'all' : 'new'; + globalState.updateSnapshot = updateSnapshots ? 'all' : 'new'; } modifyStackTracePrepareOnce(); @@ -125,21 +98,8 @@ export function decorateSnapshotUi({ // @ts-expect-error global.expectSnapshot = expectSnapshot; - lifecycle.beforeEachTest.add((currentTest: Test) => { - const { file, snapshotTitle } = getSnapshotMeta(currentTest); - - if (!snapshotStatesByFilePath[file]) { - snapshotStatesByFilePath[file] = getSnapshotState(file, currentTest, updateSnapshot); - } - - testContext = { - file, - snapshotTitle, - snapshotContext: { - snapshotState: snapshotStatesByFilePath[file].snapshotState, - currentTestName: snapshotTitle, - }, - }; + lifecycle.beforeEachTest.add((test: Test) => { + globalState.currentTest = test; }); lifecycle.afterTestSuite.add(function (testSuite) { @@ -150,19 +110,18 @@ export function decorateSnapshotUi({ const unused: string[] = []; - Object.keys(snapshotStatesByFilePath).forEach((file) => { - const { snapshotState, testsInFile } = snapshotStatesByFilePath[file]; - - testsInFile.forEach((test) => { - const snapshotMeta = getSnapshotMeta(test); + globalState.snapshots.forEach((snapshot) => { + const { tests, snapshotState } = snapshot; + tests.forEach((test) => { + const title = test.fullTitle(); // If test is failed or skipped, mark snapshots as used. Otherwise, // running a test in isolation will generate false positives. if (!test.isPassed()) { - snapshotState.markSnapshotsAsCheckedForTest(snapshotMeta.snapshotTitle); + snapshotState.markSnapshotsAsCheckedForTest(title); } }); - if (!updateSnapshots) { + if (globalState.updateSnapshot !== 'all') { unused.push(...snapshotState.getUncheckedKeys()); } else { snapshotState.removeUncheckedKeys(); @@ -179,28 +138,14 @@ export function decorateSnapshotUi({ ); } - snapshotStatesByFilePath = {}; + globalState.snapshots.length = 0; }); } -function recursivelyGetTestsFromSuite(suite: Suite): Test[] { - return suite.tests.concat(flatten(suite.suites.map((s) => recursivelyGetTestsFromSuite(s)))); -} - -function getSnapshotState(file: string, test: Test, updateSnapshot: SnapshotUpdateState) { +function getSnapshotState(file: string, updateSnapshot: SnapshotUpdateState) { const dirname = path.dirname(file); const filename = path.basename(file); - let parent: Suite | undefined = test.parent; - - while (parent && parent.parent?.file === file) { - parent = parent.parent; - } - - if (!parent) { - throw new Error('Top-level suite not found'); - } - const snapshotState = new SnapshotState( path.join(dirname + `/__snapshots__/` + filename.replace(path.extname(filename), '.snap')), { @@ -211,24 +156,54 @@ function getSnapshotState(file: string, test: Test, updateSnapshot: SnapshotUpda } ); - return { snapshotState, testsInFile: recursivelyGetTestsFromSuite(parent) }; + return snapshotState; } export function expectSnapshot(received: any) { - if (!registered) { + if (!globalState.registered) { throw new Error( 'Mocha hooks were not registered before expectSnapshot was used. Call `registerMochaHooksForSnapshots` in your top-level describe().' ); } - if (!testContext) { - throw new Error('A current Mocha context is needed to match snapshots'); + if (!globalState.currentTest) { + throw new Error('expectSnapshot can only be called inside of an it()'); + } + + const [, fileOfTest] = callsites().map((site) => site.getFileName()); + + if (!fileOfTest) { + throw new Error("Couldn't infer a filename for the current test"); + } + + let snapshot = globalState.snapshots.find(({ file }) => file === fileOfTest); + + if (!snapshot) { + snapshot = { + file: fileOfTest, + tests: [], + snapshotState: getSnapshotState(fileOfTest, globalState.updateSnapshot), + }; + globalState.snapshots.unshift(snapshot!); + } + + if (!snapshot) { + throw new Error('Snapshot is undefined'); + } + + if (!snapshot.tests.includes(globalState.currentTest)) { + snapshot.tests.push(globalState.currentTest); } + const context: SnapshotContext = { + snapshotState: snapshot.snapshotState, + currentTestName: globalState.currentTest.fullTitle(), + }; + return { - toMatch: expectToMatchSnapshot.bind(null, testContext.snapshotContext, received), + toMatch: expectToMatchSnapshot.bind(null, context, received), // use bind to support optional 3rd argument (actual) - toMatchInline: expectToMatchInlineSnapshot.bind(null, testContext.snapshotContext, received), + toMatchInline: expectToMatchInlineSnapshot.bind(null, context, received), }; } diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 1a69c7db35a7..b82254e5a141 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -182,7 +182,12 @@ export class DocLinksService { guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/maps.html`, }, monitoring: { + alertsCluster: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/cluster-alerts.html`, alertsKibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html`, + alertsKibanaCpuThreshold: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html#kibana-alerts-cpu-threshold`, + alertsKibanaDiskThreshold: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html#kibana-alerts-disk-usage-threshold`, + alertsKibanaJvmThreshold: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html#kibana-alerts-jvm-memory-threshold`, + alertsKibanaMissingData: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kibana-alerts.html#kibana-alerts-missing-monitoring-data`, monitorElasticsearch: `${ELASTICSEARCH_DOCS}configuring-metricbeat.html`, monitorKibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/monitoring-metricbeat.html`, }, diff --git a/src/core/public/http/base_path.mock.ts b/src/core/public/http/base_path.mock.ts new file mode 100644 index 000000000000..851ad1ffca44 --- /dev/null +++ b/src/core/public/http/base_path.mock.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { IBasePath } from './types'; + +const createBasePathMock = ({ + publicBaseUrl = '/', + serverBasePath = '/', +}: { publicBaseUrl?: string; serverBasePath?: string } = {}) => { + const mock: jest.Mocked = { + prepend: jest.fn(), + get: jest.fn(), + remove: jest.fn(), + publicBaseUrl, + serverBasePath, + }; + + return mock; +}; + +export const basePathMock = { + create: createBasePathMock, +}; diff --git a/src/core/public/http/http_service.mock.ts b/src/core/public/http/http_service.mock.ts index bbc412461c48..c00773b51055 100644 --- a/src/core/public/http/http_service.mock.ts +++ b/src/core/public/http/http_service.mock.ts @@ -11,6 +11,7 @@ import { HttpService } from './http_service'; import { HttpSetup } from './types'; import { BehaviorSubject } from 'rxjs'; import { BasePath } from './base_path'; +import { basePathMock } from './base_path.mock'; export type HttpSetupMock = jest.Mocked & { basePath: BasePath; @@ -54,4 +55,5 @@ export const httpServiceMock = { create: createMock, createSetupContract: createServiceMock, createStartContract: createServiceMock, + createBasePath: basePathMock.create, }; diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 784def847f1b..66dd4f3028aa 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -130,6 +130,9 @@ export { SavedObjectsImportFailure, SavedObjectsImportRetry, SavedObjectsNamespaceType, + SavedObjectsImportSimpleWarning, + SavedObjectsImportActionRequiredWarning, + SavedObjectsImportWarning, } from './saved_objects'; export { diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 5c0b2a45abd4..52fc8fbf3391 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -9,6 +9,7 @@ import { ApiResponse } from '@elastic/elasticsearch/lib/Transport'; import Boom from '@hapi/boom'; import { ConfigDeprecationProvider } from '@kbn/config'; import { ConfigPath } from '@kbn/config'; +import { DetailedPeerCertificate } from 'tls'; import { EnvironmentMode } from '@kbn/config'; import { EuiBreadcrumb } from '@elastic/eui'; import { EuiButtonEmptyProps } from '@elastic/eui'; @@ -18,20 +19,25 @@ import { EuiGlobalToastListToast } from '@elastic/eui'; import { History } from 'history'; import { Href } from 'history'; import { IconType } from '@elastic/eui'; +import { IncomingHttpHeaders } from 'http'; import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; import { Logger } from '@kbn/logging'; import { LogMeta } from '@kbn/logging'; import { MaybePromise } from '@kbn/utility-types'; +import { ObjectType } from '@kbn/config-schema'; import { Observable } from 'rxjs'; import { PackageInfo } from '@kbn/config'; import { Path } from 'history'; +import { PeerCertificate } from 'tls'; import { PublicMethodsOf } from '@kbn/utility-types'; import { PublicUiSettingsParams as PublicUiSettingsParams_2 } from 'src/core/server/types'; import React from 'react'; import { RecursiveReadonly } from '@kbn/utility-types'; +import { Request } from '@hapi/hapi'; import * as Rx from 'rxjs'; +import { SchemaTypeError } from '@kbn/config-schema'; import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport'; import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport'; import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; @@ -39,6 +45,7 @@ import { Type } from '@kbn/config-schema'; import { TypeOf } from '@kbn/config-schema'; import { UiCounterMetricType } from '@kbn/analytics'; import { UnregisterCallback } from 'history'; +import { URL } from 'url'; import { UserProvidedValues as UserProvidedValues_2 } from 'src/core/server/types'; // @internal (undocumented) @@ -1027,6 +1034,7 @@ export type PublicUiSettingsParams = Omit; // @public (undocumented) export interface SavedObject { attributes: T; + coreMigrationVersion?: string; // (undocumented) error?: SavedObjectError; id: string; @@ -1144,6 +1152,7 @@ export type SavedObjectsClientContract = PublicMethodsOf; // @public (undocumented) export interface SavedObjectsCreateOptions { + coreMigrationVersion?: string; id?: string; migrationVersion?: SavedObjectsMigrationVersion; overwrite?: boolean; @@ -1198,6 +1207,15 @@ export interface SavedObjectsFindResponsePublic extends SavedObject total: number; } +// @public +export interface SavedObjectsImportActionRequiredWarning { + actionPath: string; + buttonLabel?: string; + message: string; + // (undocumented) + type: 'action_required'; +} + // @public export interface SavedObjectsImportAmbiguousConflictError { // (undocumented) @@ -1257,6 +1275,8 @@ export interface SavedObjectsImportResponse { successCount: number; // (undocumented) successResults?: SavedObjectsImportSuccess[]; + // (undocumented) + warnings: SavedObjectsImportWarning[]; } // @public @@ -1278,6 +1298,13 @@ export interface SavedObjectsImportRetry { type: string; } +// @public +export interface SavedObjectsImportSimpleWarning { + message: string; + // (undocumented) + type: 'simple'; +} + // @public export interface SavedObjectsImportSuccess { // @deprecated (undocumented) @@ -1311,6 +1338,9 @@ export interface SavedObjectsImportUnsupportedTypeError { type: 'unsupported_type'; } +// @public +export type SavedObjectsImportWarning = SavedObjectsImportSimpleWarning | SavedObjectsImportActionRequiredWarning; + // @public export interface SavedObjectsMigrationVersion { // (undocumented) @@ -1356,10 +1386,12 @@ export class ScopedHistory implements History { - constructor(client: SavedObjectsClientContract, { id, type, version, attributes, error, references, migrationVersion }: SavedObject); + constructor(client: SavedObjectsClientContract, { id, type, version, attributes, error, references, migrationVersion, coreMigrationVersion, }: SavedObject); // (undocumented) attributes: T; // (undocumented) + coreMigrationVersion: SavedObject['coreMigrationVersion']; + // (undocumented) delete(): Promise<{}>; // (undocumented) error: SavedObject['error']; diff --git a/src/core/public/saved_objects/index.ts b/src/core/public/saved_objects/index.ts index e83d2044a2d7..537ca0da088f 100644 --- a/src/core/public/saved_objects/index.ts +++ b/src/core/public/saved_objects/index.ts @@ -35,6 +35,9 @@ export { SavedObjectsImportFailure, SavedObjectsImportRetry, SavedObjectsNamespaceType, + SavedObjectsImportSimpleWarning, + SavedObjectsImportActionRequiredWarning, + SavedObjectsImportWarning, } from '../../server/types'; export { diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index 6c24cf2d0971..fdef63c392db 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -38,6 +38,8 @@ export interface SavedObjectsCreateOptions { overwrite?: boolean; /** {@inheritDoc SavedObjectsMigrationVersion} */ migrationVersion?: SavedObjectsMigrationVersion; + /** A semver value that is used when upgrading objects between Kibana versions. */ + coreMigrationVersion?: string; references?: SavedObjectReference[]; } diff --git a/src/core/public/saved_objects/simple_saved_object.ts b/src/core/public/saved_objects/simple_saved_object.ts index a0ebc8214aae..0eb0e0b53f78 100644 --- a/src/core/public/saved_objects/simple_saved_object.ts +++ b/src/core/public/saved_objects/simple_saved_object.ts @@ -27,12 +27,22 @@ export class SimpleSavedObject { public id: SavedObjectType['id']; public type: SavedObjectType['type']; public migrationVersion: SavedObjectType['migrationVersion']; + public coreMigrationVersion: SavedObjectType['coreMigrationVersion']; public error: SavedObjectType['error']; public references: SavedObjectType['references']; constructor( private client: SavedObjectsClientContract, - { id, type, version, attributes, error, references, migrationVersion }: SavedObjectType + { + id, + type, + version, + attributes, + error, + references, + migrationVersion, + coreMigrationVersion, + }: SavedObjectType ) { this.id = id; this.type = type; @@ -40,6 +50,7 @@ export class SimpleSavedObject { this.references = references || []; this._version = version; this.migrationVersion = migrationVersion; + this.coreMigrationVersion = coreMigrationVersion; if (error) { this.error = error; } @@ -66,6 +77,7 @@ export class SimpleSavedObject { } else { return this.client.create(this.type, this.attributes, { migrationVersion: this.migrationVersion, + coreMigrationVersion: this.coreMigrationVersion, references: this.references, }); } diff --git a/src/core/utils/context.mock.ts b/src/core/server/context/container/context.mock.ts similarity index 92% rename from src/core/utils/context.mock.ts rename to src/core/server/context/container/context.mock.ts index fe15cbc7681d..7f5a8ef28aab 100644 --- a/src/core/utils/context.mock.ts +++ b/src/core/server/context/container/context.mock.ts @@ -12,6 +12,7 @@ export type ContextContainerMock = jest.Mocked>; const createContextMock = (mockContext = {}) => { const contextMock: ContextContainerMock = { + // @ts-expect-error tsc cannot infer ContextName and uses never registerContext: jest.fn(), createHandler: jest.fn(), }; diff --git a/src/core/server/context/container/context.test.ts b/src/core/server/context/container/context.test.ts new file mode 100644 index 000000000000..0e697d64ed2e --- /dev/null +++ b/src/core/server/context/container/context.test.ts @@ -0,0 +1,319 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { ContextContainer } from './context'; +import { PluginOpaqueId } from '../..'; +import { httpServerMock } from '../../http/http_server.mocks'; + +const pluginA = Symbol('pluginA'); +const pluginB = Symbol('pluginB'); +const pluginC = Symbol('pluginC'); +const pluginD = Symbol('pluginD'); +const plugins: ReadonlyMap = new Map([ + [pluginA, []], + [pluginB, [pluginA]], + [pluginC, [pluginA, pluginB]], + [pluginD, []], +]); +const coreId = Symbol(); + +interface MyContext { + core: any; + core1: string; + core2: number; + ctxFromA: string; + ctxFromB: number; + ctxFromC: boolean; + ctxFromD: object; +} + +describe('ContextContainer', () => { + it('does not allow the same context to be registered twice', () => { + const contextContainer = new ContextContainer(plugins, coreId); + contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>( + coreId, + 'ctxFromA', + () => 'aString' + ); + + expect(() => + contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>( + coreId, + 'ctxFromA', + () => 'aString' + ) + ).toThrowErrorMatchingInlineSnapshot( + `"Context provider for ctxFromA has already been registered."` + ); + }); + + describe('registerContext', () => { + it('throws error if called with an unknown symbol', async () => { + const contextContainer = new ContextContainer(plugins, coreId); + await expect(() => + contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>( + Symbol('unknown'), + 'ctxFromA', + jest.fn() + ) + ).toThrowErrorMatchingInlineSnapshot( + `"Cannot register context for unknown plugin: Symbol(unknown)"` + ); + }); + }); + + describe('context building', () => { + it('resolves dependencies', async () => { + const contextContainer = new ContextContainer(plugins, coreId); + expect.assertions(8); + contextContainer.registerContext<{ core1: string; core: any }, 'core1'>( + coreId, + 'core1', + (context) => { + expect(context).toEqual({}); + return 'core'; + } + ); + + contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>( + pluginA, + 'ctxFromA', + (context) => { + expect(context).toEqual({ core1: 'core' }); + return 'aString'; + } + ); + contextContainer.registerContext<{ ctxFromB: number; core: any }, 'ctxFromB'>( + pluginB, + 'ctxFromB', + (context) => { + expect(context).toEqual({ core1: 'core', ctxFromA: 'aString' }); + return 299; + } + ); + contextContainer.registerContext<{ ctxFromC: boolean; core: any }, 'ctxFromC'>( + pluginC, + 'ctxFromC', + (context) => { + expect(context).toEqual({ + core1: 'core', + ctxFromA: 'aString', + ctxFromB: 299, + }); + return false; + } + ); + contextContainer.registerContext<{ ctxFromD: {}; core: any }, 'ctxFromD'>( + pluginD, + 'ctxFromD', + (context) => { + expect(context).toEqual({ core1: 'core' }); + return {}; + } + ); + + const rawHandler1 = jest.fn(() => 'handler1' as any); + const handler1 = contextContainer.createHandler(pluginC, rawHandler1); + + const rawHandler2 = jest.fn(() => 'handler2' as any); + const handler2 = contextContainer.createHandler(pluginD, rawHandler2); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + + await handler1(request, response); + await handler2(request, response); + + // Should have context from pluginC, its deps, and core + expect(rawHandler1).toHaveBeenCalledWith( + { + core1: 'core', + ctxFromA: 'aString', + ctxFromB: 299, + ctxFromC: false, + }, + request, + response + ); + + // Should have context from pluginD, and core + expect(rawHandler2).toHaveBeenCalledWith( + { + core1: 'core', + ctxFromD: {}, + }, + request, + response + ); + }); + + it('exposes all core context to all providers regardless of registration order', async () => { + expect.assertions(4); + + const contextContainer = new ContextContainer(plugins, coreId); + contextContainer + .registerContext(pluginA, 'ctxFromA', (context) => { + expect(context).toEqual({ core1: 'core', core2: 101 }); + return `aString ${context.core1} ${context.core2}`; + }) + .registerContext(coreId, 'core1', () => 'core') + .registerContext(coreId, 'core2', () => 101) + .registerContext(pluginB, 'ctxFromB', (context) => { + expect(context).toEqual({ + core1: 'core', + core2: 101, + ctxFromA: 'aString core 101', + }); + return 277; + }); + + const rawHandler1 = jest.fn(() => 'handler1' as any); + const handler1 = contextContainer.createHandler(pluginB, rawHandler1); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + expect(await handler1(request, response)).toEqual('handler1'); + + expect(rawHandler1).toHaveBeenCalledWith( + { + core1: 'core', + core2: 101, + ctxFromA: 'aString core 101', + ctxFromB: 277, + }, + request, + response + ); + }); + + it('exposes all core context to core providers', async () => { + expect.assertions(4); + const contextContainer = new ContextContainer(plugins, coreId); + + contextContainer + .registerContext(coreId, 'core1', (context) => { + expect(context).toEqual({}); + return 'core'; + }) + .registerContext(coreId, 'core2', (context) => { + expect(context).toEqual({ core1: 'core' }); + return 101; + }); + + const rawHandler1 = jest.fn(() => 'handler1' as any); + const handler1 = contextContainer.createHandler(pluginA, rawHandler1); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + expect(await handler1(request, response)).toEqual('handler1'); + + // If no context is registered for pluginA, only core contexts should be exposed + expect(rawHandler1).toHaveBeenCalledWith( + { + core1: 'core', + core2: 101, + }, + request, + response + ); + }); + + it('does not expose plugin contexts to core handler', async () => { + const contextContainer = new ContextContainer(plugins, coreId); + + contextContainer + .registerContext(coreId, 'core1', (context) => 'core') + .registerContext(pluginA, 'ctxFromA', (context) => 'aString'); + + const rawHandler1 = jest.fn(() => 'handler1' as any); + const handler1 = contextContainer.createHandler(coreId, rawHandler1); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + expect(await handler1(request, response)).toEqual('handler1'); + // pluginA context should not be present in a core handler + expect(rawHandler1).toHaveBeenCalledWith( + { + core1: 'core', + }, + request, + response + ); + }); + + it('passes additional arguments to providers', async () => { + expect.assertions(6); + const contextContainer = new ContextContainer(plugins, coreId); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + contextContainer.registerContext(coreId, 'core1', (context, req, res) => { + expect(req).toBe(request); + expect(res).toBe(response); + return 'core'; + }); + + contextContainer.registerContext( + pluginD, + 'ctxFromB', + (context, req, res) => { + expect(req).toBe(request); + expect(res).toBe(response); + return 77; + } + ); + + const rawHandler1 = jest.fn(() => 'handler1' as any); + const handler1 = contextContainer.createHandler(pluginD, rawHandler1); + + expect(await handler1(request, response)).toEqual('handler1'); + + expect(rawHandler1).toHaveBeenCalledWith( + { + core1: 'core', + ctxFromB: 77, + }, + request, + response + ); + }); + }); + + describe('createHandler', () => { + it('throws error if called with an unknown symbol', async () => { + const contextContainer = new ContextContainer(plugins, coreId); + await expect(() => + contextContainer.createHandler(Symbol('unknown'), jest.fn()) + ).toThrowErrorMatchingInlineSnapshot( + `"Cannot create handler for unknown plugin: Symbol(unknown)"` + ); + }); + + it('returns value from original handler', async () => { + const contextContainer = new ContextContainer(plugins, coreId); + const rawHandler1 = jest.fn(() => 'handler1' as any); + const handler1 = contextContainer.createHandler(pluginA, rawHandler1); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + expect(await handler1(request, response)).toEqual('handler1'); + }); + + it('passes additional arguments to handlers', async () => { + const contextContainer = new ContextContainer(plugins, coreId); + + const rawHandler1 = jest.fn(() => 'handler1' as any); + const handler1 = contextContainer.createHandler(pluginA, rawHandler1); + + const request = httpServerMock.createKibanaRequest(); + const response = httpServerMock.createResponseFactory(); + await handler1(request, response); + expect(rawHandler1).toHaveBeenCalledWith({}, request, response); + }); + }); +}); diff --git a/src/core/utils/context.ts b/src/core/server/context/container/context.ts similarity index 85% rename from src/core/utils/context.ts rename to src/core/server/context/container/context.ts index f3d437001382..234fb7d34700 100644 --- a/src/core/utils/context.ts +++ b/src/core/server/context/container/context.ts @@ -8,13 +8,8 @@ import { flatten } from 'lodash'; import { ShallowPromise } from '@kbn/utility-types'; -import { pick } from '@kbn/std'; -import type { CoreId, PluginOpaqueId } from '../server'; - -/** - * Make all properties in T optional, except for the properties whose keys are in the union K - */ -type PartialExceptFor = Partial & Pick; +import { pick } from 'lodash'; +import type { CoreId, PluginOpaqueId, RequestHandler, RequestHandlerContext } from '../..'; /** * A function that returns a context value for a specific key of given context type. @@ -30,15 +25,13 @@ type PartialExceptFor = Partial & Pick; * @public */ export type IContextProvider< - THandler extends HandlerFunction, - TContextName extends keyof HandlerContextType + Context extends RequestHandlerContext, + ContextName extends keyof Context > = ( // context.core will always be available, but plugin contexts are typed as optional - context: PartialExceptFor, 'core'>, - ...rest: HandlerParameters -) => - | Promise[TContextName]> - | HandlerContextType[TContextName]; + context: Omit, + ...rest: HandlerParameters +) => Promise | Context[ContextName]; /** * A function that accepts a context object and an optional number of additional arguments. Used for the generic types @@ -142,7 +135,7 @@ export type HandlerParameters> = T extends ( * * @public */ -export interface IContextContainer> { +export interface IContextContainer { /** * Register a new context provider. * @@ -157,10 +150,10 @@ export interface IContextContainer> { * @param provider - A {@link IContextProvider} to be called each time a new context is created. * @returns The {@link IContextContainer} for method chaining. */ - registerContext>( + registerContext( pluginOpaqueId: PluginOpaqueId, - contextName: TContextName, - provider: IContextProvider + contextName: ContextName, + provider: IContextProvider ): this; /** @@ -178,21 +171,21 @@ export interface IContextContainer> { } /** @internal */ -export class ContextContainer> +export class ContextContainer implements IContextContainer { /** * Used to map contexts to their providers and associated plugin. In registration order which is tightly coupled to * plugin load order. */ private readonly contextProviders = new Map< - keyof HandlerContextType, + string, { - provider: IContextProvider>; + provider: IContextProvider; source: symbol; } >(); /** Used to keep track of which plugins registered which contexts for dependency resolution. */ - private readonly contextNamesBySource: Map>>; + private readonly contextNamesBySource: Map; /** * @param pluginDependencies - A map of plugins to an array of their dependencies. @@ -201,16 +194,18 @@ export class ContextContainer> private readonly pluginDependencies: ReadonlyMap, private readonly coreId: CoreId ) { - this.contextNamesBySource = new Map>>([ - [coreId, []], - ]); + this.contextNamesBySource = new Map([[coreId, []]]); } - public registerContext = >( + public registerContext = < + Context extends RequestHandlerContext, + ContextName extends keyof Context + >( source: symbol, - contextName: TContextName, - provider: IContextProvider + name: ContextName, + provider: IContextProvider ): this => { + const contextName = name as string; if (this.contextProviders.has(contextName)) { throw new Error(`Context provider for ${contextName} has already been registered.`); } @@ -234,6 +229,7 @@ export class ContextContainer> return (async (...args: HandlerParameters) => { const context = await this.buildContext(source, ...args); + // @ts-expect-error requires explicit handler arity return handler(context, ...args); }) as (...args: HandlerParameters) => ShallowPromise>; }; @@ -242,9 +238,7 @@ export class ContextContainer> source: symbol, ...contextArgs: HandlerParameters ): Promise> { - const contextsToBuild: ReadonlySet> = new Set( - this.getContextNamesForSource(source) - ); + const contextsToBuild = new Set(this.getContextNamesForSource(source)); return [...this.contextProviders] .sort(sortByCoreFirst(this.coreId)) @@ -256,18 +250,17 @@ export class ContextContainer> // registered that provider. const exposedContext = pick(resolvedContext, [ ...this.getContextNamesForSource(providerSource), - ]) as PartialExceptFor, 'core'>; + ]); return { ...resolvedContext, + // @ts-expect-error requires explicit provider arity [contextName]: await provider(exposedContext, ...contextArgs), }; }, Promise.resolve({}) as Promise>); } - private getContextNamesForSource( - source: symbol - ): ReadonlySet> { + private getContextNamesForSource(source: symbol): ReadonlySet { if (source === this.coreId) { return this.getContextNamesForCore(); } else { diff --git a/src/plugins/kibana_react/public/use_url_tracker/index.ts b/src/core/server/context/container/index.ts similarity index 87% rename from src/plugins/kibana_react/public/use_url_tracker/index.ts rename to src/core/server/context/container/index.ts index 7ba21ddafaef..b553c1b65223 100644 --- a/src/plugins/kibana_react/public/use_url_tracker/index.ts +++ b/src/core/server/context/container/index.ts @@ -6,4 +6,4 @@ * Public License, v 1. */ -export { useUrlTracker } from './use_url_tracker'; +export * from './context'; diff --git a/src/core/server/context/context_service.mock.ts b/src/core/server/context/context_service.mock.ts index 1ce1b26e3a4d..c849c0b6b697 100644 --- a/src/core/server/context/context_service.mock.ts +++ b/src/core/server/context/context_service.mock.ts @@ -9,7 +9,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { ContextService, ContextSetup } from './context_service'; -import { contextMock } from '../../utils/context.mock'; +import { contextMock } from './container/context.mock'; const createSetupContractMock = (mockContext = {}) => { const setupContract: jest.Mocked = { diff --git a/src/core/server/context/context_service.test.mocks.ts b/src/core/server/context/context_service.test.mocks.ts index 2b6fedb15c8c..7a69afb002c3 100644 --- a/src/core/server/context/context_service.test.mocks.ts +++ b/src/core/server/context/context_service.test.mocks.ts @@ -6,9 +6,9 @@ * Public License, v 1. */ -import { contextMock } from '../../utils/context.mock'; +import { contextMock } from './container/context.mock'; export const MockContextConstructor = jest.fn(contextMock.create); -jest.doMock('../../utils/context', () => ({ +jest.doMock('./container/context', () => ({ ContextContainer: MockContextConstructor, })); diff --git a/src/core/server/context/context_service.ts b/src/core/server/context/context_service.ts index 7a9ee4e3d35c..1f4bd7c2e3af 100644 --- a/src/core/server/context/context_service.ts +++ b/src/core/server/context/context_service.ts @@ -7,7 +7,7 @@ */ import { PluginOpaqueId } from '../../server'; -import { IContextContainer, ContextContainer, HandlerFunction } from '../../utils/context'; +import { IContextContainer, ContextContainer, HandlerFunction } from './container'; import { CoreContext } from '../core_context'; interface SetupDeps { diff --git a/src/core/server/context/index.ts b/src/core/server/context/index.ts index b9a8e0d7f498..8c690034368d 100644 --- a/src/core/server/context/index.ts +++ b/src/core/server/context/index.ts @@ -13,4 +13,4 @@ export { HandlerFunction, HandlerContextType, HandlerParameters, -} from '../../utils/context'; +} from './container'; diff --git a/src/core/server/core_usage_data/core_usage_stats_client.mock.ts b/src/core/server/core_usage_data/core_usage_stats_client.mock.ts index 8495f2e0d082..8a0aaa646438 100644 --- a/src/core/server/core_usage_data/core_usage_stats_client.mock.ts +++ b/src/core/server/core_usage_data/core_usage_stats_client.mock.ts @@ -18,6 +18,7 @@ const createUsageStatsClientMock = () => incrementSavedObjectsDelete: jest.fn().mockResolvedValue(null), incrementSavedObjectsFind: jest.fn().mockResolvedValue(null), incrementSavedObjectsGet: jest.fn().mockResolvedValue(null), + incrementSavedObjectsResolve: jest.fn().mockResolvedValue(null), incrementSavedObjectsUpdate: jest.fn().mockResolvedValue(null), incrementSavedObjectsImport: jest.fn().mockResolvedValue(null), incrementSavedObjectsResolveImportErrors: jest.fn().mockResolvedValue(null), diff --git a/src/core/server/core_usage_data/core_usage_stats_client.test.ts b/src/core/server/core_usage_data/core_usage_stats_client.test.ts index 0e43363dddb7..2067466c6351 100644 --- a/src/core/server/core_usage_data/core_usage_stats_client.test.ts +++ b/src/core/server/core_usage_data/core_usage_stats_client.test.ts @@ -20,6 +20,7 @@ import { DELETE_STATS_PREFIX, FIND_STATS_PREFIX, GET_STATS_PREFIX, + RESOLVE_STATS_PREFIX, UPDATE_STATS_PREFIX, IMPORT_STATS_PREFIX, RESOLVE_IMPORT_STATS_PREFIX, @@ -594,6 +595,81 @@ describe('CoreUsageStatsClient', () => { }); }); + describe('#incrementSavedObjectsResolve', () => { + it('does not throw an error if repository incrementCounter operation fails', async () => { + const { usageStatsClient, repositoryMock } = setup(); + repositoryMock.incrementCounter.mockRejectedValue(new Error('Oh no!')); + + const request = httpServerMock.createKibanaRequest(); + await expect( + usageStatsClient.incrementSavedObjectsResolve({ + request, + } as BaseIncrementOptions) + ).resolves.toBeUndefined(); + expect(repositoryMock.incrementCounter).toHaveBeenCalled(); + }); + + it('handles falsy options appropriately', async () => { + const { usageStatsClient, repositoryMock } = setup(); + + const request = httpServerMock.createKibanaRequest(); + await usageStatsClient.incrementSavedObjectsResolve({ + request, + } as BaseIncrementOptions); + expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1); + expect(repositoryMock.incrementCounter).toHaveBeenCalledWith( + CORE_USAGE_STATS_TYPE, + CORE_USAGE_STATS_ID, + [ + `${RESOLVE_STATS_PREFIX}.total`, + `${RESOLVE_STATS_PREFIX}.namespace.default.total`, + `${RESOLVE_STATS_PREFIX}.namespace.default.kibanaRequest.no`, + ], + incrementOptions + ); + }); + + it('handles truthy options and the default namespace string appropriately', async () => { + const { usageStatsClient, repositoryMock } = setup(DEFAULT_NAMESPACE_STRING); + + const request = httpServerMock.createKibanaRequest({ headers: firstPartyRequestHeaders }); + await usageStatsClient.incrementSavedObjectsResolve({ + request, + } as BaseIncrementOptions); + expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1); + expect(repositoryMock.incrementCounter).toHaveBeenCalledWith( + CORE_USAGE_STATS_TYPE, + CORE_USAGE_STATS_ID, + [ + `${RESOLVE_STATS_PREFIX}.total`, + `${RESOLVE_STATS_PREFIX}.namespace.default.total`, + `${RESOLVE_STATS_PREFIX}.namespace.default.kibanaRequest.yes`, + ], + incrementOptions + ); + }); + + it('handles a non-default space appropriately', async () => { + const { usageStatsClient, repositoryMock } = setup('foo'); + + const request = httpServerMock.createKibanaRequest(); + await usageStatsClient.incrementSavedObjectsResolve({ + request, + } as BaseIncrementOptions); + expect(repositoryMock.incrementCounter).toHaveBeenCalledTimes(1); + expect(repositoryMock.incrementCounter).toHaveBeenCalledWith( + CORE_USAGE_STATS_TYPE, + CORE_USAGE_STATS_ID, + [ + `${RESOLVE_STATS_PREFIX}.total`, + `${RESOLVE_STATS_PREFIX}.namespace.custom.total`, + `${RESOLVE_STATS_PREFIX}.namespace.custom.kibanaRequest.no`, + ], + incrementOptions + ); + }); + }); + describe('#incrementSavedObjectsUpdate', () => { it('does not throw an error if repository incrementCounter operation fails', async () => { const { usageStatsClient, repositoryMock } = setup(); diff --git a/src/core/server/core_usage_data/core_usage_stats_client.ts b/src/core/server/core_usage_data/core_usage_stats_client.ts index 103e98d2ef37..70bdb99f666f 100644 --- a/src/core/server/core_usage_data/core_usage_stats_client.ts +++ b/src/core/server/core_usage_data/core_usage_stats_client.ts @@ -40,6 +40,7 @@ export const CREATE_STATS_PREFIX = 'apiCalls.savedObjectsCreate'; export const DELETE_STATS_PREFIX = 'apiCalls.savedObjectsDelete'; export const FIND_STATS_PREFIX = 'apiCalls.savedObjectsFind'; export const GET_STATS_PREFIX = 'apiCalls.savedObjectsGet'; +export const RESOLVE_STATS_PREFIX = 'apiCalls.savedObjectsResolve'; export const UPDATE_STATS_PREFIX = 'apiCalls.savedObjectsUpdate'; export const IMPORT_STATS_PREFIX = 'apiCalls.savedObjectsImport'; export const RESOLVE_IMPORT_STATS_PREFIX = 'apiCalls.savedObjectsResolveImportErrors'; @@ -53,6 +54,7 @@ const ALL_COUNTER_FIELDS = [ ...getFieldsForCounter(DELETE_STATS_PREFIX), ...getFieldsForCounter(FIND_STATS_PREFIX), ...getFieldsForCounter(GET_STATS_PREFIX), + ...getFieldsForCounter(RESOLVE_STATS_PREFIX), ...getFieldsForCounter(UPDATE_STATS_PREFIX), // Saved Objects Management APIs ...getFieldsForCounter(IMPORT_STATS_PREFIX), @@ -123,6 +125,10 @@ export class CoreUsageStatsClient { await this.updateUsageStats([], GET_STATS_PREFIX, options); } + public async incrementSavedObjectsResolve(options: BaseIncrementOptions) { + await this.updateUsageStats([], RESOLVE_STATS_PREFIX, options); + } + public async incrementSavedObjectsUpdate(options: BaseIncrementOptions) { await this.updateUsageStats([], UPDATE_STATS_PREFIX, options); } diff --git a/src/core/server/core_usage_data/types.ts b/src/core/server/core_usage_data/types.ts index bd79e118c446..505dd8528e75 100644 --- a/src/core/server/core_usage_data/types.ts +++ b/src/core/server/core_usage_data/types.ts @@ -66,6 +66,13 @@ export interface CoreUsageStats { 'apiCalls.savedObjectsGet.namespace.custom.total'?: number; 'apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.yes'?: number; 'apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.no'?: number; + 'apiCalls.savedObjectsResolve.total'?: number; + 'apiCalls.savedObjectsResolve.namespace.default.total'?: number; + 'apiCalls.savedObjectsResolve.namespace.default.kibanaRequest.yes'?: number; + 'apiCalls.savedObjectsResolve.namespace.default.kibanaRequest.no'?: number; + 'apiCalls.savedObjectsResolve.namespace.custom.total'?: number; + 'apiCalls.savedObjectsResolve.namespace.custom.kibanaRequest.yes'?: number; + 'apiCalls.savedObjectsResolve.namespace.custom.kibanaRequest.no'?: number; 'apiCalls.savedObjectsUpdate.total'?: number; 'apiCalls.savedObjectsUpdate.namespace.default.total'?: number; 'apiCalls.savedObjectsUpdate.namespace.default.kibanaRequest.yes'?: number; diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index eb90783bb464..f9d6ef3cb38d 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -89,6 +89,7 @@ const createInternalSetupContractMock = () => { registerOnPreAuth: jest.fn(), registerAuth: jest.fn(), registerOnPostAuth: jest.fn(), + // @ts-expect-error tsc cannot infer ContextName and uses never registerRouteHandlerContext: jest.fn(), registerOnPreResponse: jest.fn(), createRouter: jest.fn().mockImplementation(() => mockRouter.create({})), @@ -125,6 +126,7 @@ const createSetupContractMock = () => { basePath: internalMock.basePath, csp: CspConfig.DEFAULT, createRouter: jest.fn(), + // @ts-expect-error tsc cannot infer ContextName and uses never registerRouteHandlerContext: jest.fn(), auth: { get: internalMock.auth.get, diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts index 0a3dfa12e1df..0e4ab8ec4cd6 100644 --- a/src/core/server/http/http_service.ts +++ b/src/core/server/http/http_service.ts @@ -11,6 +11,7 @@ import { first, map } from 'rxjs/operators'; import { Server } from '@hapi/hapi'; import { pick } from '@kbn/std'; +import type { RequestHandlerContext } from 'src/core/server'; import { CoreService } from '../../types'; import { Logger, LoggerFactory } from '../logging'; import { ContextSetup } from '../context'; @@ -31,7 +32,6 @@ import { InternalHttpServiceStart, } from './types'; -import { RequestHandlerContext } from '../../server'; import { registerCoreHandlers } from './lifecycle_handlers'; import { ExternalUrlConfigType, @@ -100,17 +100,23 @@ export class HttpService externalUrl: new ExternalUrlConfig(config.externalUrl), - createRouter: (path: string, pluginId: PluginOpaqueId = this.coreContext.coreId) => { + createRouter: ( + path: string, + pluginId: PluginOpaqueId = this.coreContext.coreId + ) => { const enhanceHandler = this.requestHandlerContext!.createHandler.bind(null, pluginId); - const router = new Router(path, this.log, enhanceHandler); + const router = new Router(path, this.log, enhanceHandler); registerRouter(router); return router; }, - registerRouteHandlerContext: ( + registerRouteHandlerContext: < + Context extends RequestHandlerContext, + ContextName extends keyof Context + >( pluginOpaqueId: PluginOpaqueId, - contextName: T, - provider: RequestHandlerContextProvider + contextName: ContextName, + provider: RequestHandlerContextProvider ) => this.requestHandlerContext!.registerContext(pluginOpaqueId, contextName, provider), }; diff --git a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts index 3fdc45210157..349758d9c391 100644 --- a/src/core/server/http/integration_tests/lifecycle_handlers.test.ts +++ b/src/core/server/http/integration_tests/lifecycle_handlers.test.ts @@ -175,19 +175,19 @@ describe('core lifecycle handlers', () => { }); destructiveMethods.forEach((method) => { - ((router as any)[method.toLowerCase()] as RouteRegistrar)( + ((router as any)[method.toLowerCase()] as RouteRegistrar)( { path: testPath, validate: false }, (context, req, res) => { return res.ok({ body: 'ok' }); } ); - ((router as any)[method.toLowerCase()] as RouteRegistrar)( + ((router as any)[method.toLowerCase()] as RouteRegistrar)( { path: allowlistedTestPath, validate: false }, (context, req, res) => { return res.ok({ body: 'ok' }); } ); - ((router as any)[method.toLowerCase()] as RouteRegistrar)( + ((router as any)[method.toLowerCase()] as RouteRegistrar)( { path: xsrfDisabledTestPath, validate: false, options: { xsrfRequired: false } }, (context, req, res) => { return res.ok({ body: 'ok' }); diff --git a/src/core/server/http/router/router.mock.ts b/src/core/server/http/router/router.mock.ts index ee515a3ffddf..b3ff8ce983ab 100644 --- a/src/core/server/http/router/router.mock.ts +++ b/src/core/server/http/router/router.mock.ts @@ -8,7 +8,7 @@ import { IRouter } from './router'; -export type RouterMock = jest.Mocked; +export type RouterMock = jest.Mocked>; function create({ routerPath = '' }: { routerPath?: string } = {}): RouterMock { return { diff --git a/src/core/server/http/router/router.ts b/src/core/server/http/router/router.ts index 4a5db793b0b0..1368714aa993 100644 --- a/src/core/server/http/router/router.ts +++ b/src/core/server/http/router/router.ts @@ -44,9 +44,12 @@ interface RouterRoute { * * @public */ -export type RouteRegistrar = ( +export type RouteRegistrar< + Method extends RouteMethod, + Context extends RequestHandlerContext = RequestHandlerContext +> = ( route: RouteConfig, - handler: RequestHandler + handler: RequestHandler ) => void; /** @@ -55,7 +58,7 @@ export type RouteRegistrar = ( * * @public */ -export interface IRouter { +export interface IRouter { /** * Resulted path */ @@ -66,35 +69,35 @@ export interface IRouter { * @param route {@link RouteConfig} - a route configuration. * @param handler {@link RequestHandler} - a function to call to respond to an incoming request */ - get: RouteRegistrar<'get'>; + get: RouteRegistrar<'get', Context>; /** * Register a route handler for `POST` request. * @param route {@link RouteConfig} - a route configuration. * @param handler {@link RequestHandler} - a function to call to respond to an incoming request */ - post: RouteRegistrar<'post'>; + post: RouteRegistrar<'post', Context>; /** * Register a route handler for `PUT` request. * @param route {@link RouteConfig} - a route configuration. * @param handler {@link RequestHandler} - a function to call to respond to an incoming request */ - put: RouteRegistrar<'put'>; + put: RouteRegistrar<'put', Context>; /** * Register a route handler for `PATCH` request. * @param route {@link RouteConfig} - a route configuration. * @param handler {@link RequestHandler} - a function to call to respond to an incoming request */ - patch: RouteRegistrar<'patch'>; + patch: RouteRegistrar<'patch', Context>; /** * Register a route handler for `DELETE` request. * @param route {@link RouteConfig} - a route configuration. * @param handler {@link RequestHandler} - a function to call to respond to an incoming request */ - delete: RouteRegistrar<'delete'>; + delete: RouteRegistrar<'delete', Context>; /** * Wrap a router handler to catch and converts legacy boom errors to proper custom errors. @@ -110,9 +113,13 @@ export interface IRouter { getRoutes: () => RouterRoute[]; } -export type ContextEnhancer = ( - handler: RequestHandler -) => RequestHandlerEnhanced; +export type ContextEnhancer< + P, + Q, + B, + Method extends RouteMethod, + Context extends RequestHandlerContext +> = (handler: RequestHandler) => RequestHandlerEnhanced; function getRouteFullPath(routerPath: string, routePath: string) { // If router's path ends with slash and route's path starts with slash, @@ -195,22 +202,23 @@ function validOptions( /** * @internal */ -export class Router implements IRouter { +export class Router + implements IRouter { public routes: Array> = []; - public get: IRouter['get']; - public post: IRouter['post']; - public delete: IRouter['delete']; - public put: IRouter['put']; - public patch: IRouter['patch']; + public get: IRouter['get']; + public post: IRouter['post']; + public delete: IRouter['delete']; + public put: IRouter['put']; + public patch: IRouter['patch']; constructor( public readonly routerPath: string, private readonly log: Logger, - private readonly enhanceWithContext: ContextEnhancer + private readonly enhanceWithContext: ContextEnhancer ) { const buildMethod = (method: Method) => ( route: RouteConfig, - handler: RequestHandler + handler: RequestHandler ) => { const routeSchemas = routeSchemasFromRouteConfig(route, method); @@ -300,7 +308,7 @@ type WithoutHeadArgument = T extends (first: any, ...rest: infer Params) => i : never; type RequestHandlerEnhanced = WithoutHeadArgument< - RequestHandler + RequestHandler >; /** @@ -341,10 +349,11 @@ export type RequestHandler< P = unknown, Q = unknown, B = unknown, + Context extends RequestHandlerContext = RequestHandlerContext, Method extends RouteMethod = any, ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory > = ( - context: RequestHandlerContext, + context: Context, request: KibanaRequest, response: ResponseFactory ) => IKibanaResponse | Promise>; @@ -366,8 +375,9 @@ export type RequestHandlerWrapper = < P, Q, B, + Context extends RequestHandlerContext = RequestHandlerContext, Method extends RouteMethod = any, ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory >( - handler: RequestHandler -) => RequestHandler; + handler: RequestHandler +) => RequestHandler; diff --git a/src/core/server/http/types.ts b/src/core/server/http/types.ts index fdcc7a87082a..262ac3eb49f9 100644 --- a/src/core/server/http/types.ts +++ b/src/core/server/http/types.ts @@ -21,13 +21,13 @@ import { OnPostAuthHandler } from './lifecycle/on_post_auth'; import { OnPreResponseHandler } from './lifecycle/on_pre_response'; import { IBasePath } from './base_path_service'; import { ExternalUrlConfig } from '../external_url'; -import { PluginOpaqueId, RequestHandlerContext } from '..'; +import type { PluginOpaqueId, RequestHandlerContext } from '..'; /** * An object that handles registration of http request context providers. * @public */ -export type RequestHandlerContextContainer = IContextContainer>; +export type RequestHandlerContextContainer = IContextContainer; /** * Context provider for request handler. @@ -36,8 +36,9 @@ export type RequestHandlerContextContainer = IContextContainer = IContextProvider, TContextName>; + Context extends RequestHandlerContext, + ContextName extends keyof Context +> = IContextProvider; /** * @public @@ -230,14 +231,19 @@ export interface HttpServiceSetup { * ``` * @public */ - createRouter: () => IRouter; + createRouter: < + Context extends RequestHandlerContext = RequestHandlerContext + >() => IRouter; /** * Register a context provider for a route handler. * @example * ```ts * // my-plugin.ts - * deps.http.registerRouteHandlerContext( + * interface MyRequestHandlerContext extends RequestHandlerContext { + * myApp: { search(id: string): Promise }; + * } + * deps.http.registerRouteHandlerContext( * 'myApp', * (context, req) => { * async function search (id: string) { @@ -248,6 +254,8 @@ export interface HttpServiceSetup { * ); * * // my-route-handler.ts + * import type { MyRequestHandlerContext } from './my-plugin.ts'; + * const router = createRouter(); * router.get({ path: '/', validate: false }, async (context, req, res) => { * const response = await context.myApp.search(...); * return res.ok(response); @@ -255,9 +263,12 @@ export interface HttpServiceSetup { * ``` * @public */ - registerRouteHandlerContext: ( - contextName: T, - provider: RequestHandlerContextProvider + registerRouteHandlerContext: < + Context extends RequestHandlerContext, + ContextName extends keyof Context + >( + contextName: ContextName, + provider: RequestHandlerContextProvider ) => RequestHandlerContextContainer; /** @@ -272,13 +283,19 @@ export interface InternalHttpServiceSetup auth: HttpServerSetup['auth']; server: HttpServerSetup['server']; externalUrl: ExternalUrlConfig; - createRouter: (path: string, plugin?: PluginOpaqueId) => IRouter; + createRouter: ( + path: string, + plugin?: PluginOpaqueId + ) => IRouter; registerStaticDir: (path: string, dirPath: string) => void; getAuthHeaders: GetAuthHeaders; - registerRouteHandlerContext: ( + registerRouteHandlerContext: < + Context extends RequestHandlerContext, + ContextName extends keyof Context + >( pluginOpaqueId: PluginOpaqueId, - contextName: T, - provider: RequestHandlerContextProvider + contextName: ContextName, + provider: RequestHandlerContextProvider ) => RequestHandlerContextContainer; } diff --git a/src/core/server/http_resources/http_resources_service.ts b/src/core/server/http_resources/http_resources_service.ts index b8e458016442..916fef262438 100644 --- a/src/core/server/http_resources/http_resources_service.ts +++ b/src/core/server/http_resources/http_resources_service.ts @@ -53,12 +53,12 @@ export class HttpResourcesService implements CoreService( + register: ( route: RouteConfig, - handler: HttpResourcesRequestHandler + handler: HttpResourcesRequestHandler ) => { return router.get(route, (context, request, response) => { - return handler(context, request, { + return handler(context as Context, request, { ...response, ...this.createResponseToolkit(deps, context, request, response), }); diff --git a/src/core/server/http_resources/types.ts b/src/core/server/http_resources/types.ts index 6b1374d2d0be..152bdd18f021 100644 --- a/src/core/server/http_resources/types.ts +++ b/src/core/server/http_resources/types.ts @@ -6,7 +6,8 @@ * Public License, v 1. */ -import { +import type { RequestHandlerContext } from 'src/core/server'; +import type { IRouter, RouteConfig, IKibanaResponse, @@ -72,13 +73,12 @@ export interface HttpResourcesServiceToolkit { * }); * @public */ -export type HttpResourcesRequestHandler

= RequestHandler< - P, - Q, - B, - 'get', - KibanaResponseFactory & HttpResourcesServiceToolkit ->; +export type HttpResourcesRequestHandler< + P = unknown, + Q = unknown, + B = unknown, + Context extends RequestHandlerContext = RequestHandlerContext +> = RequestHandler; /** * Allows to configure HTTP response parameters @@ -98,8 +98,8 @@ export interface InternalHttpResourcesSetup { */ export interface HttpResources { /** To register a route handler executing passed function to form response. */ - register: ( + register: ( route: RouteConfig, - handler: HttpResourcesRequestHandler + handler: HttpResourcesRequestHandler ) => void; } diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 187c2afd1703..af6d511a5877 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -277,10 +277,12 @@ export { SavedObjectMigrationContext, SavedObjectsMigrationLogger, SavedObjectsRawDoc, + SavedObjectsRawDocParseOptions, SavedObjectSanitizedDoc, SavedObjectUnsanitizedDoc, SavedObjectsRepositoryFactory, SavedObjectsResolveImportErrorsOptions, + SavedObjectsResolveResponse, SavedObjectsSerializer, SavedObjectsUpdateOptions, SavedObjectsUpdateResponse, @@ -318,9 +320,16 @@ export { SavedObjectsExportByObjectOptions, SavedObjectsExportByTypeOptions, SavedObjectsExportError, + SavedObjectsExportTransform, + SavedObjectsExportTransformContext, SavedObjectsImporter, ISavedObjectsImporter, SavedObjectsImportError, + SavedObjectsImportHook, + SavedObjectsImportHookResult, + SavedObjectsImportSimpleWarning, + SavedObjectsImportActionRequiredWarning, + SavedObjectsImportWarning, } from './saved_objects'; export { diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index ea68c3245064..02b2d0bdf073 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -11,6 +11,7 @@ import { first, map, publishReplay, tap } from 'rxjs/operators'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { PathConfigType } from '@kbn/utils'; +import type { RequestHandlerContext } from 'src/core/server'; // @ts-expect-error legacy config class import { Config as LegacyConfigClass } from '../../../legacy/server/config'; import { CoreService } from '../../types'; @@ -18,7 +19,14 @@ import { Config } from '../config'; import { CoreContext } from '../core_context'; import { CspConfigType, config as cspConfig } from '../csp'; import { DevConfig, DevConfigType, config as devConfig } from '../dev'; -import { BasePathProxyServer, HttpConfig, HttpConfigType, config as httpConfig } from '../http'; +import { + BasePathProxyServer, + HttpConfig, + HttpConfigType, + config as httpConfig, + IRouter, + RequestHandlerContextProvider, +} from '../http'; import { Logger } from '../logging'; import { LegacyServiceSetupDeps, LegacyServiceStartDeps, LegacyConfig, LegacyVars } from './types'; import { ExternalUrlConfigType, config as externalUrlConfig } from '../external_url'; @@ -225,11 +233,15 @@ export class LegacyService implements CoreService { }, http: { createCookieSessionStorageFactory: setupDeps.core.http.createCookieSessionStorageFactory, - registerRouteHandlerContext: setupDeps.core.http.registerRouteHandlerContext.bind( - null, - this.legacyId - ), - createRouter: () => router, + registerRouteHandlerContext: < + Context extends RequestHandlerContext, + ContextName extends keyof Context + >( + contextName: ContextName, + provider: RequestHandlerContextProvider + ) => setupDeps.core.http.registerRouteHandlerContext(this.legacyId, contextName, provider), + createRouter: () => + router as IRouter, resources: setupDeps.core.httpResources.createRegistrar(router), registerOnPreRouting: setupDeps.core.http.registerOnPreRouting, registerOnPreAuth: setupDeps.core.http.registerOnPreAuth, diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 1731996b7745..5b0e2ee21a88 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -10,6 +10,7 @@ import { map, shareReplay } from 'rxjs/operators'; import { combineLatest } from 'rxjs'; import { PathConfigType, config as pathConfig } from '@kbn/utils'; import { pick, deepFreeze } from '@kbn/std'; +import type { RequestHandlerContext } from 'src/core/server'; import { CoreContext } from '../core_context'; import { PluginWrapper } from './plugin'; import { PluginsServiceSetupDeps, PluginsServiceStartDeps } from './plugins_service'; @@ -24,6 +25,7 @@ import { ElasticsearchConfigType, config as elasticsearchConfig, } from '../elasticsearch/elasticsearch_config'; +import { IRouter, RequestHandlerContextProvider } from '../http'; import { SavedObjectsConfigType, savedObjectsConfig } from '../saved_objects/saved_objects_config'; import { CoreSetup, CoreStart } from '..'; @@ -149,11 +151,15 @@ export function createPluginSetupContext( }, http: { createCookieSessionStorageFactory: deps.http.createCookieSessionStorageFactory, - registerRouteHandlerContext: deps.http.registerRouteHandlerContext.bind( - null, - plugin.opaqueId - ), - createRouter: () => router, + registerRouteHandlerContext: < + Context extends RequestHandlerContext, + ContextName extends keyof Context + >( + contextName: ContextName, + provider: RequestHandlerContextProvider + ) => deps.http.registerRouteHandlerContext(plugin.opaqueId, contextName, provider), + createRouter: () => + router as IRouter, resources: deps.httpResources.createRegistrar(router), registerOnPreRouting: deps.http.registerOnPreRouting, registerOnPreAuth: deps.http.registerOnPreAuth, diff --git a/src/core/server/saved_objects/export/apply_export_transforms.test.ts b/src/core/server/saved_objects/export/apply_export_transforms.test.ts new file mode 100644 index 000000000000..b1d0a3516252 --- /dev/null +++ b/src/core/server/saved_objects/export/apply_export_transforms.test.ts @@ -0,0 +1,300 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { SavedObject } from '../../../types'; +import { KibanaRequest } from '../../http'; +import { httpServerMock } from '../../http/http_server.mocks'; +import { applyExportTransforms } from './apply_export_transforms'; +import { SavedObjectsExportTransform } from './types'; + +const createObj = ( + type: string, + id: string, + attributes: Record = {} +): SavedObject => ({ + type, + id, + attributes, + references: [], +}); + +const createTransform = ( + implementation: SavedObjectsExportTransform = (ctx, objs) => objs +): jest.MockedFunction => jest.fn(implementation); + +const expectedContext = { + request: expect.any(KibanaRequest), +}; + +describe('applyExportTransforms', () => { + let request: ReturnType; + + beforeEach(() => { + request = httpServerMock.createKibanaRequest(); + }); + + it('calls the transform functions with the correct parameters', async () => { + const foo1 = createObj('foo', '1'); + const foo2 = createObj('foo', '2'); + const bar1 = createObj('bar', '1'); + + const fooTransform = createTransform(); + const barTransform = createTransform(); + + await applyExportTransforms({ + request, + objects: [foo1, bar1, foo2], + transforms: { + foo: fooTransform, + bar: barTransform, + }, + }); + + expect(fooTransform).toHaveBeenCalledTimes(1); + expect(fooTransform).toHaveBeenCalledWith(expectedContext, [foo1, foo2]); + + expect(barTransform).toHaveBeenCalledTimes(1); + expect(barTransform).toHaveBeenCalledWith(expectedContext, [bar1]); + }); + + it('does not call the transform functions if no objects are present', async () => { + const foo1 = createObj('foo', '1'); + + const fooTransform = createTransform(); + const barTransform = createTransform(); + + await applyExportTransforms({ + request, + objects: [foo1], + transforms: { + foo: fooTransform, + bar: barTransform, + }, + }); + + expect(fooTransform).toHaveBeenCalledTimes(1); + expect(fooTransform).toHaveBeenCalledWith(expectedContext, [foo1]); + + expect(barTransform).not.toHaveBeenCalled(); + }); + + it('allows to add objects to the export', async () => { + const foo1 = createObj('foo', '1'); + const foo2 = createObj('foo', '2'); + const bar1 = createObj('bar', '1'); + const dolly1 = createObj('dolly', '1'); + const hello1 = createObj('hello', '1'); + + const fooTransform = createTransform((ctx, objs) => { + return [...objs, dolly1]; + }); + const barTransform = createTransform((ctx, objs) => { + return [...objs, hello1]; + }); + + const result = await applyExportTransforms({ + request, + objects: [foo1, bar1, foo2], + transforms: { + foo: fooTransform, + bar: barTransform, + }, + }); + + expect(result).toEqual([foo1, foo2, dolly1, bar1, hello1]); + }); + + it('returns unmutated objects if no transform is defined for the type', async () => { + const foo1 = createObj('foo', '1'); + const foo2 = createObj('foo', '2'); + const bar1 = createObj('bar', '1'); + const bar2 = createObj('bar', '2'); + const dolly1 = createObj('dolly', '1'); + + const fooTransform = createTransform((ctx, objs) => { + return [...objs, dolly1]; + }); + + const result = await applyExportTransforms({ + request, + objects: [foo1, foo2, bar1, bar2], + transforms: { + foo: fooTransform, + }, + }); + + expect(result).toEqual([foo1, foo2, dolly1, bar1, bar2]); + }); + + it('allows to mutate objects', async () => { + const foo1 = createObj('foo', '1', { enabled: true }); + const foo2 = createObj('foo', '2', { enabled: true }); + + const disableFoo = (obj: SavedObject) => ({ + ...obj, + attributes: { + ...obj.attributes, + enabled: false, + }, + }); + + const fooTransform = createTransform((ctx, objs) => { + return objs.map(disableFoo); + }); + + const result = await applyExportTransforms({ + request, + objects: [foo1, foo2], + transforms: { + foo: fooTransform, + }, + }); + + expect(result).toEqual([foo1, foo2].map(disableFoo)); + }); + + it('supports async transforms', async () => { + const foo1 = createObj('foo', '1'); + const bar1 = createObj('bar', '1'); + const dolly1 = createObj('dolly', '1'); + const hello1 = createObj('hello', '1'); + + const fooTransform = createTransform((ctx, objs) => { + return Promise.resolve([...objs, dolly1]); + }); + + const barTransform = createTransform((ctx, objs) => { + return [...objs, hello1]; + }); + + const result = await applyExportTransforms({ + request, + objects: [foo1, bar1], + transforms: { + foo: fooTransform, + bar: barTransform, + }, + }); + + expect(result).toEqual([foo1, dolly1, bar1, hello1]); + }); + + it('uses the provided sortFunction when provided', async () => { + const foo1 = createObj('foo', 'A'); + const bar1 = createObj('bar', 'B'); + const dolly1 = createObj('dolly', 'C'); + const hello1 = createObj('hello', 'D'); + + const fooTransform = createTransform((ctx, objs) => { + return [...objs, dolly1]; + }); + + const barTransform = createTransform((ctx, objs) => { + return [...objs, hello1]; + }); + + const result = await applyExportTransforms({ + request, + objects: [foo1, bar1], + transforms: { + foo: fooTransform, + bar: barTransform, + }, + sortFunction: (obj1, obj2) => (obj1.id > obj2.id ? 1 : -1), + }); + + expect(result).toEqual([foo1, bar1, dolly1, hello1]); + }); + + it('throws when removing objects', async () => { + const foo1 = createObj('foo', '1', { enabled: true }); + const foo2 = createObj('foo', '2', { enabled: true }); + + const fooTransform = createTransform((ctx, objs) => { + return [objs[0]]; + }); + + await expect( + applyExportTransforms({ + request, + objects: [foo1, foo2], + transforms: { + foo: fooTransform, + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid transform performed on objects to export"` + ); + }); + + it('throws when changing the object type', async () => { + const foo1 = createObj('foo', '1', { enabled: true }); + const foo2 = createObj('foo', '2', { enabled: true }); + + const fooTransform = createTransform((ctx, objs) => { + return objs.map((obj) => ({ + ...obj, + type: 'mutated', + })); + }); + + await expect( + applyExportTransforms({ + request, + objects: [foo1, foo2], + transforms: { + foo: fooTransform, + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid transform performed on objects to export"` + ); + }); + + it('throws when changing the object id', async () => { + const foo1 = createObj('foo', '1', { enabled: true }); + const foo2 = createObj('foo', '2', { enabled: true }); + + const fooTransform = createTransform((ctx, objs) => { + return objs.map((obj, idx) => ({ + ...obj, + id: `mutated-${idx}`, + })); + }); + + await expect( + applyExportTransforms({ + request, + objects: [foo1, foo2], + transforms: { + foo: fooTransform, + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid transform performed on objects to export"` + ); + }); + + it('throws if the transform function throws', async () => { + const foo1 = createObj('foo', '1'); + + const fooTransform = createTransform(() => { + throw new Error('oups.'); + }); + + await expect( + applyExportTransforms({ + request, + objects: [foo1], + transforms: { + foo: fooTransform, + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Error transforming objects to export"`); + }); +}); diff --git a/src/core/server/saved_objects/export/apply_export_transforms.ts b/src/core/server/saved_objects/export/apply_export_transforms.ts new file mode 100644 index 000000000000..0297fe201ef6 --- /dev/null +++ b/src/core/server/saved_objects/export/apply_export_transforms.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { SavedObject } from '../../../types'; +import { KibanaRequest } from '../../http'; +import { SavedObjectsExportError } from './errors'; +import { SavedObjectsExportTransform, SavedObjectsExportTransformContext } from './types'; +import { getObjKey, SavedObjectComparator } from './utils'; + +interface ApplyExportTransformsOptions { + objects: SavedObject[]; + request: KibanaRequest; + transforms: Record; + sortFunction?: SavedObjectComparator; +} + +export const applyExportTransforms = async ({ + objects, + request, + transforms, + sortFunction, +}: ApplyExportTransformsOptions): Promise => { + const context = createContext(request); + const byType = splitByType(objects); + + let finalObjects: SavedObject[] = []; + for (const [type, typeObjs] of Object.entries(byType)) { + const typeTransformFn = transforms[type]; + if (typeTransformFn) { + finalObjects = [ + ...finalObjects, + ...(await applyTransform(typeObjs, typeTransformFn, context)), + ]; + } else { + finalObjects = [...finalObjects, ...typeObjs]; + } + } + + if (sortFunction) { + finalObjects.sort(sortFunction); + } + + return finalObjects; +}; + +const applyTransform = async ( + objs: SavedObject[], + transformFn: SavedObjectsExportTransform, + context: SavedObjectsExportTransformContext +) => { + const objKeys = objs.map(getObjKey); + let transformedObjects: SavedObject[]; + try { + transformedObjects = await transformFn(context, objs); + } catch (e) { + throw SavedObjectsExportError.objectTransformError(objs, e); + } + assertValidTransform(transformedObjects, objKeys); + return transformedObjects; +}; + +const createContext = (request: KibanaRequest): SavedObjectsExportTransformContext => { + return { + request, + }; +}; + +const splitByType = (objects: SavedObject[]): Record => { + return objects.reduce((memo, obj) => { + memo[obj.type] = [...(memo[obj.type] ?? []), obj]; + return memo; + }, {} as Record); +}; + +const assertValidTransform = (transformedObjects: SavedObject[], initialKeys: string[]) => { + const transformedKeys = transformedObjects.map(getObjKey); + const missingKeys: string[] = []; + initialKeys.forEach((initialKey) => { + if (!transformedKeys.includes(initialKey)) { + missingKeys.push(initialKey); + } + }); + if (missingKeys.length) { + throw SavedObjectsExportError.invalidTransformError(missingKeys); + } +}; diff --git a/src/core/server/saved_objects/export/errors.ts b/src/core/server/saved_objects/export/errors.ts index 96a729846f6b..5720f3b2daf3 100644 --- a/src/core/server/saved_objects/export/errors.ts +++ b/src/core/server/saved_objects/export/errors.ts @@ -36,4 +36,32 @@ export class SavedObjectsExportError extends Error { objects, }); } + + /** + * Error returned when a {@link SavedObjectsExportTransform | export tranform} threw an error + */ + static objectTransformError(objects: SavedObject[], cause: Error) { + return new SavedObjectsExportError( + 'object-transform-error', + 'Error transforming objects to export', + { + objects, + cause: cause.message, + } + ); + } + + /** + * Error returned when a {@link SavedObjectsExportTransform | export tranform} performed an invalid operation + * during the transform, such as removing objects from the export, or changing an object's type or id. + */ + static invalidTransformError(objectKeys: string[]) { + return new SavedObjectsExportError( + 'invalid-transform-error', + 'Invalid transform performed on objects to export', + { + objectKeys, + } + ); + } } diff --git a/src/core/server/saved_objects/export/index.ts b/src/core/server/saved_objects/export/index.ts index 386b8c208ad6..8ac2e68819c4 100644 --- a/src/core/server/saved_objects/export/index.ts +++ b/src/core/server/saved_objects/export/index.ts @@ -11,6 +11,8 @@ export { SavedObjectExportBaseOptions, SavedObjectsExportByTypeOptions, SavedObjectsExportResultDetails, + SavedObjectsExportTransformContext, + SavedObjectsExportTransform, } from './types'; export { ISavedObjectsExporter, SavedObjectsExporter } from './saved_objects_exporter'; export { SavedObjectsExportError } from './errors'; diff --git a/src/core/server/saved_objects/export/saved_objects_exporter.test.ts b/src/core/server/saved_objects/export/saved_objects_exporter.test.ts index e7d37280762f..346f14cbeb07 100644 --- a/src/core/server/saved_objects/export/saved_objects_exporter.test.ts +++ b/src/core/server/saved_objects/export/saved_objects_exporter.test.ts @@ -6,24 +6,30 @@ * Public License, v 1. */ +import type { SavedObject } from '../../../types'; import { SavedObjectsExporter } from './saved_objects_exporter'; import { savedObjectsClientMock } from '../service/saved_objects_client.mock'; +import { SavedObjectTypeRegistry } from '../saved_objects_type_registry'; +import { httpServerMock } from '../../http/http_server.mocks'; import { Readable } from 'stream'; import { createPromiseFromStreams, createConcatStream } from '@kbn/utils'; -async function readStreamToCompletion(stream: Readable) { +async function readStreamToCompletion(stream: Readable): Promise>> { return createPromiseFromStreams([stream, createConcatStream([])]); } const exportSizeLimit = 500; +const request = httpServerMock.createKibanaRequest(); describe('getSortedObjectsForExport()', () => { let savedObjectsClient: ReturnType; + let typeRegistry: SavedObjectTypeRegistry; let exporter: SavedObjectsExporter; beforeEach(() => { + typeRegistry = new SavedObjectTypeRegistry(); savedObjectsClient = savedObjectsClientMock.create(); - exporter = new SavedObjectsExporter({ savedObjectsClient, exportSizeLimit }); + exporter = new SavedObjectsExporter({ savedObjectsClient, exportSizeLimit, typeRegistry }); }); describe('#exportByTypes', () => { @@ -56,6 +62,7 @@ describe('getSortedObjectsForExport()', () => { page: 0, }); const exportStream = await exporter.exportByTypes({ + request, types: ['index-pattern', 'search'], }); @@ -115,6 +122,52 @@ describe('getSortedObjectsForExport()', () => { `); }); + test('applies the export transforms', async () => { + typeRegistry.registerType({ + name: 'foo', + mappings: { properties: {} }, + namespaceType: 'single', + hidden: false, + management: { + importableAndExportable: true, + onExport: (ctx, objects) => { + objects.forEach((obj: SavedObject) => { + obj.attributes.foo = 'modified'; + }); + return objects; + }, + }, + }); + exporter = new SavedObjectsExporter({ savedObjectsClient, exportSizeLimit, typeRegistry }); + + savedObjectsClient.find.mockResolvedValueOnce({ + total: 1, + saved_objects: [ + { + id: '1', + type: 'foo', + attributes: { + foo: 'initial', + }, + score: 0, + references: [], + }, + ], + per_page: 1, + page: 0, + }); + const exportStream = await exporter.exportByTypes({ + request, + types: ['foo'], + excludeExportDetails: true, + }); + + const response = await readStreamToCompletion(exportStream); + + expect(response).toHaveLength(1); + expect(response[0].attributes.foo).toEqual('modified'); + }); + test('omits the `namespaces` property from the export', async () => { savedObjectsClient.find.mockResolvedValueOnce({ total: 2, @@ -146,6 +199,7 @@ describe('getSortedObjectsForExport()', () => { page: 0, }); const exportStream = await exporter.exportByTypes({ + request, types: ['index-pattern', 'search'], }); @@ -234,6 +288,7 @@ describe('getSortedObjectsForExport()', () => { page: 0, }); const exportStream = await exporter.exportByTypes({ + request, types: ['index-pattern', 'search'], excludeExportDetails: true, }); @@ -293,6 +348,7 @@ describe('getSortedObjectsForExport()', () => { page: 0, }); const exportStream = await exporter.exportByTypes({ + request, types: ['index-pattern', 'search'], search: 'foo', }); @@ -375,6 +431,7 @@ describe('getSortedObjectsForExport()', () => { page: 0, }); const exportStream = await exporter.exportByTypes({ + request, types: ['index-pattern', 'search'], hasReference: [ { @@ -468,6 +525,7 @@ describe('getSortedObjectsForExport()', () => { page: 0, }); const exportStream = await exporter.exportByTypes({ + request, types: ['index-pattern', 'search'], namespace: 'foo', }); @@ -531,7 +589,7 @@ describe('getSortedObjectsForExport()', () => { }); test('export selected types throws error when exceeding exportSizeLimit', async () => { - exporter = new SavedObjectsExporter({ savedObjectsClient, exportSizeLimit: 1 }); + exporter = new SavedObjectsExporter({ savedObjectsClient, exportSizeLimit: 1, typeRegistry }); savedObjectsClient.find.mockResolvedValueOnce({ total: 2, @@ -562,6 +620,7 @@ describe('getSortedObjectsForExport()', () => { }); await expect( exporter.exportByTypes({ + request, types: ['index-pattern', 'search'], }) ).rejects.toThrowErrorMatchingInlineSnapshot(`"Can't export more than 1 objects"`); @@ -603,6 +662,7 @@ describe('getSortedObjectsForExport()', () => { ], }); const exportStream = await exporter.exportByTypes({ + request, types: ['index-pattern'], }); const response = await readStreamToCompletion(exportStream); @@ -667,6 +727,7 @@ describe('getSortedObjectsForExport()', () => { ], }); const exportStream = await exporter.exportByObjects({ + request, objects: [ { type: 'index-pattern', @@ -759,6 +820,7 @@ describe('getSortedObjectsForExport()', () => { }); await expect( exporter.exportByObjects({ + request, objects: [ { type: 'index-pattern', @@ -774,9 +836,10 @@ describe('getSortedObjectsForExport()', () => { }); test('export selected objects throws error when exceeding exportSizeLimit', async () => { - exporter = new SavedObjectsExporter({ savedObjectsClient, exportSizeLimit: 1 }); + exporter = new SavedObjectsExporter({ savedObjectsClient, exportSizeLimit: 1, typeRegistry }); const exportOpts = { + request, objects: [ { type: 'index-pattern', @@ -803,6 +866,7 @@ describe('getSortedObjectsForExport()', () => { ], }); const exportStream = await exporter.exportByObjects({ + request, objects: [ { type: 'multi', id: '1' }, { type: 'multi', id: '2' }, @@ -846,6 +910,7 @@ describe('getSortedObjectsForExport()', () => { ], }); const exportStream = await exporter.exportByObjects({ + request, objects: [ { type: 'search', diff --git a/src/core/server/saved_objects/export/saved_objects_exporter.ts b/src/core/server/saved_objects/export/saved_objects_exporter.ts index 7588f13a5764..bd3e60fc1a14 100644 --- a/src/core/server/saved_objects/export/saved_objects_exporter.ts +++ b/src/core/server/saved_objects/export/saved_objects_exporter.ts @@ -9,6 +9,7 @@ import { createListStream } from '@kbn/utils'; import { PublicMethodsOf } from '@kbn/utility-types'; import { SavedObject, SavedObjectsClientContract } from '../types'; +import { ISavedObjectTypeRegistry } from '../saved_objects_type_registry'; import { fetchNestedDependencies } from './fetch_nested_dependencies'; import { sortObjects } from './sort_objects'; import { @@ -16,8 +17,11 @@ import { SavedObjectExportBaseOptions, SavedObjectsExportByObjectOptions, SavedObjectsExportByTypeOptions, + SavedObjectsExportTransform, } from './types'; import { SavedObjectsExportError } from './errors'; +import { applyExportTransforms } from './apply_export_transforms'; +import { byIdAscComparator, getPreservedOrderComparator, SavedObjectComparator } from './utils'; /** * @public @@ -29,17 +33,29 @@ export type ISavedObjectsExporter = PublicMethodsOf; */ export class SavedObjectsExporter { readonly #savedObjectsClient: SavedObjectsClientContract; + readonly #exportTransforms: Record; readonly #exportSizeLimit: number; constructor({ savedObjectsClient, + typeRegistry, exportSizeLimit, }: { savedObjectsClient: SavedObjectsClientContract; + typeRegistry: ISavedObjectTypeRegistry; exportSizeLimit: number; }) { this.#savedObjectsClient = savedObjectsClient; this.#exportSizeLimit = exportSizeLimit; + this.#exportTransforms = typeRegistry.getAllTypes().reduce((transforms, type) => { + if (type.management?.onExport) { + return { + ...transforms, + [type.name]: type.management.onExport, + }; + } + return transforms; + }, {} as Record); } /** @@ -51,7 +67,8 @@ export class SavedObjectsExporter { */ public async exportByTypes(options: SavedObjectsExportByTypeOptions) { const objects = await this.fetchByTypes(options); - return this.processObjects(objects, { + return this.processObjects(objects, byIdAscComparator, { + request: options.request, includeReferencesDeep: options.includeReferencesDeep, excludeExportDetails: options.excludeExportDetails, namespace: options.namespace, @@ -70,7 +87,9 @@ export class SavedObjectsExporter { throw SavedObjectsExportError.exportSizeExceeded(this.#exportSizeLimit); } const objects = await this.fetchByObjects(options); - return this.processObjects(objects, { + const comparator = getPreservedOrderComparator(objects); + return this.processObjects(objects, comparator, { + request: options.request, includeReferencesDeep: options.includeReferencesDeep, excludeExportDetails: options.excludeExportDetails, namespace: options.namespace, @@ -79,7 +98,9 @@ export class SavedObjectsExporter { private async processObjects( savedObjects: SavedObject[], + sortFunction: SavedObjectComparator, { + request, excludeExportDetails = false, includeReferencesDeep = false, namespace, @@ -88,6 +109,13 @@ export class SavedObjectsExporter { let exportedObjects: Array>; let missingReferences: SavedObjectsExportResultDetails['missingReferences'] = []; + savedObjects = await applyExportTransforms({ + request, + objects: savedObjects, + transforms: this.#exportTransforms, + sortFunction, + }); + if (includeReferencesDeep) { const fetchResult = await fetchNestedDependencies( savedObjects, @@ -145,7 +173,7 @@ export class SavedObjectsExporter { findResponse.saved_objects // exclude the find-specific `score` property from the exported objects .map(({ score, ...obj }) => obj) - .sort((a: SavedObject, b: SavedObject) => (a.id > b.id ? 1 : -1)) + .sort(byIdAscComparator) ); } } diff --git a/src/core/server/saved_objects/export/types.ts b/src/core/server/saved_objects/export/types.ts index d8d162e51c29..bf7b265e45d2 100644 --- a/src/core/server/saved_objects/export/types.ts +++ b/src/core/server/saved_objects/export/types.ts @@ -6,10 +6,13 @@ * Public License, v 1. */ -import { SavedObjectsFindOptionsReference } from '../types'; +import { KibanaRequest } from '../../http'; +import { SavedObject, SavedObjectsFindOptionsReference } from '../types'; /** @public */ export interface SavedObjectExportBaseOptions { + /** The http request initiating the export. */ + request: KibanaRequest; /** flag to also include all related saved objects in the export stream. */ includeReferencesDeep?: boolean; /** flag to not append {@link SavedObjectsExportResultDetails | export details} to the end of the export stream. */ @@ -64,3 +67,92 @@ export interface SavedObjectsExportResultDetails { type: string; }>; } + +/** + * Context passed down to a {@link SavedObjectsExportTransform | export transform function} + * + * @public + */ +export interface SavedObjectsExportTransformContext { + /** + * The request that initiated the export request. Can be used to create scoped + * services or client inside the {@link SavedObjectsExportTransform | transformation} + */ + request: KibanaRequest; +} + +/** + * Transformation function used to mutate the exported objects of the associated type. + * + * A type's export transform function will be executed once per user-initiated export, + * for all objects of that type. + * + * @example + * Registering a transform function changing the object's attributes during the export + * ```ts + * // src/plugins/my_plugin/server/plugin.ts + * import { myType } from './saved_objects'; + * + * export class Plugin() { + * setup: (core: CoreSetup) => { + * core.savedObjects.registerType({ + * ...myType, + * management: { + * ...myType.management, + * onExport: (ctx, objects) => { + * return objects.map((obj) => ({ + * ...obj, + * attributes: { + * ...obj.attributes, + * enabled: false, + * } + * }) + * } + * }, + * }); + * } + * } + * ``` + * + * @example + * Registering a transform function adding additional objects to the export + * ```ts + * // src/plugins/my_plugin/server/plugin.ts + * import { myType } from './saved_objects'; + * + * export class Plugin() { + * setup: (core: CoreSetup) => { + * const savedObjectStartContractPromise = getStartServices().then( + * ([{ savedObjects: savedObjectsStart }]) => savedObjectsStart + * ); + * + * core.savedObjects.registerType({ + * ...myType, + * management: { + * ...myType.management, + * onExport: async (ctx, objects) => { + * const { getScopedClient } = await savedObjectStartContractPromise; + * const client = getScopedClient(ctx.request); + * + * const depResponse = await client.find({ + * type: 'my-nested-object', + * hasReference: objs.map(({ id, type }) => ({ id, type })), + * }); + * + * return [...objs, ...depResponse.saved_objects]; + * } + * }, + * }); + * } + * } + * ``` + * + * @remarks Trying to change an object's id or type during the transform will result in + * a runtime error during the export process. + * + * @public + */ +export type SavedObjectsExportTransform = ( + context: SavedObjectsExportTransformContext, + objects: Array> +) => SavedObject[] | Promise; diff --git a/src/core/server/saved_objects/export/utils.test.ts b/src/core/server/saved_objects/export/utils.test.ts new file mode 100644 index 000000000000..c547aa2271cf --- /dev/null +++ b/src/core/server/saved_objects/export/utils.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { byIdAscComparator, getPreservedOrderComparator } from './utils'; +import { SavedObject } from '../../../types'; + +const createObj = (id: string): SavedObject => ({ + id, + type: 'dummy', + attributes: {}, + references: [], +}); + +describe('byIdAscComparator', () => { + it('sorts the objects by id asc', () => { + const objs = [createObj('delta'), createObj('alpha'), createObj('beta')]; + + objs.sort(byIdAscComparator); + + expect(objs.map((obj) => obj.id)).toEqual(['alpha', 'beta', 'delta']); + }); +}); + +describe('getPreservedOrderComparator', () => { + it('sorts objects depending on the order of the provided list', () => { + const objA = createObj('A'); + const objB = createObj('B'); + const objC = createObj('C'); + + const comparator = getPreservedOrderComparator([objA, objB, objC]); + + const objs = [objC, objA, objB]; + objs.sort(comparator); + + expect(objs.map((obj) => obj.id)).toEqual(['A', 'B', 'C']); + }); + + it('appends unknown objects at the end of the list and sort them by id', () => { + const objA = createObj('A'); + const objB = createObj('B'); + const objC = createObj('C'); + const addedA = createObj('addedA'); + const addedB = createObj('addedB'); + + const comparator = getPreservedOrderComparator([objA, objB, objC]); + + const objs = [addedB, objC, addedA, objA, objB]; + objs.sort(comparator); + + expect(objs.map((obj) => obj.id)).toEqual(['A', 'B', 'C', 'addedA', 'addedB']); + }); +}); diff --git a/src/core/server/saved_objects/export/utils.ts b/src/core/server/saved_objects/export/utils.ts new file mode 100644 index 000000000000..e8567c6da1dc --- /dev/null +++ b/src/core/server/saved_objects/export/utils.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { SavedObject } from '../../../types'; + +export type SavedObjectComparator = (a: SavedObject, b: SavedObject) => number; + +export const getObjKey = (obj: SavedObject) => `${obj.type}|${obj.id}`; + +export const byIdAscComparator: SavedObjectComparator = (a: SavedObject, b: SavedObject) => + a.id > b.id ? 1 : -1; + +/** + * Create a comparator that will sort objects depending on their position in the provided array. + * Objects not present in the array will be appended at the end of the list, and sorted by id asc. + * + * @example + * ```ts + * const comparator = getPreservedOrderComparator([objA, objB, objC]); + * const list = [newB, objB, objC, newA, objA]; // with obj.title matching their variable name + * list.sort() + * // list = [objA, objB, objC, newA, newB] + * ``` + */ +export const getPreservedOrderComparator = (objects: SavedObject[]): SavedObjectComparator => { + const orderedKeys = objects.map(getObjKey); + return (a: SavedObject, b: SavedObject) => { + const indexA = orderedKeys.indexOf(getObjKey(a)); + const indexB = orderedKeys.indexOf(getObjKey(b)); + if (indexA > -1 && indexB > -1) { + return indexA - indexB > 0 ? 1 : -1; + } + if (indexA > -1) { + return -1; + } + if (indexB > -1) { + return 1; + } + return byIdAscComparator(a, b); + }; +}; diff --git a/src/core/server/saved_objects/import/import_saved_objects.test.ts b/src/core/server/saved_objects/import/import_saved_objects.test.ts index e675fb7ea100..11d3df5faae5 100644 --- a/src/core/server/saved_objects/import/import_saved_objects.test.ts +++ b/src/core/server/saved_objects/import/import_saved_objects.test.ts @@ -18,6 +18,7 @@ import { savedObjectsClientMock } from '../../mocks'; import { ISavedObjectTypeRegistry } from '..'; import { typeRegistryMock } from '../saved_objects_type_registry.mock'; import { importSavedObjectsFromStream, ImportSavedObjectsOptions } from './import_saved_objects'; +import { SavedObjectsImportHook, SavedObjectsImportWarning } from './types'; import { collectSavedObjects, @@ -26,6 +27,7 @@ import { checkConflicts, checkOriginConflicts, createSavedObjects, + executeImportHooks, } from './lib'; jest.mock('./lib/collect_saved_objects'); @@ -34,6 +36,7 @@ jest.mock('./lib/validate_references'); jest.mock('./lib/check_conflicts'); jest.mock('./lib/check_origin_conflicts'); jest.mock('./lib/create_saved_objects'); +jest.mock('./lib/execute_import_hooks'); const getMockFn = any, U>(fn: (...args: Parameters) => U) => fn as jest.MockedFunction<(...args: Parameters) => U>; @@ -61,6 +64,7 @@ describe('#importSavedObjectsFromStream', () => { pendingOverwrites: new Set(), }); getMockFn(createSavedObjects).mockResolvedValue({ errors: [], createdObjects: [] }); + getMockFn(executeImportHooks).mockResolvedValue([]); }); let readStream: Readable; @@ -70,14 +74,19 @@ describe('#importSavedObjectsFromStream', () => { let typeRegistry: jest.Mocked; const namespace = 'some-namespace'; - const setupOptions = ( - createNewCopies: boolean = false, - getTypeImpl: (name: string) => any = (type: string) => + const setupOptions = ({ + createNewCopies = false, + getTypeImpl = (type: string) => ({ // other attributes aren't needed for the purposes of injecting metadata management: { icon: `${type}-icon` }, - } as any) - ): ImportSavedObjectsOptions => { + } as any), + importHooks = {}, + }: { + createNewCopies?: boolean; + getTypeImpl?: (name: string) => any; + importHooks?: Record; + } = {}): ImportSavedObjectsOptions => { readStream = new Readable(); savedObjectsClient = savedObjectsClientMock.create(); typeRegistry = typeRegistryMock.create(); @@ -90,6 +99,7 @@ describe('#importSavedObjectsFromStream', () => { typeRegistry, namespace, createNewCopies, + importHooks, }; }; const createObject = ({ @@ -153,6 +163,31 @@ describe('#importSavedObjectsFromStream', () => { ); }); + test('executes import hooks', async () => { + const importHooks = { + foo: [jest.fn()], + }; + + const options = setupOptions({ importHooks }); + const collectedObjects = [createObject()]; + getMockFn(collectSavedObjects).mockResolvedValue({ + errors: [], + collectedObjects, + importIdMap: new Map(), + }); + getMockFn(createSavedObjects).mockResolvedValue({ + errors: [], + createdObjects: collectedObjects, + }); + + await importSavedObjectsFromStream(options); + + expect(executeImportHooks).toHaveBeenCalledWith({ + objects: collectedObjects, + importHooks, + }); + }); + describe('with createNewCopies disabled', () => { test('does not regenerate object IDs', async () => { const options = setupOptions(); @@ -256,7 +291,7 @@ describe('#importSavedObjectsFromStream', () => { describe('with createNewCopies enabled', () => { test('regenerates object IDs', async () => { - const options = setupOptions(true); + const options = setupOptions({ createNewCopies: true }); const collectedObjects = [createObject()]; getMockFn(collectSavedObjects).mockResolvedValue({ errors: [], @@ -269,7 +304,7 @@ describe('#importSavedObjectsFromStream', () => { }); test('does not check conflicts or check origin conflicts', async () => { - const options = setupOptions(true); + const options = setupOptions({ createNewCopies: true }); getMockFn(validateReferences).mockResolvedValue([]); await importSavedObjectsFromStream(options); @@ -278,7 +313,7 @@ describe('#importSavedObjectsFromStream', () => { }); test('creates saved objects', async () => { - const options = setupOptions(true); + const options = setupOptions({ createNewCopies: true }); const collectedObjects = [createObject()]; const errors = [createError(), createError()]; getMockFn(collectSavedObjects).mockResolvedValue({ @@ -313,7 +348,7 @@ describe('#importSavedObjectsFromStream', () => { const options = setupOptions(); const result = await importSavedObjectsFromStream(options); - expect(result).toEqual({ success: true, successCount: 0 }); + expect(result).toEqual({ success: true, successCount: 0, warnings: [] }); }); test('returns success=false if an error occurred', async () => { @@ -325,7 +360,33 @@ describe('#importSavedObjectsFromStream', () => { }); const result = await importSavedObjectsFromStream(options); - expect(result).toEqual({ success: false, successCount: 0, errors: [expect.any(Object)] }); + expect(result).toEqual({ + success: false, + successCount: 0, + errors: [expect.any(Object)], + warnings: [], + }); + }); + + test('returns warnings from the import hooks', async () => { + const options = setupOptions(); + const collectedObjects = [createObject()]; + getMockFn(collectSavedObjects).mockResolvedValue({ + errors: [], + collectedObjects, + importIdMap: new Map(), + }); + getMockFn(createSavedObjects).mockResolvedValue({ + errors: [], + createdObjects: collectedObjects, + }); + + const warnings: SavedObjectsImportWarning[] = [{ type: 'simple', message: 'foo' }]; + getMockFn(executeImportHooks).mockResolvedValue(warnings); + + const result = await importSavedObjectsFromStream(options); + + expect(result.warnings).toEqual(warnings); }); describe('handles a mix of successes and errors and injects metadata', () => { @@ -389,12 +450,13 @@ describe('#importSavedObjectsFromStream', () => { successCount: 3, successResults, errors: errorResults, + warnings: [], }); }); test('with createNewCopies enabled', async () => { // however, we include it here for posterity - const options = setupOptions(true); + const options = setupOptions({ createNewCopies: true }); getMockFn(createSavedObjects).mockResolvedValue({ errors, createdObjects }); const result = await importSavedObjectsFromStream(options); @@ -410,6 +472,7 @@ describe('#importSavedObjectsFromStream', () => { successCount: 3, successResults, errors: errorResults, + warnings: [], }); }); }); @@ -418,15 +481,18 @@ describe('#importSavedObjectsFromStream', () => { const obj1 = createObject({ type: 'foo' }); const obj2 = createObject({ type: 'bar', title: 'bar-title' }); - const options = setupOptions(false, (type) => { - if (type === 'foo') { + const options = setupOptions({ + createNewCopies: false, + getTypeImpl: (type) => { + if (type === 'foo') { + return { + management: { getTitle: () => 'getTitle-foo', icon: `${type}-icon` }, + }; + } return { - management: { getTitle: () => 'getTitle-foo', icon: `${type}-icon` }, + management: { icon: `${type}-icon` }, }; - } - return { - management: { icon: `${type}-icon` }, - }; + }, }); getMockFn(checkConflicts).mockResolvedValue({ @@ -456,6 +522,7 @@ describe('#importSavedObjectsFromStream', () => { success: true, successCount: 2, successResults, + warnings: [], }); }); @@ -483,7 +550,12 @@ describe('#importSavedObjectsFromStream', () => { const result = await importSavedObjectsFromStream(options); const expectedErrors = errors.map(({ type, id }) => expect.objectContaining({ type, id })); - expect(result).toEqual({ success: false, successCount: 0, errors: expectedErrors }); + expect(result).toEqual({ + success: false, + successCount: 0, + errors: expectedErrors, + warnings: [], + }); }); }); }); diff --git a/src/core/server/saved_objects/import/import_saved_objects.ts b/src/core/server/saved_objects/import/import_saved_objects.ts index a788bcf47d32..9baef59dc162 100644 --- a/src/core/server/saved_objects/import/import_saved_objects.ts +++ b/src/core/server/saved_objects/import/import_saved_objects.ts @@ -9,7 +9,11 @@ import { Readable } from 'stream'; import { ISavedObjectTypeRegistry } from '../saved_objects_type_registry'; import { SavedObjectsClientContract } from '../types'; -import { SavedObjectsImportFailure, SavedObjectsImportResponse } from './types'; +import { + SavedObjectsImportFailure, + SavedObjectsImportResponse, + SavedObjectsImportHook, +} from './types'; import { validateReferences, checkOriginConflicts, @@ -17,6 +21,7 @@ import { checkConflicts, regenerateIds, collectSavedObjects, + executeImportHooks, } from './lib'; /** @@ -33,6 +38,8 @@ export interface ImportSavedObjectsOptions { savedObjectsClient: SavedObjectsClientContract; /** The registry of all known saved object types */ typeRegistry: ISavedObjectTypeRegistry; + /** List of registered import hooks */ + importHooks: Record; /** if specified, will import in given namespace, else will import as global object */ namespace?: string; /** If true, will create new copies of import objects, each with a random `id` and undefined `originId`. */ @@ -52,6 +59,7 @@ export async function importSavedObjectsFromStream({ createNewCopies, savedObjectsClient, typeRegistry, + importHooks, namespace, }: ImportSavedObjectsOptions): Promise { let errorAccumulator: SavedObjectsImportFailure[] = []; @@ -147,10 +155,15 @@ export async function importSavedObjectsFromStream({ ...(attemptedOverwrite && { overwrite: true }), }; }); + const warnings = await executeImportHooks({ + objects: createSavedObjectsResult.createdObjects, + importHooks, + }); return { successCount: createSavedObjectsResult.createdObjects.length, success: errorAccumulator.length === 0, + warnings, ...(successResults.length && { successResults }), ...(errorResults.length && { errors: errorResults }), }; diff --git a/src/core/server/saved_objects/import/index.ts b/src/core/server/saved_objects/import/index.ts index 4cc2e6e83995..0616c1277a3a 100644 --- a/src/core/server/saved_objects/import/index.ts +++ b/src/core/server/saved_objects/import/index.ts @@ -19,5 +19,10 @@ export { SavedObjectsImportUnsupportedTypeError, SavedObjectsResolveImportErrorsOptions, SavedObjectsImportRetry, + SavedObjectsImportHook, + SavedObjectsImportHookResult, + SavedObjectsImportSimpleWarning, + SavedObjectsImportActionRequiredWarning, + SavedObjectsImportWarning, } from './types'; export { SavedObjectsImportError } from './errors'; diff --git a/src/core/server/saved_objects/import/lib/execute_import_hooks.test.ts b/src/core/server/saved_objects/import/lib/execute_import_hooks.test.ts new file mode 100644 index 000000000000..ca769bc9ac4c --- /dev/null +++ b/src/core/server/saved_objects/import/lib/execute_import_hooks.test.ts @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { SavedObject } from '../../types'; +import { SavedObjectsImportHookResult, SavedObjectsImportWarning } from '../types'; +import { executeImportHooks } from './execute_import_hooks'; + +const createObject = (type: string, id: string): SavedObject => ({ + type, + id, + attributes: {}, + references: [], +}); + +const createHook = ( + result: SavedObjectsImportHookResult | Promise = {} +) => jest.fn().mockReturnValue(result); + +const createWarning = (message: string): SavedObjectsImportWarning => ({ + type: 'simple', + message, +}); + +describe('executeImportHooks', () => { + it('invokes the hooks with the correct objects', async () => { + const foo1 = createObject('foo', '1'); + const foo2 = createObject('foo', '2'); + const bar1 = createObject('bar', '1'); + const objects = [foo1, bar1, foo2]; + + const fooHook = createHook(); + const barHook = createHook(); + + await executeImportHooks({ + objects, + importHooks: { + foo: [fooHook], + bar: [barHook], + }, + }); + + expect(fooHook).toHaveBeenCalledTimes(1); + expect(fooHook).toHaveBeenCalledWith([foo1, foo2]); + + expect(barHook).toHaveBeenCalledTimes(1); + expect(barHook).toHaveBeenCalledWith([bar1]); + }); + + it('handles multiple hooks per type', async () => { + const foo1 = createObject('foo', '1'); + const foo2 = createObject('foo', '2'); + const bar1 = createObject('bar', '1'); + const objects = [foo1, bar1, foo2]; + + const fooHook1 = createHook(); + const fooHook2 = createHook(); + + await executeImportHooks({ + objects, + importHooks: { + foo: [fooHook1, fooHook2], + }, + }); + + expect(fooHook1).toHaveBeenCalledTimes(1); + expect(fooHook1).toHaveBeenCalledWith([foo1, foo2]); + + expect(fooHook2).toHaveBeenCalledTimes(1); + expect(fooHook2).toHaveBeenCalledWith([foo1, foo2]); + }); + + it('does not call a hook if no object of its type is present', async () => { + const objects = [createObject('foo', '1'), createObject('foo', '2')]; + const hook = createHook(); + + await executeImportHooks({ + objects, + importHooks: { + bar: [hook], + }, + }); + + expect(hook).not.toHaveBeenCalled(); + }); + + it('returns the warnings returned by the hooks', async () => { + const foo1 = createObject('foo', '1'); + const bar1 = createObject('bar', '1'); + const objects = [foo1, bar1]; + + const fooWarning1 = createWarning('foo warning 1'); + const fooWarning2 = createWarning('foo warning 2'); + const barWarning = createWarning('bar warning'); + + const fooHook = createHook({ warnings: [fooWarning1, fooWarning2] }); + const barHook = createHook({ warnings: [barWarning] }); + + const warnings = await executeImportHooks({ + objects, + importHooks: { + foo: [fooHook], + bar: [barHook], + }, + }); + + expect(warnings).toEqual([fooWarning1, fooWarning2, barWarning]); + }); + + it('handles asynchronous hooks', async () => { + const foo1 = createObject('foo', '1'); + const bar1 = createObject('bar', '1'); + const objects = [foo1, bar1]; + + const fooWarning = createWarning('foo warning 1'); + const barWarning = createWarning('bar warning'); + + const fooHook = createHook(Promise.resolve({ warnings: [fooWarning] })); + const barHook = createHook(Promise.resolve({ warnings: [barWarning] })); + + const warnings = await executeImportHooks({ + objects, + importHooks: { + foo: [fooHook], + bar: [barHook], + }, + }); + + expect(warnings).toEqual([fooWarning, barWarning]); + }); +}); diff --git a/src/core/server/saved_objects/import/lib/execute_import_hooks.ts b/src/core/server/saved_objects/import/lib/execute_import_hooks.ts new file mode 100644 index 000000000000..aff8bac1c17c --- /dev/null +++ b/src/core/server/saved_objects/import/lib/execute_import_hooks.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { SavedObject } from '../../types'; +import { SavedObjectsImportHook, SavedObjectsImportWarning } from '../types'; + +interface ExecuteImportHooksOptions { + objects: SavedObject[]; + importHooks: Record; +} + +export const executeImportHooks = async ({ + objects, + importHooks, +}: ExecuteImportHooksOptions): Promise => { + const objsByType = splitByType(objects); + let warnings: SavedObjectsImportWarning[] = []; + + for (const [type, typeObjs] of Object.entries(objsByType)) { + const hooks = importHooks[type] ?? []; + for (const hook of hooks) { + const hookResult = await hook(typeObjs); + if (hookResult.warnings) { + warnings = [...warnings, ...hookResult.warnings]; + } + } + } + + return warnings; +}; + +const splitByType = (objects: SavedObject[]): Record => { + return objects.reduce((memo, obj) => { + memo[obj.type] = [...(memo[obj.type] ?? []), obj]; + return memo; + }, {} as Record); +}; diff --git a/src/core/server/saved_objects/import/lib/index.ts b/src/core/server/saved_objects/import/lib/index.ts index ceb301cd1018..64735f1d0dac 100644 --- a/src/core/server/saved_objects/import/lib/index.ts +++ b/src/core/server/saved_objects/import/lib/index.ts @@ -18,3 +18,4 @@ export { regenerateIds } from './regenerate_ids'; export { splitOverwrites } from './split_overwrites'; export { getNonExistingReferenceAsKeys, validateReferences } from './validate_references'; export { validateRetries } from './validate_retries'; +export { executeImportHooks } from './execute_import_hooks'; diff --git a/src/core/server/saved_objects/import/resolve_import_errors.test.ts b/src/core/server/saved_objects/import/resolve_import_errors.test.ts index 28b31d22a4de..b4861a35266b 100644 --- a/src/core/server/saved_objects/import/resolve_import_errors.test.ts +++ b/src/core/server/saved_objects/import/resolve_import_errors.test.ts @@ -15,9 +15,10 @@ import { SavedObjectsImportFailure, SavedObjectsImportRetry, SavedObjectReference, + SavedObjectsImportWarning, } from '../types'; import { savedObjectsClientMock } from '../../mocks'; -import { ISavedObjectTypeRegistry } from '..'; +import { ISavedObjectTypeRegistry, SavedObjectsImportHook } from '..'; import { typeRegistryMock } from '../saved_objects_type_registry.mock'; import { resolveSavedObjectsImportErrors, @@ -34,6 +35,7 @@ import { splitOverwrites, createSavedObjects, createObjectsFilter, + executeImportHooks, } from './lib'; jest.mock('./lib/validate_retries'); @@ -45,6 +47,7 @@ jest.mock('./lib/check_conflicts'); jest.mock('./lib/check_origin_conflicts'); jest.mock('./lib/split_overwrites'); jest.mock('./lib/create_saved_objects'); +jest.mock('./lib/execute_import_hooks'); const getMockFn = any, U>(fn: (...args: Parameters) => U) => fn as jest.MockedFunction<(...args: Parameters) => U>; @@ -73,6 +76,7 @@ describe('#importSavedObjectsFromStream', () => { objectsToNotOverwrite: [], }); getMockFn(createSavedObjects).mockResolvedValue({ errors: [], createdObjects: [] }); + getMockFn(executeImportHooks).mockResolvedValue([]); }); let readStream: Readable; @@ -81,15 +85,21 @@ describe('#importSavedObjectsFromStream', () => { let typeRegistry: jest.Mocked; const namespace = 'some-namespace'; - const setupOptions = ( - retries: SavedObjectsImportRetry[] = [], - createNewCopies: boolean = false, - getTypeImpl: (name: string) => any = (type: string) => + const setupOptions = ({ + retries = [], + createNewCopies = false, + getTypeImpl = (type: string) => ({ // other attributes aren't needed for the purposes of injecting metadata management: { icon: `${type}-icon` }, - } as any) - ): ResolveSavedObjectsImportErrorsOptions => { + } as any), + importHooks = {}, + }: { + retries?: SavedObjectsImportRetry[]; + createNewCopies?: boolean; + getTypeImpl?: (name: string) => any; + importHooks?: Record; + } = {}): ResolveSavedObjectsImportErrorsOptions => { readStream = new Readable(); savedObjectsClient = savedObjectsClientMock.create(); typeRegistry = typeRegistryMock.create(); @@ -101,6 +111,7 @@ describe('#importSavedObjectsFromStream', () => { retries, savedObjectsClient, typeRegistry, + importHooks, // namespace and createNewCopies don't matter, as they don't change the logic in this module, they just get passed to sub-module methods namespace, createNewCopies, @@ -148,7 +159,7 @@ describe('#importSavedObjectsFromStream', () => { describe('module calls', () => { test('validates retries', async () => { const retry = createRetry(); - const options = setupOptions([retry]); + const options = setupOptions({ retries: [retry] }); await resolveSavedObjectsImportErrors(options); expect(validateRetries).toHaveBeenCalledWith([retry]); @@ -156,7 +167,7 @@ describe('#importSavedObjectsFromStream', () => { test('creates objects filter', async () => { const retry = createRetry(); - const options = setupOptions([retry]); + const options = setupOptions({ retries: [retry] }); await resolveSavedObjectsImportErrors(options); expect(createObjectsFilter).toHaveBeenCalledWith([retry]); @@ -178,7 +189,7 @@ describe('#importSavedObjectsFromStream', () => { test('validates references', async () => { const retries = [createRetry()]; - const options = setupOptions(retries); + const options = setupOptions({ retries }); const collectedObjects = [createObject()]; getMockFn(collectSavedObjects).mockResolvedValue({ errors: [], @@ -195,6 +206,30 @@ describe('#importSavedObjectsFromStream', () => { ); }); + test('execute import hooks', async () => { + const importHooks = { + foo: [jest.fn()], + }; + const options = setupOptions({ importHooks }); + const collectedObjects = [createObject()]; + getMockFn(collectSavedObjects).mockResolvedValue({ + errors: [], + collectedObjects, + importIdMap: new Map(), + }); + getMockFn(createSavedObjects).mockResolvedValueOnce({ + errors: [], + createdObjects: collectedObjects, + }); + + await resolveSavedObjectsImportErrors(options); + + expect(executeImportHooks).toHaveBeenCalledWith({ + objects: collectedObjects, + importHooks, + }); + }); + test('uses `retries` to replace references of collected objects before validating', async () => { const object = createObject([{ type: 'bar-type', id: 'abc', name: 'some name' }]); const retries = [ @@ -203,7 +238,7 @@ describe('#importSavedObjectsFromStream', () => { replaceReferences: [{ type: 'bar-type', from: 'abc', to: 'def' }], }), ]; - const options = setupOptions(retries); + const options = setupOptions({ retries }); getMockFn(collectSavedObjects).mockResolvedValue({ errors: [], collectedObjects: [object], @@ -226,7 +261,7 @@ describe('#importSavedObjectsFromStream', () => { test('checks conflicts', async () => { const createNewCopies = (Symbol() as unknown) as boolean; const retries = [createRetry()]; - const options = setupOptions(retries, createNewCopies); + const options = setupOptions({ retries, createNewCopies }); const collectedObjects = [createObject()]; getMockFn(collectSavedObjects).mockResolvedValue({ errors: [], @@ -248,7 +283,7 @@ describe('#importSavedObjectsFromStream', () => { test('gets import ID map for retries', async () => { const retries = [createRetry()]; const createNewCopies = (Symbol() as unknown) as boolean; - const options = setupOptions(retries, createNewCopies); + const options = setupOptions({ retries, createNewCopies }); const filteredObjects = [createObject()]; getMockFn(checkConflicts).mockResolvedValue({ errors: [], @@ -264,7 +299,7 @@ describe('#importSavedObjectsFromStream', () => { test('splits objects to overwrite from those not to overwrite', async () => { const retries = [createRetry()]; - const options = setupOptions(retries); + const options = setupOptions({ retries }); const collectedObjects = [createObject()]; getMockFn(collectSavedObjects).mockResolvedValue({ errors: [], @@ -344,7 +379,7 @@ describe('#importSavedObjectsFromStream', () => { describe('with createNewCopies enabled', () => { test('regenerates object IDs', async () => { - const options = setupOptions([], true); + const options = setupOptions({ createNewCopies: true }); const collectedObjects = [createObject()]; getMockFn(collectSavedObjects).mockResolvedValue({ errors: [], @@ -357,7 +392,7 @@ describe('#importSavedObjectsFromStream', () => { }); test('creates saved objects', async () => { - const options = setupOptions([], true); + const options = setupOptions({ createNewCopies: true }); const errors = [createError(), createError(), createError()]; getMockFn(collectSavedObjects).mockResolvedValue({ errors: [errors[0]], @@ -422,7 +457,7 @@ describe('#importSavedObjectsFromStream', () => { const options = setupOptions(); const result = await resolveSavedObjectsImportErrors(options); - expect(result).toEqual({ success: true, successCount: 0 }); + expect(result).toEqual({ success: true, successCount: 0, warnings: [] }); }); test('returns success=false if an error occurred', async () => { @@ -434,15 +469,40 @@ describe('#importSavedObjectsFromStream', () => { }); const result = await resolveSavedObjectsImportErrors(options); - expect(result).toEqual({ success: false, successCount: 0, errors: [expect.any(Object)] }); + expect(result).toEqual({ + success: false, + successCount: 0, + errors: [expect.any(Object)], + warnings: [], + }); + }); + + test('executes import hooks', async () => { + const options = setupOptions(); + const collectedObjects = [createObject()]; + getMockFn(collectSavedObjects).mockResolvedValue({ + errors: [], + collectedObjects, + importIdMap: new Map(), + }); + getMockFn(createSavedObjects).mockResolvedValueOnce({ + errors: [], + createdObjects: collectedObjects, + }); + const warnings: SavedObjectsImportWarning[] = [{ type: 'simple', message: 'foo' }]; + getMockFn(executeImportHooks).mockResolvedValue(warnings); + + const result = await resolveSavedObjectsImportErrors(options); + + expect(result.warnings).toEqual(warnings); }); test('handles a mix of successes and errors and injects metadata', async () => { const error1 = createError(); const error2 = createError(); - const options = setupOptions([ - { type: error2.type, id: error2.id, overwrite: true, replaceReferences: [] }, - ]); + const options = setupOptions({ + retries: [{ type: error2.type, id: error2.id, overwrite: true, replaceReferences: [] }], + }); const obj1 = createObject(); const tmp = createObject(); const obj2 = { ...tmp, destinationId: 'some-destinationId', originId: tmp.id }; @@ -483,22 +543,30 @@ describe('#importSavedObjectsFromStream', () => { { ...error1, meta: { ...error1.meta, icon: `${error1.type}-icon` } }, { ...error2, meta: { ...error2.meta, icon: `${error2.type}-icon` }, overwrite: true }, ]; - expect(result).toEqual({ success: false, successCount: 3, successResults, errors }); + expect(result).toEqual({ + success: false, + successCount: 3, + successResults, + errors, + warnings: [], + }); }); test('uses `type.management.getTitle` to resolve the titles', async () => { const obj1 = createObject([], { type: 'foo' }); const obj2 = createObject([], { type: 'bar', title: 'bar-title' }); - const options = setupOptions([], false, (type) => { - if (type === 'foo') { + const options = setupOptions({ + getTypeImpl: (type) => { + if (type === 'foo') { + return { + management: { getTitle: () => 'getTitle-foo', icon: `${type}-icon` }, + }; + } return { - management: { getTitle: () => 'getTitle-foo', icon: `${type}-icon` }, + management: { icon: `${type}-icon` }, }; - } - return { - management: { icon: `${type}-icon` }, - }; + }, }); getMockFn(checkConflicts).mockResolvedValue({ @@ -532,6 +600,7 @@ describe('#importSavedObjectsFromStream', () => { success: true, successCount: 2, successResults, + warnings: [], }); }); @@ -555,7 +624,12 @@ describe('#importSavedObjectsFromStream', () => { const result = await resolveSavedObjectsImportErrors(options); const expectedErrors = errors.map(({ type, id }) => expect.objectContaining({ type, id })); - expect(result).toEqual({ success: false, successCount: 0, errors: expectedErrors }); + expect(result).toEqual({ + success: false, + successCount: 0, + errors: expectedErrors, + warnings: [], + }); }); }); }); diff --git a/src/core/server/saved_objects/import/resolve_import_errors.ts b/src/core/server/saved_objects/import/resolve_import_errors.ts index 5bb4c79e34cd..4526eefe467d 100644 --- a/src/core/server/saved_objects/import/resolve_import_errors.ts +++ b/src/core/server/saved_objects/import/resolve_import_errors.ts @@ -11,6 +11,7 @@ import { SavedObject, SavedObjectsClientContract, SavedObjectsImportRetry } from import { ISavedObjectTypeRegistry } from '../saved_objects_type_registry'; import { SavedObjectsImportFailure, + SavedObjectsImportHook, SavedObjectsImportResponse, SavedObjectsImportSuccess, } from './types'; @@ -24,6 +25,7 @@ import { createSavedObjects, getImportIdMapForRetries, checkConflicts, + executeImportHooks, } from './lib'; /** @@ -38,6 +40,8 @@ export interface ResolveSavedObjectsImportErrorsOptions { savedObjectsClient: SavedObjectsClientContract; /** The registry of all known saved object types */ typeRegistry: ISavedObjectTypeRegistry; + /** List of registered import hooks */ + importHooks: Record; /** saved object import references to retry */ retries: SavedObjectsImportRetry[]; /** if specified, will import in given namespace */ @@ -58,6 +62,7 @@ export async function resolveSavedObjectsImportErrors({ retries, savedObjectsClient, typeRegistry, + importHooks, namespace, createNewCopies, }: ResolveSavedObjectsImportErrorsOptions): Promise { @@ -146,6 +151,7 @@ export async function resolveSavedObjectsImportErrors({ // Bulk create in two batches, overwrites and non-overwrites let successResults: SavedObjectsImportSuccess[] = []; + let successObjects: SavedObject[] = []; const accumulatedErrors = [...errorAccumulator]; const bulkCreateObjects = async ( objects: Array>, @@ -162,6 +168,7 @@ export async function resolveSavedObjectsImportErrors({ const { createdObjects, errors: bulkCreateErrors } = await createSavedObjects( createSavedObjectsParams ); + successObjects = [...successObjects, ...createdObjects]; errorAccumulator = [...errorAccumulator, ...bulkCreateErrors]; successCount += createdObjects.length; successResults = [ @@ -200,9 +207,15 @@ export async function resolveSavedObjectsImportErrors({ }; }); + const warnings = await executeImportHooks({ + objects: successObjects, + importHooks, + }); + return { successCount, success: errorAccumulator.length === 0, + warnings, ...(successResults.length && { successResults }), ...(errorResults.length && { errors: errorResults }), }; diff --git a/src/core/server/saved_objects/import/saved_objects_importer.ts b/src/core/server/saved_objects/import/saved_objects_importer.ts index 77f4afd519cc..94568cb33663 100644 --- a/src/core/server/saved_objects/import/saved_objects_importer.ts +++ b/src/core/server/saved_objects/import/saved_objects_importer.ts @@ -15,6 +15,7 @@ import { SavedObjectsImportResponse, SavedObjectsImportOptions, SavedObjectsResolveImportErrorsOptions, + SavedObjectsImportHook, } from './types'; /** @@ -29,6 +30,7 @@ export class SavedObjectsImporter { readonly #savedObjectsClient: SavedObjectsClientContract; readonly #typeRegistry: ISavedObjectTypeRegistry; readonly #importSizeLimit: number; + readonly #importHooks: Record; constructor({ savedObjectsClient, @@ -42,6 +44,15 @@ export class SavedObjectsImporter { this.#savedObjectsClient = savedObjectsClient; this.#typeRegistry = typeRegistry; this.#importSizeLimit = importSizeLimit; + this.#importHooks = typeRegistry.getAllTypes().reduce((hooks, type) => { + if (type.management?.onImport) { + return { + ...hooks, + [type.name]: [type.management.onImport], + }; + } + return hooks; + }, {} as Record); } /** @@ -64,6 +75,7 @@ export class SavedObjectsImporter { objectLimit: this.#importSizeLimit, savedObjectsClient: this.#savedObjectsClient, typeRegistry: this.#typeRegistry, + importHooks: this.#importHooks, }); } @@ -87,6 +99,7 @@ export class SavedObjectsImporter { objectLimit: this.#importSizeLimit, savedObjectsClient: this.#savedObjectsClient, typeRegistry: this.#typeRegistry, + importHooks: this.#importHooks, }); } } diff --git a/src/core/server/saved_objects/import/types.ts b/src/core/server/saved_objects/import/types.ts index 8a159824ec7a..bbd814eb9b61 100644 --- a/src/core/server/saved_objects/import/types.ts +++ b/src/core/server/saved_objects/import/types.ts @@ -142,6 +142,7 @@ export interface SavedObjectsImportResponse { success: boolean; successCount: number; successResults?: SavedObjectsImportSuccess[]; + warnings: SavedObjectsImportWarning[]; errors?: SavedObjectsImportFailure[]; } @@ -176,3 +177,72 @@ export interface SavedObjectsResolveImportErrorsOptions { } export type CreatedObject = SavedObject & { destinationId?: string }; + +/** + * A simple informative warning that will be displayed to the user. + * + * @public + */ +export interface SavedObjectsImportSimpleWarning { + type: 'simple'; + /** The translated message to display to the user */ + message: string; +} + +/** + * A warning meant to notify that a specific user action is required to finalize the import + * of some type of object. + * + * @remark The `actionUrl` must be a path relative to the basePath, and not include it. + * + * @public + */ +export interface SavedObjectsImportActionRequiredWarning { + type: 'action_required'; + /** The translated message to display to the user. */ + message: string; + /** The path (without the basePath) that the user should be redirect to to address this warning. */ + actionPath: string; + /** An optional label to use for the link button. If unspecified, a default label will be used. */ + buttonLabel?: string; +} + +/** + * Composite type of all the possible types of import warnings. + * + * See {@link SavedObjectsImportSimpleWarning} and {@link SavedObjectsImportActionRequiredWarning} + * for more details. + * + * @public + */ +export type SavedObjectsImportWarning = + | SavedObjectsImportSimpleWarning + | SavedObjectsImportActionRequiredWarning; + +/** + * Result from a {@link SavedObjectsImportHook | import hook} + * + * @public + */ +export interface SavedObjectsImportHookResult { + /** + * An optional list of warnings to display in the UI when the import succeeds. + */ + warnings?: SavedObjectsImportWarning[]; +} + +/** + * A hook associated with a specific saved object type, that will be invoked during + * the import process. The hook will have access to the objects of the registered type. + * + * Currently, the only supported feature for import hooks is to return warnings to be displayed + * in the UI when the import succeeds. + * + * @remark The only interactions the hook can have with the import process is via the hook's + * response. Mutating the objects inside the hook's code will have no effect. + * + * @public + */ +export type SavedObjectsImportHook = ( + objects: Array> +) => SavedObjectsImportHookResult | Promise; diff --git a/src/core/server/saved_objects/index.ts b/src/core/server/saved_objects/index.ts index ed641ca2add2..9cf400a65030 100644 --- a/src/core/server/saved_objects/index.ts +++ b/src/core/server/saved_objects/index.ts @@ -23,6 +23,11 @@ export { SavedObjectsImportUnsupportedTypeError, SavedObjectsResolveImportErrorsOptions, SavedObjectsImportError, + SavedObjectsImportHook, + SavedObjectsImportHookResult, + SavedObjectsImportSimpleWarning, + SavedObjectsImportActionRequiredWarning, + SavedObjectsImportWarning, } from './import'; export { @@ -33,11 +38,14 @@ export { SavedObjectsExportByObjectOptions, SavedObjectsExportResultDetails, SavedObjectsExportError, + SavedObjectsExportTransformContext, + SavedObjectsExportTransform, } from './export'; export { SavedObjectsSerializer, SavedObjectsRawDoc, + SavedObjectsRawDocParseOptions, SavedObjectSanitizedDoc, SavedObjectUnsanitizedDoc, } from './serialization'; diff --git a/src/core/server/saved_objects/migrations/core/__mocks__/index.ts b/src/core/server/saved_objects/migrations/core/__mocks__/index.ts new file mode 100644 index 000000000000..b22ad0c93b23 --- /dev/null +++ b/src/core/server/saved_objects/migrations/core/__mocks__/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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +const mockUuidv5 = jest.fn().mockReturnValue('uuidv5'); +Object.defineProperty(mockUuidv5, 'DNS', { value: 'DNSUUID', writable: false }); +jest.mock('uuid/v5', () => mockUuidv5); + +export { mockUuidv5 }; diff --git a/src/core/server/saved_objects/migrations/core/__snapshots__/build_active_mappings.test.ts.snap b/src/core/server/saved_objects/migrations/core/__snapshots__/build_active_mappings.test.ts.snap index f8ef47cae894..9ee998118bde 100644 --- a/src/core/server/saved_objects/migrations/core/__snapshots__/build_active_mappings.test.ts.snap +++ b/src/core/server/saved_objects/migrations/core/__snapshots__/build_active_mappings.test.ts.snap @@ -6,6 +6,7 @@ Object { "migrationMappingPropertyHashes": Object { "aaa": "625b32086eb1d1203564cf85062dd22e", "bbb": "18c78c995965207ed3f6e7fc5c6e55fe", + "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", "migrationVersion": "4a1746014a75ade3a714e1db5763276f", "namespace": "2f4316de49999235636386fe51dc06c1", "namespaces": "2f4316de49999235636386fe51dc06c1", @@ -23,6 +24,9 @@ Object { "bbb": Object { "type": "long", }, + "coreMigrationVersion": Object { + "type": "keyword", + }, "migrationVersion": Object { "dynamic": "true", "type": "object", @@ -64,6 +68,7 @@ exports[`buildActiveMappings handles the \`dynamic\` property of types 1`] = ` Object { "_meta": Object { "migrationMappingPropertyHashes": Object { + "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", "firstType": "635418ab953d81d93f1190b70a8d3f57", "migrationVersion": "4a1746014a75ade3a714e1db5763276f", "namespace": "2f4316de49999235636386fe51dc06c1", @@ -78,6 +83,9 @@ Object { }, "dynamic": "strict", "properties": Object { + "coreMigrationVersion": Object { + "type": "keyword", + }, "firstType": Object { "dynamic": "strict", "properties": Object { diff --git a/src/core/server/saved_objects/migrations/core/build_active_mappings.ts b/src/core/server/saved_objects/migrations/core/build_active_mappings.ts index 594c6e4e3df6..83e7b1549bc9 100644 --- a/src/core/server/saved_objects/migrations/core/build_active_mappings.ts +++ b/src/core/server/saved_objects/migrations/core/build_active_mappings.ts @@ -153,6 +153,9 @@ function defaultMapping(): IndexMapping { }, }, }, + coreMigrationVersion: { + type: 'keyword', + }, }, }; } diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts index 9b97867bf187..6ba652abda3d 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.test.ts @@ -6,6 +6,7 @@ * Public License, v 1. */ +import { mockUuidv5 } from './__mocks__'; import { set } from '@elastic/safer-lodash-set'; import _ from 'lodash'; import { SavedObjectUnsanitizedDoc } from '../../serialization'; @@ -13,9 +14,11 @@ import { DocumentMigrator } from './document_migrator'; import { loggingSystemMock } from '../../../logging/logging_system.mock'; import { SavedObjectsType } from '../../types'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; +import { LEGACY_URL_ALIAS_TYPE } from '../../object_types'; const mockLoggerFactory = loggingSystemMock.create(); const mockLogger = mockLoggerFactory.get('mock logger'); +const kibanaVersion = '25.2.3'; const createRegistry = (...types: Array>) => { const registry = new SavedObjectTypeRegistry(); @@ -32,644 +35,1230 @@ const createRegistry = (...types: Array>) => { return registry; }; +beforeEach(() => { + mockUuidv5.mockClear(); +}); + describe('DocumentMigrator', () => { function testOpts() { return { - kibanaVersion: '25.2.3', + kibanaVersion, typeRegistry: createRegistry(), + minimumConvertVersion: '0.0.0', // no minimum version unless we specify it for a test case log: mockLogger, }; } - const createDefinition = (migrations: any) => ({ - kibanaVersion: '3.2.3', - typeRegistry: createRegistry({ - name: 'foo', - migrations: migrations as any, - }), - log: mockLogger, - }); + describe('validation', () => { + const createDefinition = (migrations: any) => ({ + kibanaVersion: '3.2.3', + typeRegistry: createRegistry({ + name: 'foo', + migrations: migrations as any, + }), + log: mockLogger, + }); - it('validates migration definition', () => { - expect(() => new DocumentMigrator(createDefinition(() => {}))).not.toThrow(); - expect(() => new DocumentMigrator(createDefinition({}))).not.toThrow(); - expect(() => new DocumentMigrator(createDefinition(123))).toThrow( - /Migration for type foo should be an object or a function/i - ); - }); + describe('#prepareMigrations', () => { + it('validates individual migration definitions', () => { + const invalidMigrator = new DocumentMigrator(createDefinition(() => 123)); + const voidMigrator = new DocumentMigrator(createDefinition(() => {})); + const emptyObjectMigrator = new DocumentMigrator(createDefinition(() => ({}))); - describe('#prepareMigrations', () => { - it('validates individual migration definitions', () => { - const invalidMigrator = new DocumentMigrator(createDefinition(() => 123)); - const voidMigrator = new DocumentMigrator(createDefinition(() => {})); - const emptyObjectMigrator = new DocumentMigrator(createDefinition(() => ({}))); + expect(invalidMigrator.prepareMigrations).toThrow( + /Migrations map for type foo should be an object/i + ); + expect(voidMigrator.prepareMigrations).not.toThrow(); + expect(emptyObjectMigrator.prepareMigrations).not.toThrow(); + }); - expect(invalidMigrator.prepareMigrations).toThrow( - /Migrations map for type foo should be an object/i - ); - expect(voidMigrator.prepareMigrations).not.toThrow(); - expect(emptyObjectMigrator.prepareMigrations).not.toThrow(); - }); + it('validates individual migrations are valid semvers', () => { + const withInvalidVersion = { + bar: (doc: any) => doc, + '1.2.3': (doc: any) => doc, + }; + const migrationFn = new DocumentMigrator(createDefinition(() => withInvalidVersion)); + const migrationObj = new DocumentMigrator(createDefinition(withInvalidVersion)); - it('validates individual migration semvers', () => { - const withInvalidVersion = { - bar: (doc: any) => doc, - '1.2.3': (doc: any) => doc, - }; - const migrationFn = new DocumentMigrator(createDefinition(() => withInvalidVersion)); - const migrationObj = new DocumentMigrator(createDefinition(withInvalidVersion)); + expect(migrationFn.prepareMigrations).toThrow(/Expected all properties to be semvers/i); + expect(migrationObj.prepareMigrations).toThrow(/Expected all properties to be semvers/i); + }); - expect(migrationFn.prepareMigrations).toThrow(/Expected all properties to be semvers/i); - expect(migrationObj.prepareMigrations).toThrow(/Expected all properties to be semvers/i); - }); + it('validates individual migrations are not greater than the current Kibana version', () => { + const withGreaterVersion = { + '3.2.4': (doc: any) => doc, + }; + const migrationFn = new DocumentMigrator(createDefinition(() => withGreaterVersion)); + const migrationObj = new DocumentMigrator(createDefinition(withGreaterVersion)); - it('validates the migration function', () => { - const invalidVersionFunction = { '1.2.3': 23 as any }; - const migrationFn = new DocumentMigrator(createDefinition(() => invalidVersionFunction)); - const migrationObj = new DocumentMigrator(createDefinition(invalidVersionFunction)); + const expectedError = `Invalid migration for type foo. Property '3.2.4' cannot be greater than the current Kibana version '3.2.3'.`; + expect(migrationFn.prepareMigrations).toThrowError(expectedError); + expect(migrationObj.prepareMigrations).toThrowError(expectedError); + }); - expect(migrationFn.prepareMigrations).toThrow(/expected a function, but got 23/i); - expect(migrationObj.prepareMigrations).toThrow(/expected a function, but got 23/i); - }); - it('validates definitions with migrations: Function | Objects', () => { - const validMigrationMap = { '1.2.3': () => {} }; - const migrationFn = new DocumentMigrator(createDefinition(() => validMigrationMap)); - const migrationObj = new DocumentMigrator(createDefinition(validMigrationMap)); - expect(migrationFn.prepareMigrations).not.toThrow(); - expect(migrationObj.prepareMigrations).not.toThrow(); - }); - }); + it('validates the migration function', () => { + const invalidVersionFunction = { '1.2.3': 23 as any }; + const migrationFn = new DocumentMigrator(createDefinition(() => invalidVersionFunction)); + const migrationObj = new DocumentMigrator(createDefinition(invalidVersionFunction)); - it('throws if #prepareMigrations is not called before #migrate is called', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'user', - migrations: { - '1.2.3': setAttr('attributes.name', 'Chris'), - }, - }), + expect(migrationFn.prepareMigrations).toThrow(/expected a function, but got 23/i); + expect(migrationObj.prepareMigrations).toThrow(/expected a function, but got 23/i); + }); + it('validates definitions with migrations: Function | Objects', () => { + const validMigrationMap = { '1.2.3': () => {} }; + const migrationFn = new DocumentMigrator(createDefinition(() => validMigrationMap)); + const migrationObj = new DocumentMigrator(createDefinition(validMigrationMap)); + expect(migrationFn.prepareMigrations).not.toThrow(); + expect(migrationObj.prepareMigrations).not.toThrow(); + }); }); - expect(() => - migrator.migrate({ - id: 'me', - type: 'user', - attributes: { name: 'Christopher' }, - migrationVersion: {}, - }) - ).toThrow(/Migrations are not ready. Make sure prepareMigrations is called first./i); - }); + it('throws if #prepareMigrations is not called before #migrate or #migrateAndConvert is called', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'user', + migrations: { + '1.2.3': setAttr('attributes.name', 'Chris'), + }, + }), + }); - it('migrates type and attributes', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'user', - migrations: { - '1.2.3': setAttr('attributes.name', 'Chris'), - }, - }), - }); - migrator.prepareMigrations(); + expect(() => + migrator.migrate({ + id: 'me', + type: 'user', + attributes: { name: 'Christopher' }, + migrationVersion: {}, + }) + ).toThrow(/Migrations are not ready. Make sure prepareMigrations is called first./i); - const actual = migrator.migrate({ - id: 'me', - type: 'user', - attributes: { name: 'Christopher' }, - migrationVersion: {}, + expect(() => + migrator.migrateAndConvert({ + id: 'me', + type: 'user', + attributes: { name: 'Christopher' }, + migrationVersion: {}, + }) + ).toThrow(/Migrations are not ready. Make sure prepareMigrations is called first./i); }); - expect(actual).toEqual({ - id: 'me', - type: 'user', - attributes: { name: 'Chris' }, - migrationVersion: { user: '1.2.3' }, - }); - }); - it(`doesn't mutate the original document`, () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'user', - migrations: { - '1.2.3': (doc) => { - set(doc, 'attributes.name', 'Mike'); - return doc; - }, - }, - }), + it(`validates convertToMultiNamespaceTypeVersion can only be used with namespaceType 'multiple'`, () => { + const invalidDefinition = { + kibanaVersion: '3.2.3', + typeRegistry: createRegistry({ + name: 'foo', + convertToMultiNamespaceTypeVersion: 'bar', + }), + minimumConvertVersion: '0.0.0', + log: mockLogger, + }; + expect(() => new DocumentMigrator(invalidDefinition)).toThrow( + `Invalid convertToMultiNamespaceTypeVersion for type foo. Expected namespaceType to be 'multiple', but got 'single'.` + ); }); - const originalDoc = { - id: 'me', - type: 'user', - attributes: {}, - migrationVersion: {}, - }; - migrator.prepareMigrations(); - const migratedDoc = migrator.migrate(originalDoc); - expect(_.get(originalDoc, 'attributes.name')).toBeUndefined(); - expect(_.get(migratedDoc, 'attributes.name')).toBe('Mike'); - }); - it('migrates root properties', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'acl', - migrations: { - '2.3.5': setAttr('acl', 'admins-only, sucka!'), - }, - }), + it(`validates convertToMultiNamespaceTypeVersion must be a semver`, () => { + const invalidDefinition = { + kibanaVersion: '3.2.3', + typeRegistry: createRegistry({ + name: 'foo', + convertToMultiNamespaceTypeVersion: 'bar', + namespaceType: 'multiple', + }), + minimumConvertVersion: '0.0.0', + log: mockLogger, + }; + expect(() => new DocumentMigrator(invalidDefinition)).toThrow( + `Invalid convertToMultiNamespaceTypeVersion for type foo. Expected value to be a semver, but got 'bar'.` + ); }); - migrator.prepareMigrations(); - const actual = migrator.migrate({ - id: 'me', - type: 'user', - attributes: { name: 'Tyler' }, - acl: 'anyone', - migrationVersion: {}, - } as SavedObjectUnsanitizedDoc); - expect(actual).toEqual({ - id: 'me', - type: 'user', - attributes: { name: 'Tyler' }, - migrationVersion: { acl: '2.3.5' }, - acl: 'admins-only, sucka!', + + it('validates convertToMultiNamespaceTypeVersion is not less than the minimum allowed version', () => { + const invalidDefinition = { + kibanaVersion: '3.2.3', + typeRegistry: createRegistry({ + name: 'foo', + convertToMultiNamespaceTypeVersion: '3.2.4', + namespaceType: 'multiple', + }), + // not using a minimumConvertVersion parameter, the default is 8.0.0 + log: mockLogger, + }; + expect(() => new DocumentMigrator(invalidDefinition)).toThrowError( + `Invalid convertToMultiNamespaceTypeVersion for type foo. Value '3.2.4' cannot be less than '8.0.0'.` + ); }); - }); - it('does not apply migrations to unrelated docs', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry( - { - name: 'aaa', - migrations: { - '1.0.0': setAttr('aaa', 'A'), - }, - }, - { - name: 'bbb', - migrations: { - '1.0.0': setAttr('bbb', 'B'), - }, - }, - { - name: 'ccc', - migrations: { - '1.0.0': setAttr('ccc', 'C'), - }, - } - ), + it('validates convertToMultiNamespaceTypeVersion is not greater than the current Kibana version', () => { + const invalidDefinition = { + kibanaVersion: '3.2.3', + typeRegistry: createRegistry({ + name: 'foo', + convertToMultiNamespaceTypeVersion: '3.2.4', + namespaceType: 'multiple', + }), + minimumConvertVersion: '0.0.0', + log: mockLogger, + }; + expect(() => new DocumentMigrator(invalidDefinition)).toThrowError( + `Invalid convertToMultiNamespaceTypeVersion for type foo. Value '3.2.4' cannot be greater than the current Kibana version '3.2.3'.` + ); }); - migrator.prepareMigrations(); - const actual = migrator.migrate({ - id: 'me', - type: 'user', - attributes: { name: 'Tyler' }, - migrationVersion: {}, + + it('coerces the current Kibana version if it has a hyphen', () => { + const validDefinition = { + kibanaVersion: '3.2.0-SNAPSHOT', + typeRegistry: createRegistry({ + name: 'foo', + convertToMultiNamespaceTypeVersion: '3.2.0', + namespaceType: 'multiple', + }), + minimumConvertVersion: '0.0.0', + log: mockLogger, + }; + expect(() => new DocumentMigrator(validDefinition)).not.toThrowError(); }); - expect(actual).toEqual({ - id: 'me', - type: 'user', - attributes: { name: 'Tyler' }, + + it('validates convertToMultiNamespaceTypeVersion is not used on a patch version', () => { + const invalidDefinition = { + kibanaVersion: '3.2.3', + typeRegistry: createRegistry({ + name: 'foo', + convertToMultiNamespaceTypeVersion: '3.1.1', + namespaceType: 'multiple', + }), + minimumConvertVersion: '0.0.0', + log: mockLogger, + }; + expect(() => new DocumentMigrator(invalidDefinition)).toThrowError( + `Invalid convertToMultiNamespaceTypeVersion for type foo. Value '3.1.1' cannot be used on a patch version (must be like 'x.y.0').` + ); }); }); - it('assumes documents w/ undefined migrationVersion are up to date', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry( - { + describe('migration', () => { + it('migrates type and attributes', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ name: 'user', migrations: { - '1.0.0': setAttr('aaa', 'A'), + '1.2.3': setAttr('attributes.name', 'Chris'), }, - }, - { - name: 'bbb', + }), + }); + migrator.prepareMigrations(); + const actual = migrator.migrate({ + id: 'me', + type: 'user', + attributes: { name: 'Christopher' }, + migrationVersion: {}, + }); + expect(actual).toEqual({ + id: 'me', + type: 'user', + attributes: { name: 'Chris' }, + migrationVersion: { user: '1.2.3' }, + coreMigrationVersion: kibanaVersion, + }); + }); + + it(`doesn't mutate the original document`, () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'user', migrations: { - '2.3.4': setAttr('bbb', 'B'), + '1.2.3': (doc) => { + set(doc, 'attributes.name', 'Mike'); + return doc; + }, }, - }, - { - name: 'ccc', + }), + }); + migrator.prepareMigrations(); + const originalDoc = { + id: 'me', + type: 'user', + attributes: {}, + migrationVersion: {}, + }; + const migratedDoc = migrator.migrate(originalDoc); + expect(_.get(originalDoc, 'attributes.name')).toBeUndefined(); + expect(_.get(migratedDoc, 'attributes.name')).toBe('Mike'); + }); + + it('migrates root properties', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'acl', migrations: { - '1.0.0': setAttr('ccc', 'C'), + '2.3.5': setAttr('acl', 'admins-only, sucka!'), }, - } - ), + }), + }); + migrator.prepareMigrations(); + const actual = migrator.migrate({ + id: 'me', + type: 'user', + attributes: { name: 'Tyler' }, + acl: 'anyone', + migrationVersion: {}, + } as SavedObjectUnsanitizedDoc); + expect(actual).toEqual({ + id: 'me', + type: 'user', + attributes: { name: 'Tyler' }, + migrationVersion: { acl: '2.3.5' }, + acl: 'admins-only, sucka!', + coreMigrationVersion: kibanaVersion, + }); }); - migrator.prepareMigrations(); - const actual = migrator.migrate({ - id: 'me', - type: 'user', - attributes: { name: 'Tyler' }, - bbb: 'Shazm', - } as SavedObjectUnsanitizedDoc); - expect(actual).toEqual({ - id: 'me', - type: 'user', - attributes: { name: 'Tyler' }, - bbb: 'Shazm', - migrationVersion: { - user: '1.0.0', - bbb: '2.3.4', - }, + + it('does not apply migrations to unrelated docs', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry( + { + name: 'aaa', + migrations: { + '1.0.0': setAttr('aaa', 'A'), + }, + }, + { + name: 'bbb', + migrations: { + '1.0.0': setAttr('bbb', 'B'), + }, + }, + { + name: 'ccc', + migrations: { + '1.0.0': setAttr('ccc', 'C'), + }, + } + ), + }); + migrator.prepareMigrations(); + const actual = migrator.migrate({ + id: 'me', + type: 'user', + attributes: { name: 'Tyler' }, + migrationVersion: {}, + }); + expect(actual).toEqual({ + id: 'me', + type: 'user', + attributes: { name: 'Tyler' }, + coreMigrationVersion: kibanaVersion, + }); }); - }); - it('only applies migrations that are more recent than the doc', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'dog', - migrations: { - '1.2.3': setAttr('attributes.a', 'A'), - '1.2.4': setAttr('attributes.b', 'B'), - '2.0.1': setAttr('attributes.c', 'C'), + it('assumes documents w/ undefined migrationVersion and correct coreMigrationVersion are up to date', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry( + { + name: 'user', + migrations: { + '1.0.0': setAttr('aaa', 'A'), + }, + }, + { + name: 'bbb', + migrations: { + '2.3.4': setAttr('bbb', 'B'), + }, + }, + { + name: 'ccc', + migrations: { + '1.0.0': setAttr('ccc', 'C'), + }, + } + ), + }); + migrator.prepareMigrations(); + const actual = migrator.migrate({ + id: 'me', + type: 'user', + attributes: { name: 'Tyler' }, + bbb: 'Shazm', + coreMigrationVersion: kibanaVersion, + } as SavedObjectUnsanitizedDoc); + expect(actual).toEqual({ + id: 'me', + type: 'user', + attributes: { name: 'Tyler' }, + bbb: 'Shazm', + migrationVersion: { + user: '1.0.0', + bbb: '2.3.4', }, - }), + coreMigrationVersion: kibanaVersion, + }); }); - migrator.prepareMigrations(); - const actual = migrator.migrate({ - id: 'smelly', - type: 'dog', - attributes: { name: 'Callie' }, - migrationVersion: { dog: '1.2.3' }, - }); - expect(actual).toEqual({ - id: 'smelly', - type: 'dog', - attributes: { name: 'Callie', b: 'B', c: 'C' }, - migrationVersion: { dog: '2.0.1' }, - }); - }); - it('rejects docs that belong to a newer Kibana instance', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - kibanaVersion: '8.0.1', - }); - migrator.prepareMigrations(); - expect(() => - migrator.migrate({ + it('only applies migrations that are more recent than the doc', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'dog', + migrations: { + '1.2.3': setAttr('attributes.a', 'A'), + '1.2.4': setAttr('attributes.b', 'B'), + '2.0.1': setAttr('attributes.c', 'C'), + }, + }), + }); + migrator.prepareMigrations(); + const actual = migrator.migrate({ id: 'smelly', type: 'dog', attributes: { name: 'Callie' }, - migrationVersion: { dog: '10.2.0' }, - }) - ).toThrow( - /Document "smelly" has property "dog" which belongs to a more recent version of Kibana \[10\.2\.0\]\. The last known version is \[undefined\]/i - ); - }); + migrationVersion: { dog: '1.2.3' }, + }); + expect(actual).toEqual({ + id: 'smelly', + type: 'dog', + attributes: { name: 'Callie', b: 'B', c: 'C' }, + migrationVersion: { dog: '2.0.1' }, + coreMigrationVersion: kibanaVersion, + }); + }); - it('rejects docs that belong to a newer plugin', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'dawg', - migrations: { - '1.2.3': setAttr('attributes.a', 'A'), - }, - }), + it('rejects docs with a migrationVersion[type] for a type that does not have any migrations defined', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + }); + migrator.prepareMigrations(); + expect(() => + migrator.migrate({ + id: 'smelly', + type: 'dog', + attributes: { name: 'Callie' }, + migrationVersion: { dog: '10.2.0' }, + }) + ).toThrow( + /Document "smelly" has property "dog" which belongs to a more recent version of Kibana \[10\.2\.0\]\. The last known version is \[undefined\]/i + ); }); - migrator.prepareMigrations(); - expect(() => - migrator.migrate({ - id: 'fleabag', - type: 'dawg', - attributes: { name: 'Callie' }, - migrationVersion: { dawg: '1.2.4' }, - }) - ).toThrow( - /Document "fleabag" has property "dawg" which belongs to a more recent version of Kibana \[1\.2\.4\]\. The last known version is \[1\.2\.3\]/i - ); - }); - it('applies migrations in order', () => { - let count = 0; - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'dog', - migrations: { - '2.2.4': setAttr('attributes.b', () => ++count), - '10.0.1': setAttr('attributes.c', () => ++count), - '1.2.3': setAttr('attributes.a', () => ++count), - }, - }), + it('rejects docs with a migrationVersion[type] for a type that does not have a migration >= that version defined', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'dawg', + migrations: { + '1.2.3': setAttr('attributes.a', 'A'), + }, + }), + }); + migrator.prepareMigrations(); + expect(() => + migrator.migrate({ + id: 'fleabag', + type: 'dawg', + attributes: { name: 'Callie' }, + migrationVersion: { dawg: '1.2.4' }, + }) + ).toThrow( + /Document "fleabag" has property "dawg" which belongs to a more recent version of Kibana \[1\.2\.4\]\. The last known version is \[1\.2\.3\]/i + ); }); - migrator.prepareMigrations(); - const actual = migrator.migrate({ - id: 'smelly', - type: 'dog', - attributes: { name: 'Callie' }, - migrationVersion: { dog: '1.2.0' }, + + it('rejects docs that have an invalid coreMigrationVersion', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + kibanaVersion: '8.0.1', + }); + migrator.prepareMigrations(); + expect(() => + migrator.migrate({ + id: 'happy', + type: 'dog', + attributes: { name: 'Callie' }, + coreMigrationVersion: 'not-a-semver', + }) + ).toThrowErrorMatchingInlineSnapshot( + `"Document \\"happy\\" has an invalid \\"coreMigrationVersion\\" [not-a-semver]. This must be a semver value."` + ); }); - expect(actual).toEqual({ - id: 'smelly', - type: 'dog', - attributes: { name: 'Callie', a: 1, b: 2, c: 3 }, - migrationVersion: { dog: '10.0.1' }, + + it('rejects docs that have a coreMigrationVersion higher than the current Kibana version', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + kibanaVersion: '8.0.1', + }); + migrator.prepareMigrations(); + expect(() => + migrator.migrate({ + id: 'wet', + type: 'dog', + attributes: { name: 'Callie' }, + coreMigrationVersion: '8.0.2', + }) + ).toThrowErrorMatchingInlineSnapshot( + `"Document \\"wet\\" has a \\"coreMigrationVersion\\" which belongs to a more recent version of Kibana [8.0.2]. The current version is [8.0.1]."` + ); }); - }); - it('allows props to be added', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry( - { - name: 'animal', - migrations: { - '1.0.0': setAttr('animal', (name: string) => `Animal: ${name}`), - }, - }, - { + it('applies migrations in order', () => { + let count = 0; + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ name: 'dog', migrations: { - '2.2.4': setAttr('animal', 'Doggie'), + '2.2.4': setAttr('attributes.b', () => ++count), + '10.0.1': setAttr('attributes.c', () => ++count), + '1.2.3': setAttr('attributes.a', () => ++count), }, - } - ), - }); - migrator.prepareMigrations(); - const actual = migrator.migrate({ - id: 'smelly', - type: 'dog', - attributes: { name: 'Callie' }, - migrationVersion: { dog: '1.2.0' }, - }); - expect(actual).toEqual({ - id: 'smelly', - type: 'dog', - attributes: { name: 'Callie' }, - animal: 'Animal: Doggie', - migrationVersion: { animal: '1.0.0', dog: '2.2.4' }, + }), + }); + migrator.prepareMigrations(); + const actual = migrator.migrate({ + id: 'smelly', + type: 'dog', + attributes: { name: 'Callie' }, + migrationVersion: { dog: '1.2.0' }, + }); + expect(actual).toEqual({ + id: 'smelly', + type: 'dog', + attributes: { name: 'Callie', a: 1, b: 2, c: 3 }, + migrationVersion: { dog: '10.0.1' }, + coreMigrationVersion: kibanaVersion, + }); }); - }); - it('allows props to be renamed', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry( - { - name: 'animal', - migrations: { - '1.0.0': setAttr('animal', (name: string) => `Animal: ${name}`), - '3.2.1': renameAttr('animal', 'dawg'), + it('allows props to be added', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry( + { + name: 'animal', + migrations: { + '1.0.0': setAttr('animal', (name: string) => `Animal: ${name}`), + }, }, - }, - { - name: 'dawg', + { + name: 'dog', + migrations: { + '2.2.4': setAttr('animal', 'Doggie'), + }, + } + ), + }); + migrator.prepareMigrations(); + const actual = migrator.migrate({ + id: 'smelly', + type: 'dog', + attributes: { name: 'Callie' }, + migrationVersion: { dog: '1.2.0' }, + }); + expect(actual).toEqual({ + id: 'smelly', + type: 'dog', + attributes: { name: 'Callie' }, + animal: 'Animal: Doggie', + migrationVersion: { animal: '1.0.0', dog: '2.2.4' }, + coreMigrationVersion: kibanaVersion, + }); + }); + + it('allows props to be renamed', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'dog', migrations: { - '2.2.4': renameAttr('dawg', 'animal'), - '3.2.0': setAttr('dawg', (name: string) => `Dawg3.x: ${name}`), + '1.0.0': setAttr('attributes.name', (name: string) => `Name: ${name}`), + '1.0.1': renameAttr('attributes.name', 'attributes.title'), + '1.0.2': setAttr('attributes.title', (name: string) => `Title: ${name}`), }, - } - ), + }), + }); + migrator.prepareMigrations(); + const actual = migrator.migrate({ + id: 'smelly', + type: 'dog', + attributes: { name: 'Callie' }, + migrationVersion: {}, + }); + expect(actual).toEqual({ + id: 'smelly', + type: 'dog', + attributes: { title: 'Title: Name: Callie' }, + migrationVersion: { dog: '1.0.2' }, + coreMigrationVersion: kibanaVersion, + }); }); - migrator.prepareMigrations(); - const actual = migrator.migrate({ - id: 'smelly', - type: 'foo', - attributes: { name: 'Callie' }, - dawg: 'Yo', - migrationVersion: {}, - } as SavedObjectUnsanitizedDoc); - expect(actual).toEqual({ - id: 'smelly', - type: 'foo', - attributes: { name: 'Callie' }, - dawg: 'Dawg3.x: Animal: Yo', - migrationVersion: { animal: '3.2.1', dawg: '3.2.0' }, + + it('allows changing type', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry( + { + name: 'cat', + migrations: { + '1.0.0': setAttr('attributes.name', (name: string) => `Kitty ${name}`), + }, + }, + { + name: 'dog', + migrations: { + '2.2.4': setAttr('type', 'cat'), + }, + } + ), + }); + migrator.prepareMigrations(); + const actual = migrator.migrate({ + id: 'smelly', + type: 'dog', + attributes: { name: 'Callie' }, + migrationVersion: {}, + }); + expect(actual).toEqual({ + id: 'smelly', + type: 'cat', + attributes: { name: 'Kitty Callie' }, + migrationVersion: { dog: '2.2.4', cat: '1.0.0' }, + coreMigrationVersion: kibanaVersion, + }); }); - }); - it('allows changing type', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry( - { + it('disallows updating a migrationVersion prop to a lower version', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ name: 'cat', migrations: { - '1.0.0': setAttr('attributes.name', (name: string) => `Kitty ${name}`), + '1.0.0': setAttr('migrationVersion.foo', '3.2.1'), }, - }, - { - name: 'dog', + }), + }); + migrator.prepareMigrations(); + expect(() => + migrator.migrate({ + id: 'smelly', + type: 'cat', + attributes: { name: 'Boo' }, + migrationVersion: { foo: '4.5.6' }, + }) + ).toThrow( + /Migration "cat v 1.0.0" attempted to downgrade "migrationVersion.foo" from 4.5.6 to 3.2.1./ + ); + }); + + it('disallows removing a migrationVersion prop', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'cat', migrations: { - '2.2.4': setAttr('type', 'cat'), + '1.0.0': setAttr('migrationVersion', {}), }, - } - ), - }); - migrator.prepareMigrations(); - const actual = migrator.migrate({ - id: 'smelly', - type: 'dog', - attributes: { name: 'Callie' }, - migrationVersion: {}, - }); - expect(actual).toEqual({ - id: 'smelly', - type: 'cat', - attributes: { name: 'Kitty Callie' }, - migrationVersion: { dog: '2.2.4', cat: '1.0.0' }, + }), + }); + migrator.prepareMigrations(); + expect(() => + migrator.migrate({ + id: 'smelly', + type: 'cat', + attributes: { name: 'Boo' }, + migrationVersion: { foo: '4.5.6' }, + }) + ).toThrow( + /Migration "cat v 1.0.0" attempted to downgrade "migrationVersion.foo" from 4.5.6 to undefined./ + ); }); - }); - it('disallows updating a migrationVersion prop to a lower version', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'cat', - migrations: { - '1.0.0': setAttr('migrationVersion.foo', '3.2.1'), - }, - }), + it('allows updating a migrationVersion prop to a later version', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'cat', + migrations: { + '1.0.0': setAttr('migrationVersion.cat', '2.9.1'), + '2.0.0': () => { + throw new Error('POW!'); + }, + '2.9.1': () => { + throw new Error('BANG!'); + }, + '3.0.0': setAttr('attributes.name', 'Shiny'), + }, + }), + }); + migrator.prepareMigrations(); + const actual = migrator.migrate({ + id: 'smelly', + type: 'cat', + attributes: { name: 'Boo' }, + migrationVersion: { cat: '0.5.6' }, + }); + expect(actual).toEqual({ + id: 'smelly', + type: 'cat', + attributes: { name: 'Shiny' }, + migrationVersion: { cat: '3.0.0' }, + coreMigrationVersion: kibanaVersion, + }); }); - migrator.prepareMigrations(); - expect(() => - migrator.migrate({ + it('allows adding props to migrationVersion', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'cat', + migrations: { + '1.0.0': setAttr('migrationVersion.foo', '5.6.7'), + }, + }), + }); + migrator.prepareMigrations(); + const actual = migrator.migrate({ id: 'smelly', type: 'cat', attributes: { name: 'Boo' }, - migrationVersion: { foo: '4.5.6' }, - }) - ).toThrow( - /Migration "cat v 1.0.0" attempted to downgrade "migrationVersion.foo" from 4.5.6 to 3.2.1./ - ); - }); - - it('disallows removing a migrationVersion prop', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'cat', - migrations: { - '1.0.0': setAttr('migrationVersion', {}), - }, - }), - }); - migrator.prepareMigrations(); - expect(() => - migrator.migrate({ + migrationVersion: {}, + }); + expect(actual).toEqual({ id: 'smelly', type: 'cat', attributes: { name: 'Boo' }, - migrationVersion: { foo: '4.5.6' }, - }) - ).toThrow( - /Migration "cat v 1.0.0" attempted to downgrade "migrationVersion.foo" from 4.5.6 to undefined./ - ); - }); - - it('allows updating a migrationVersion prop to a later version', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'cat', - migrations: { - '1.0.0': setAttr('migrationVersion.cat', '2.9.1'), - '2.0.0': () => { - throw new Error('POW!'); - }, - '2.9.1': () => { - throw new Error('BANG!'); - }, - '3.0.0': setAttr('attributes.name', 'Shiny'), - }, - }), - }); - migrator.prepareMigrations(); - const actual = migrator.migrate({ - id: 'smelly', - type: 'cat', - attributes: { name: 'Boo' }, - migrationVersion: { cat: '0.5.6' }, - }); - expect(actual).toEqual({ - id: 'smelly', - type: 'cat', - attributes: { name: 'Shiny' }, - migrationVersion: { cat: '3.0.0' }, - }); - }); - - it('allows adding props to migrationVersion', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'cat', - migrations: { - '1.0.0': setAttr('migrationVersion.foo', '5.6.7'), - }, - }), - }); - migrator.prepareMigrations(); - const actual = migrator.migrate({ - id: 'smelly', - type: 'cat', - attributes: { name: 'Boo' }, - migrationVersion: {}, - }); - expect(actual).toEqual({ - id: 'smelly', - type: 'cat', - attributes: { name: 'Boo' }, - migrationVersion: { cat: '1.0.0', foo: '5.6.7' }, + migrationVersion: { cat: '1.0.0', foo: '5.6.7' }, + coreMigrationVersion: kibanaVersion, + }); }); - }); - it('logs the document and transform that failed', () => { - const log = mockLogger; - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'dog', - migrations: { - '1.2.3': () => { - throw new Error('Dang diggity!'); + it('logs the document and transform that failed', () => { + const log = mockLogger; + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'dog', + migrations: { + '1.2.3': () => { + throw new Error('Dang diggity!'); + }, }, - }, - }), - log, - }); - const failedDoc = { - id: 'smelly', - type: 'dog', - attributes: {}, - migrationVersion: {}, - }; - try { + }), + log, + }); migrator.prepareMigrations(); - migrator.migrate(_.cloneDeep(failedDoc)); - expect('Did not throw').toEqual('But it should have!'); - } catch (error) { - expect(error.message).toMatch(/Dang diggity!/); - const warning = loggingSystemMock.collect(mockLoggerFactory).warn[0][0]; - expect(warning).toContain(JSON.stringify(failedDoc)); - expect(warning).toContain('dog:1.2.3'); - } - }); + const failedDoc = { + id: 'smelly', + type: 'dog', + attributes: {}, + migrationVersion: {}, + }; + try { + migrator.migrate(_.cloneDeep(failedDoc)); + expect('Did not throw').toEqual('But it should have!'); + } catch (error) { + expect(error.message).toMatch(/Dang diggity!/); + const warning = loggingSystemMock.collect(mockLoggerFactory).warn[0][0]; + expect(warning).toContain(JSON.stringify(failedDoc)); + expect(warning).toContain('dog:1.2.3'); + } + }); - it('logs message in transform function', () => { - const logTestMsg = '...said the joker to the thief'; - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry({ - name: 'dog', - migrations: { - '1.2.3': (doc, { log }) => { - log.info(logTestMsg); - log.warning(logTestMsg); - return doc; + it('logs message in transform function', () => { + const logTestMsg = '...said the joker to the thief'; + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'dog', + migrations: { + '1.2.3': (doc, { log }) => { + log.info(logTestMsg); + log.warning(logTestMsg); + return doc; + }, }, - }, - }), - log: mockLogger, + }), + log: mockLogger, + }); + migrator.prepareMigrations(); + const doc = { + id: 'joker', + type: 'dog', + attributes: {}, + migrationVersion: {}, + }; + migrator.migrate(doc); + expect(loggingSystemMock.collect(mockLoggerFactory).info[0][0]).toEqual(logTestMsg); + expect(loggingSystemMock.collect(mockLoggerFactory).warn[1][0]).toEqual(logTestMsg); }); - const doc = { - id: 'joker', - type: 'dog', - attributes: {}, - migrationVersion: {}, - }; - migrator.prepareMigrations(); - migrator.migrate(doc); - expect(loggingSystemMock.collect(mockLoggerFactory).info[0][0]).toEqual(logTestMsg); - expect(loggingSystemMock.collect(mockLoggerFactory).warn[1][0]).toEqual(logTestMsg); - }); - test('extracts the latest migration version info', () => { - const migrator = new DocumentMigrator({ - ...testOpts(), - typeRegistry: createRegistry( - { - name: 'aaa', - migrations: { - '1.2.3': (doc: SavedObjectUnsanitizedDoc) => doc, - '10.4.0': (doc: SavedObjectUnsanitizedDoc) => doc, - '2.2.1': (doc: SavedObjectUnsanitizedDoc) => doc, + test('extracts the latest migration version info', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry( + { + name: 'aaa', + migrations: { + '1.2.3': (doc: SavedObjectUnsanitizedDoc) => doc, + '10.4.0': (doc: SavedObjectUnsanitizedDoc) => doc, + '2.2.1': (doc: SavedObjectUnsanitizedDoc) => doc, + }, }, - }, - { - name: 'bbb', - migrations: { - '3.2.3': (doc: SavedObjectUnsanitizedDoc) => doc, - '2.0.0': (doc: SavedObjectUnsanitizedDoc) => doc, + { + name: 'bbb', + migrations: { + '3.2.3': (doc: SavedObjectUnsanitizedDoc) => doc, + '2.0.0': (doc: SavedObjectUnsanitizedDoc) => doc, + }, }, - } - ), + { + name: 'ccc', + namespaceType: 'multiple', + migrations: { + '9.0.0': (doc: SavedObjectUnsanitizedDoc) => doc, + }, + convertToMultiNamespaceTypeVersion: '11.0.0', // this results in reference transforms getting added to other types, but does not increase the migrationVersion of those types + } + ), + }); + migrator.prepareMigrations(); + expect(migrator.migrationVersion).toEqual({ + aaa: '10.4.0', + bbb: '3.2.3', + ccc: '11.0.0', + }); }); - migrator.prepareMigrations(); - expect(migrator.migrationVersion).toEqual({ - aaa: '10.4.0', - bbb: '3.2.3', + describe('conversion to multi-namespace type', () => { + it('assumes documents w/ undefined migrationVersion and correct coreMigrationVersion are up to date', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry( + { name: 'dog', namespaceType: 'multiple', convertToMultiNamespaceTypeVersion: '1.0.0' } + // no migration transforms are defined, the migrationVersion will be derived from 'convertToMultiNamespaceTypeVersion' + ), + }); + migrator.prepareMigrations(); + const obj = { + id: 'mischievous', + type: 'dog', + attributes: { name: 'Ann' }, + coreMigrationVersion: kibanaVersion, + } as SavedObjectUnsanitizedDoc; + const actual = migrator.migrateAndConvert(obj); + expect(actual).toEqual([ + { + id: 'mischievous', + type: 'dog', + attributes: { name: 'Ann' }, + migrationVersion: { dog: '1.0.0' }, + coreMigrationVersion: kibanaVersion, + // there is no 'namespaces' field because no transforms were applied; this scenario is contrived for a clean test case but is not indicative of a real-world scenario + }, + ]); + }); + + it('skips reference transforms and conversion transforms when using `migrate`', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry( + { name: 'dog', namespaceType: 'multiple', convertToMultiNamespaceTypeVersion: '1.0.0' }, + { name: 'toy', namespaceType: 'multiple', convertToMultiNamespaceTypeVersion: '1.0.0' } + ), + }); + migrator.prepareMigrations(); + const obj = { + id: 'cowardly', + type: 'dog', + attributes: { name: 'Leslie' }, + migrationVersion: {}, + references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], + namespace: 'foo-namespace', + }; + const actual = migrator.migrate(obj); + expect(mockUuidv5).not.toHaveBeenCalled(); + expect(actual).toEqual({ + id: 'cowardly', + type: 'dog', + attributes: { name: 'Leslie' }, + migrationVersion: { dog: '1.0.0' }, + references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], + coreMigrationVersion: kibanaVersion, + namespace: 'foo-namespace', + // there is no 'namespaces' field because no conversion transform was applied; this scenario is contrived for a clean test case but is not indicative of a real-world scenario + }); + }); + + describe('correctly applies reference transforms', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry( + { name: 'dog', namespaceType: 'single' }, + { name: 'toy', namespaceType: 'multiple', convertToMultiNamespaceTypeVersion: '1.0.0' } + ), + }); + migrator.prepareMigrations(); + const obj = { + id: 'bad', + type: 'dog', + attributes: { name: 'Sweet Peach' }, + migrationVersion: {}, + references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], + }; + + it('in the default space', () => { + const actual = migrator.migrateAndConvert(obj); + expect(mockUuidv5).not.toHaveBeenCalled(); + expect(actual).toEqual([ + { + id: 'bad', + type: 'dog', + attributes: { name: 'Sweet Peach' }, + references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], // no change + coreMigrationVersion: kibanaVersion, + }, + ]); + }); + + it('in a non-default space', () => { + const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); + expect(mockUuidv5).toHaveBeenCalledTimes(1); + expect(mockUuidv5).toHaveBeenCalledWith('foo-namespace:toy:favorite', 'DNSUUID'); + expect(actual).toEqual([ + { + id: 'bad', + type: 'dog', + attributes: { name: 'Sweet Peach' }, + references: [{ id: 'uuidv5', type: 'toy', name: 'BALL!' }], // changed + coreMigrationVersion: kibanaVersion, + namespace: 'foo-namespace', + }, + ]); + }); + }); + + describe('correctly applies conversion transforms', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'dog', + namespaceType: 'multiple', + convertToMultiNamespaceTypeVersion: '1.0.0', + }), + }); + migrator.prepareMigrations(); + const obj = { + id: 'loud', + type: 'dog', + attributes: { name: 'Wally' }, + migrationVersion: {}, + }; + + it('in the default space', () => { + const actual = migrator.migrateAndConvert(obj); + expect(mockUuidv5).not.toHaveBeenCalled(); + expect(actual).toEqual([ + { + id: 'loud', + type: 'dog', + attributes: { name: 'Wally' }, + migrationVersion: { dog: '1.0.0' }, + coreMigrationVersion: kibanaVersion, + namespaces: ['default'], + }, + ]); + }); + + it('in a non-default space', () => { + const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); + expect(mockUuidv5).toHaveBeenCalledTimes(1); + expect(mockUuidv5).toHaveBeenCalledWith('foo-namespace:dog:loud', 'DNSUUID'); + expect(actual).toEqual([ + { + id: 'uuidv5', + type: 'dog', + attributes: { name: 'Wally' }, + migrationVersion: { dog: '1.0.0' }, + coreMigrationVersion: kibanaVersion, + namespaces: ['foo-namespace'], + originId: 'loud', + }, + { + id: 'foo-namespace:dog:loud', + type: LEGACY_URL_ALIAS_TYPE, + attributes: { + targetNamespace: 'foo-namespace', + targetType: 'dog', + targetId: 'uuidv5', + }, + migrationVersion: {}, + coreMigrationVersion: kibanaVersion, + }, + ]); + }); + }); + + describe('correctly applies reference and conversion transforms', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry( + { name: 'dog', namespaceType: 'multiple', convertToMultiNamespaceTypeVersion: '1.0.0' }, + { name: 'toy', namespaceType: 'multiple', convertToMultiNamespaceTypeVersion: '1.0.0' } + ), + }); + migrator.prepareMigrations(); + const obj = { + id: 'cute', + type: 'dog', + attributes: { name: 'Too' }, + migrationVersion: {}, + references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], + }; + + it('in the default space', () => { + const actual = migrator.migrateAndConvert(obj); + expect(mockUuidv5).not.toHaveBeenCalled(); + expect(actual).toEqual([ + { + id: 'cute', + type: 'dog', + attributes: { name: 'Too' }, + migrationVersion: { dog: '1.0.0' }, + references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], // no change + coreMigrationVersion: kibanaVersion, + namespaces: ['default'], + }, + ]); + }); + + it('in a non-default space', () => { + const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); + expect(mockUuidv5).toHaveBeenCalledTimes(2); + expect(mockUuidv5).toHaveBeenNthCalledWith(1, 'foo-namespace:toy:favorite', 'DNSUUID'); + expect(mockUuidv5).toHaveBeenNthCalledWith(2, 'foo-namespace:dog:cute', 'DNSUUID'); + expect(actual).toEqual([ + { + id: 'uuidv5', + type: 'dog', + attributes: { name: 'Too' }, + migrationVersion: { dog: '1.0.0' }, + references: [{ id: 'uuidv5', type: 'toy', name: 'BALL!' }], // changed + coreMigrationVersion: kibanaVersion, + namespaces: ['foo-namespace'], + originId: 'cute', + }, + { + id: 'foo-namespace:dog:cute', + type: LEGACY_URL_ALIAS_TYPE, + attributes: { + targetNamespace: 'foo-namespace', + targetType: 'dog', + targetId: 'uuidv5', + }, + migrationVersion: {}, + coreMigrationVersion: kibanaVersion, + }, + ]); + }); + }); + + describe('correctly applies reference and migration transforms', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry( + { + name: 'dog', + namespaceType: 'single', + migrations: { + '1.0.0': setAttr('migrationVersion.dog', '2.0.0'), + '2.0.0': (doc) => doc, // noop + }, + }, + { name: 'toy', namespaceType: 'multiple', convertToMultiNamespaceTypeVersion: '1.0.0' } + ), + }); + migrator.prepareMigrations(); + const obj = { + id: 'sleepy', + type: 'dog', + attributes: { name: 'Patches' }, + migrationVersion: {}, + references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], + }; + + it('in the default space', () => { + const actual = migrator.migrateAndConvert(obj); + expect(mockUuidv5).not.toHaveBeenCalled(); + expect(actual).toEqual([ + { + id: 'sleepy', + type: 'dog', + attributes: { name: 'Patches' }, + migrationVersion: { dog: '2.0.0' }, + references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], // no change + coreMigrationVersion: kibanaVersion, + }, + ]); + }); + + it('in a non-default space', () => { + const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); + expect(mockUuidv5).toHaveBeenCalledTimes(1); + expect(mockUuidv5).toHaveBeenCalledWith('foo-namespace:toy:favorite', 'DNSUUID'); + expect(actual).toEqual([ + { + id: 'sleepy', + type: 'dog', + attributes: { name: 'Patches' }, + migrationVersion: { dog: '2.0.0' }, + references: [{ id: 'uuidv5', type: 'toy', name: 'BALL!' }], // changed + coreMigrationVersion: kibanaVersion, + namespace: 'foo-namespace', + }, + ]); + }); + }); + + describe('correctly applies conversion and migration transforms', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry({ + name: 'dog', + namespaceType: 'multiple', + migrations: { + '1.0.0': setAttr('migrationVersion.dog', '2.0.0'), + '2.0.0': (doc) => doc, // noop + }, + convertToMultiNamespaceTypeVersion: '1.0.0', // the conversion transform occurs before the migration transform above + }), + }); + migrator.prepareMigrations(); + const obj = { + id: 'hungry', + type: 'dog', + attributes: { name: 'Remy' }, + migrationVersion: {}, + }; + + it('in the default space', () => { + const actual = migrator.migrateAndConvert(obj); + expect(mockUuidv5).not.toHaveBeenCalled(); + expect(actual).toEqual([ + { + id: 'hungry', + type: 'dog', + attributes: { name: 'Remy' }, + migrationVersion: { dog: '2.0.0' }, + coreMigrationVersion: kibanaVersion, + namespaces: ['default'], + }, + ]); + }); + + it('in a non-default space', () => { + const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); + expect(mockUuidv5).toHaveBeenCalledTimes(1); + expect(mockUuidv5).toHaveBeenCalledWith('foo-namespace:dog:hungry', 'DNSUUID'); + expect(actual).toEqual([ + { + id: 'uuidv5', + type: 'dog', + attributes: { name: 'Remy' }, + migrationVersion: { dog: '2.0.0' }, + coreMigrationVersion: kibanaVersion, + namespaces: ['foo-namespace'], + originId: 'hungry', + }, + { + id: 'foo-namespace:dog:hungry', + type: LEGACY_URL_ALIAS_TYPE, + attributes: { + targetNamespace: 'foo-namespace', + targetType: 'dog', + targetId: 'uuidv5', + }, + migrationVersion: {}, + coreMigrationVersion: kibanaVersion, + }, + ]); + }); + }); + + describe('correctly applies reference, conversion, and migration transforms', () => { + const migrator = new DocumentMigrator({ + ...testOpts(), + typeRegistry: createRegistry( + { + name: 'dog', + namespaceType: 'multiple', + migrations: { + '1.0.0': setAttr('migrationVersion.dog', '2.0.0'), + '2.0.0': (doc) => doc, // noop + }, + convertToMultiNamespaceTypeVersion: '1.0.0', + }, + { name: 'toy', namespaceType: 'multiple', convertToMultiNamespaceTypeVersion: '1.0.0' } + ), + }); + migrator.prepareMigrations(); + const obj = { + id: 'pretty', + type: 'dog', + attributes: { name: 'Sasha' }, + migrationVersion: {}, + references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], + }; + + it('in the default space', () => { + const actual = migrator.migrateAndConvert(obj); + expect(mockUuidv5).not.toHaveBeenCalled(); + expect(actual).toEqual([ + { + id: 'pretty', + type: 'dog', + attributes: { name: 'Sasha' }, + migrationVersion: { dog: '2.0.0' }, + references: [{ id: 'favorite', type: 'toy', name: 'BALL!' }], // no change + coreMigrationVersion: kibanaVersion, + namespaces: ['default'], + }, + ]); + }); + + it('in a non-default space', () => { + const actual = migrator.migrateAndConvert({ ...obj, namespace: 'foo-namespace' }); + expect(mockUuidv5).toHaveBeenCalledTimes(2); + expect(mockUuidv5).toHaveBeenNthCalledWith(1, 'foo-namespace:toy:favorite', 'DNSUUID'); + expect(mockUuidv5).toHaveBeenNthCalledWith(2, 'foo-namespace:dog:pretty', 'DNSUUID'); + expect(actual).toEqual([ + { + id: 'uuidv5', + type: 'dog', + attributes: { name: 'Sasha' }, + migrationVersion: { dog: '2.0.0' }, + references: [{ id: 'uuidv5', type: 'toy', name: 'BALL!' }], // changed + coreMigrationVersion: kibanaVersion, + namespaces: ['foo-namespace'], + originId: 'pretty', + }, + { + id: 'foo-namespace:dog:pretty', + type: LEGACY_URL_ALIAS_TYPE, + attributes: { + targetNamespace: 'foo-namespace', + targetType: 'dog', + targetId: 'uuidv5', + }, + migrationVersion: {}, + coreMigrationVersion: kibanaVersion, + }, + ]); + }); + }); }); }); }); diff --git a/src/core/server/saved_objects/migrations/core/document_migrator.ts b/src/core/server/saved_objects/migrations/core/document_migrator.ts index 04e9a4e165f9..e93586ec7ce4 100644 --- a/src/core/server/saved_objects/migrations/core/document_migrator.ts +++ b/src/core/server/saved_objects/migrations/core/document_migrator.ts @@ -50,50 +50,102 @@ */ import Boom from '@hapi/boom'; +import uuidv5 from 'uuid/v5'; import { set } from '@elastic/safer-lodash-set'; import _ from 'lodash'; import Semver from 'semver'; import { Logger } from '../../../logging'; import { SavedObjectUnsanitizedDoc } from '../../serialization'; -import { SavedObjectsMigrationVersion } from '../../types'; +import { + SavedObjectsMigrationVersion, + SavedObjectsNamespaceType, + SavedObjectsType, +} from '../../types'; import { MigrationLogger } from './migration_logger'; import { ISavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { SavedObjectMigrationFn, SavedObjectMigrationMap } from '../types'; +import { DEFAULT_NAMESPACE_STRING } from '../../service/lib/utils'; +import { LegacyUrlAlias, LEGACY_URL_ALIAS_TYPE } from '../../object_types'; -export type TransformFn = (doc: SavedObjectUnsanitizedDoc) => SavedObjectUnsanitizedDoc; +const DEFAULT_MINIMUM_CONVERT_VERSION = '8.0.0'; + +export type MigrateFn = (doc: SavedObjectUnsanitizedDoc) => SavedObjectUnsanitizedDoc; +export type MigrateAndConvertFn = (doc: SavedObjectUnsanitizedDoc) => SavedObjectUnsanitizedDoc[]; + +interface TransformResult { + /** + * This is the original document that has been transformed. + */ + transformedDoc: SavedObjectUnsanitizedDoc; + /** + * These are any new document(s) that have been created during the transformation process; these are not transformed, but they are marked + * as up-to-date. Only conversion transforms generate additional documents. + */ + additionalDocs: SavedObjectUnsanitizedDoc[]; +} + +type ApplyTransformsFn = ( + doc: SavedObjectUnsanitizedDoc, + options?: TransformOptions +) => TransformResult; + +interface TransformOptions { + convertNamespaceTypes?: boolean; +} interface DocumentMigratorOptions { kibanaVersion: string; typeRegistry: ISavedObjectTypeRegistry; + minimumConvertVersion?: string; log: Logger; } interface ActiveMigrations { [type: string]: { - latestVersion: string; - transforms: Array<{ - version: string; - transform: TransformFn; - }>; + /** Derived from `migrate` transforms and `convert` transforms */ + latestMigrationVersion?: string; + /** Derived from `reference` transforms */ + latestCoreMigrationVersion?: string; + transforms: Transform[]; }; } +interface Transform { + version: string; + transform: (doc: SavedObjectUnsanitizedDoc) => TransformResult; + /** + * There are two "migrationVersion" transform types: + * * `migrate` - These transforms are defined and added by consumers using the type registry; each is applied to a single object type + * based on an object's `migrationVersion[type]` field. These are applied during index migrations and document migrations. + * * `convert` - These transforms are defined by core and added by consumers using the type registry; each is applied to a single object + * type based on an object's `migrationVersion[type]` field. These are applied during index migrations, NOT document migrations. + * + * There is one "coreMigrationVersion" transform type: + * * `reference` - These transforms are defined by core and added by consumers using the type registry; they are applied to all object + * types based on their `coreMigrationVersion` field. These are applied during index migrations, NOT document migrations. + * + * If any additional transform types are added, the functions below should be updated to account for them. + */ + transformType: 'migrate' | 'convert' | 'reference'; +} + /** * Manages migration of individual documents. */ export interface VersionedTransformer { migrationVersion: SavedObjectsMigrationVersion; + migrate: MigrateFn; + migrateAndConvert: MigrateAndConvertFn; prepareMigrations: () => void; - migrate: TransformFn; } /** * A concrete implementation of the VersionedTransformer interface. */ export class DocumentMigrator implements VersionedTransformer { - private documentMigratorOptions: DocumentMigratorOptions; + private documentMigratorOptions: Omit; private migrations?: ActiveMigrations; - private transformDoc?: TransformFn; + private transformDoc?: ApplyTransformsFn; /** * Creates an instance of DocumentMigrator. @@ -101,11 +153,19 @@ export class DocumentMigrator implements VersionedTransformer { * @param {DocumentMigratorOptions} opts * @prop {string} kibanaVersion - The current version of Kibana * @prop {SavedObjectTypeRegistry} typeRegistry - The type registry to get type migrations from + * @prop {string} minimumConvertVersion - The minimum version of Kibana in which documents can be converted to multi-namespace types * @prop {Logger} log - The migration logger * @memberof DocumentMigrator */ - constructor({ typeRegistry, kibanaVersion, log }: DocumentMigratorOptions) { - validateMigrationDefinition(typeRegistry); + constructor({ + typeRegistry, + kibanaVersion: rawKibanaVersion, + minimumConvertVersion = DEFAULT_MINIMUM_CONVERT_VERSION, + log, + }: DocumentMigratorOptions) { + const kibanaVersion = rawKibanaVersion.split('-')[0]; // coerce a semver-like string (x.y.z-SNAPSHOT) or prerelease version (x.y.z-alpha) to a regular semver (x.y.z) + validateMigrationDefinition(typeRegistry, kibanaVersion, minimumConvertVersion); + this.documentMigratorOptions = { typeRegistry, kibanaVersion, log }; } @@ -120,7 +180,14 @@ export class DocumentMigrator implements VersionedTransformer { if (!this.migrations) { throw new Error('Migrations are not ready. Make sure prepareMigrations is called first.'); } - return _.mapValues(this.migrations, ({ latestVersion }) => latestVersion); + + return Object.entries(this.migrations).reduce((acc, [prop, { latestMigrationVersion }]) => { + // some migration objects won't have a latestMigrationVersion (they only contain reference transforms that are applied from other types) + if (latestMigrationVersion) { + return { ...acc, [prop]: latestMigrationVersion }; + } + return acc; + }, {}); } /** @@ -132,7 +199,7 @@ export class DocumentMigrator implements VersionedTransformer { public prepareMigrations = () => { const { typeRegistry, kibanaVersion, log } = this.documentMigratorOptions; - this.migrations = buildActiveMigrations(typeRegistry, log); + this.migrations = buildActiveMigrations(typeRegistry, kibanaVersion, log); this.transformDoc = buildDocumentTransform({ kibanaVersion, migrations: this.migrations, @@ -155,25 +222,56 @@ export class DocumentMigrator implements VersionedTransformer { // Ex: Importing sample data that is cached at import level, migrations would // execute on mutated data the second time. const clonedDoc = _.cloneDeep(doc); - return this.transformDoc(clonedDoc); + const { transformedDoc } = this.transformDoc(clonedDoc); + return transformedDoc; + }; + + /** + * Migrates a document to the latest version and applies type conversions if applicable. Also returns any additional document(s) that may + * have been created during the transformation process. + * + * @param {SavedObjectUnsanitizedDoc} doc + * @returns {SavedObjectUnsanitizedDoc} + * @memberof DocumentMigrator + */ + public migrateAndConvert = (doc: SavedObjectUnsanitizedDoc): SavedObjectUnsanitizedDoc[] => { + if (!this.migrations || !this.transformDoc) { + throw new Error('Migrations are not ready. Make sure prepareMigrations is called first.'); + } + + // Clone the document to prevent accidental mutations on the original data + // Ex: Importing sample data that is cached at import level, migrations would + // execute on mutated data the second time. + const clonedDoc = _.cloneDeep(doc); + const { transformedDoc, additionalDocs } = this.transformDoc(clonedDoc, { + convertNamespaceTypes: true, + }); + return [transformedDoc, ...additionalDocs]; }; } -function validateMigrationsMapObject(name: string, migrationsMap?: SavedObjectMigrationMap) { +function validateMigrationsMapObject( + name: string, + kibanaVersion: string, + migrationsMap?: SavedObjectMigrationMap +) { function assertObject(obj: any, prefix: string) { if (!obj || typeof obj !== 'object') { throw new Error(`${prefix} Got ${obj}.`); } } - function assertValidSemver(version: string, type: string) { if (!Semver.valid(version)) { throw new Error( `Invalid migration for type ${type}. Expected all properties to be semvers, but got ${version}.` ); } + if (Semver.gt(version, kibanaVersion)) { + throw new Error( + `Invalid migration for type ${type}. Property '${version}' cannot be greater than the current Kibana version '${kibanaVersion}'.` + ); + } } - function assertValidTransform(fn: any, version: string, type: string) { if (typeof fn !== 'function') { throw new Error(`Invalid migration ${type}.${version}: expected a function, but got ${fn}.`); @@ -194,23 +292,63 @@ function validateMigrationsMapObject(name: string, migrationsMap?: SavedObjectMi } /** - * Basic validation that the migraiton definition matches our expectations. We can't + * Basic validation that the migration definition matches our expectations. We can't * rely on TypeScript here, as the caller may be JavaScript / ClojureScript / any compile-to-js * language. So, this is just to provide a little developer-friendly error messaging. Joi was * giving weird errors, so we're just doing manual validation. */ -function validateMigrationDefinition(registry: ISavedObjectTypeRegistry) { +function validateMigrationDefinition( + registry: ISavedObjectTypeRegistry, + kibanaVersion: string, + minimumConvertVersion: string +) { function assertObjectOrFunction(entity: any, prefix: string) { if (!entity || (typeof entity !== 'function' && typeof entity !== 'object')) { throw new Error(`${prefix} Got! ${typeof entity}, ${JSON.stringify(entity)}.`); } } + function assertValidConvertToMultiNamespaceType( + namespaceType: SavedObjectsNamespaceType, + convertToMultiNamespaceTypeVersion: string, + type: string + ) { + if (namespaceType !== 'multiple') { + throw new Error( + `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Expected namespaceType to be 'multiple', but got '${namespaceType}'.` + ); + } else if (!Semver.valid(convertToMultiNamespaceTypeVersion)) { + throw new Error( + `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Expected value to be a semver, but got '${convertToMultiNamespaceTypeVersion}'.` + ); + } else if (Semver.lt(convertToMultiNamespaceTypeVersion, minimumConvertVersion)) { + throw new Error( + `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Value '${convertToMultiNamespaceTypeVersion}' cannot be less than '${minimumConvertVersion}'.` + ); + } else if (Semver.gt(convertToMultiNamespaceTypeVersion, kibanaVersion)) { + throw new Error( + `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Value '${convertToMultiNamespaceTypeVersion}' cannot be greater than the current Kibana version '${kibanaVersion}'.` + ); + } else if (Semver.patch(convertToMultiNamespaceTypeVersion)) { + throw new Error( + `Invalid convertToMultiNamespaceTypeVersion for type ${type}. Value '${convertToMultiNamespaceTypeVersion}' cannot be used on a patch version (must be like 'x.y.0').` + ); + } + } + registry.getAllTypes().forEach((type) => { - if (type.migrations) { + const { name, migrations, convertToMultiNamespaceTypeVersion, namespaceType } = type; + if (migrations) { assertObjectOrFunction( type.migrations, - `Migration for type ${type.name} should be an object or a function returning an object like { '2.0.0': (doc) => doc }.` + `Migration for type ${name} should be an object or a function returning an object like { '2.0.0': (doc) => doc }.` + ); + } + if (convertToMultiNamespaceTypeVersion) { + assertValidConvertToMultiNamespaceType( + namespaceType, + convertToMultiNamespaceTypeVersion, + name ); } }); @@ -220,74 +358,144 @@ function validateMigrationDefinition(registry: ISavedObjectTypeRegistry) { * Converts migrations from a format that is convenient for callers to a format that * is convenient for our internal usage: * From: { type: { version: fn } } - * To: { type: { latestVersion: string, transforms: [{ version: string, transform: fn }] } } + * To: { type: { latestMigrationVersion?: string; latestCoreMigrationVersion?: string; transforms: [{ version: string, transform: fn }] } } */ function buildActiveMigrations( typeRegistry: ISavedObjectTypeRegistry, + kibanaVersion: string, log: Logger ): ActiveMigrations { - const typesWithMigrationMaps = typeRegistry - .getAllTypes() - .map((type) => ({ - ...type, - migrationsMap: typeof type.migrations === 'function' ? type.migrations() : type.migrations, - })) - .filter((type) => typeof type.migrationsMap !== 'undefined'); - - typesWithMigrationMaps.forEach((type) => - validateMigrationsMapObject(type.name, type.migrationsMap) - ); + const referenceTransforms = getReferenceTransforms(typeRegistry); + + return typeRegistry.getAllTypes().reduce((migrations, type) => { + const migrationsMap = + typeof type.migrations === 'function' ? type.migrations() : type.migrations; + validateMigrationsMapObject(type.name, kibanaVersion, migrationsMap); + + const migrationTransforms = Object.entries(migrationsMap ?? {}).map( + ([version, transform]) => ({ + version, + transform: wrapWithTry(version, type.name, transform, log), + transformType: 'migrate', + }) + ); + const conversionTransforms = getConversionTransforms(type); + const transforms = [ + ...referenceTransforms, + ...conversionTransforms, + ...migrationTransforms, + ].sort(transformComparator); + + if (!transforms.length) { + return migrations; + } - return typesWithMigrationMaps - .filter((type) => type.migrationsMap && Object.keys(type.migrationsMap).length > 0) - .reduce((migrations, type) => { - const transforms = Object.entries(type.migrationsMap!) - .map(([version, transform]) => ({ - version, - transform: wrapWithTry(version, type.name, transform, log), - })) - .sort((a, b) => Semver.compare(a.version, b.version)); - return { - ...migrations, - [type.name]: { - latestVersion: _.last(transforms)!.version, - transforms, - }, - }; - }, {} as ActiveMigrations); + const migrationVersionTransforms: Transform[] = []; + const coreMigrationVersionTransforms: Transform[] = []; + transforms.forEach((x) => { + if (x.transformType === 'migrate' || x.transformType === 'convert') { + migrationVersionTransforms.push(x); + } else { + coreMigrationVersionTransforms.push(x); + } + }); + + return { + ...migrations, + [type.name]: { + latestMigrationVersion: _.last(migrationVersionTransforms)?.version, + latestCoreMigrationVersion: _.last(coreMigrationVersionTransforms)?.version, + transforms, + }, + }; + }, {} as ActiveMigrations); } + /** * Creates a function which migrates and validates any document that is passed to it. */ function buildDocumentTransform({ + kibanaVersion, migrations, }: { kibanaVersion: string; migrations: ActiveMigrations; -}): TransformFn { - return function transformAndValidate(doc: SavedObjectUnsanitizedDoc) { - const result = doc.migrationVersion - ? applyMigrations(doc, migrations) - : markAsUpToDate(doc, migrations); +}): ApplyTransformsFn { + return function transformAndValidate( + doc: SavedObjectUnsanitizedDoc, + options: TransformOptions = {} + ) { + validateCoreMigrationVersion(doc, kibanaVersion); + + const { convertNamespaceTypes = false } = options; + let transformedDoc: SavedObjectUnsanitizedDoc; + let additionalDocs: SavedObjectUnsanitizedDoc[] = []; + if (doc.migrationVersion) { + const result = applyMigrations(doc, migrations, kibanaVersion, convertNamespaceTypes); + transformedDoc = result.transformedDoc; + additionalDocs = additionalDocs.concat( + result.additionalDocs.map((x) => markAsUpToDate(x, migrations, kibanaVersion)) + ); + } else { + transformedDoc = markAsUpToDate(doc, migrations, kibanaVersion); + } // In order to keep tests a bit more stable, we won't // tack on an empy migrationVersion to docs that have // no migrations defined. - if (_.isEmpty(result.migrationVersion)) { - delete result.migrationVersion; + if (_.isEmpty(transformedDoc.migrationVersion)) { + delete transformedDoc.migrationVersion; } - return result; + return { transformedDoc, additionalDocs }; }; } -function applyMigrations(doc: SavedObjectUnsanitizedDoc, migrations: ActiveMigrations) { +function validateCoreMigrationVersion(doc: SavedObjectUnsanitizedDoc, kibanaVersion: string) { + const { id, coreMigrationVersion: docVersion } = doc; + if (!docVersion) { + return; + } + + // We verify that the object's coreMigrationVersion is valid, and that it is not greater than the version supported by Kibana. + // If we have a coreMigrationVersion and the kibanaVersion is smaller than it or does not exist, we are dealing with a document that + // belongs to a future Kibana / plugin version. + if (!Semver.valid(docVersion)) { + throw Boom.badData( + `Document "${id}" has an invalid "coreMigrationVersion" [${docVersion}]. This must be a semver value.`, + doc + ); + } + + if (doc.coreMigrationVersion && Semver.gt(docVersion, kibanaVersion)) { + throw Boom.badData( + `Document "${id}" has a "coreMigrationVersion" which belongs to a more recent version` + + ` of Kibana [${docVersion}]. The current version is [${kibanaVersion}].`, + doc + ); + } +} + +function applyMigrations( + doc: SavedObjectUnsanitizedDoc, + migrations: ActiveMigrations, + kibanaVersion: string, + convertNamespaceTypes: boolean +) { + let additionalDocs: SavedObjectUnsanitizedDoc[] = []; while (true) { const prop = nextUnmigratedProp(doc, migrations); if (!prop) { - return doc; + // regardless of whether or not any reference transform was applied, update the coreMigrationVersion + // this is needed to ensure that newly created documents have an up-to-date coreMigrationVersion field + return { + transformedDoc: { ...doc, coreMigrationVersion: kibanaVersion }, + additionalDocs, + }; } - doc = migrateProp(doc, prop, migrations); + const result = migrateProp(doc, prop, migrations, convertNamespaceTypes); + doc = result.transformedDoc; + additionalDocs = [...additionalDocs, ...result.additionalDocs]; } } @@ -303,7 +511,7 @@ function props(doc: SavedObjectUnsanitizedDoc) { */ function propVersion(doc: SavedObjectUnsanitizedDoc | ActiveMigrations, prop: string) { return ( - ((doc as any)[prop] && (doc as any)[prop].latestVersion) || + ((doc as any)[prop] && (doc as any)[prop].latestMigrationVersion) || (doc.migrationVersion && (doc as any).migrationVersion[prop]) ); } @@ -311,16 +519,137 @@ function propVersion(doc: SavedObjectUnsanitizedDoc | ActiveMigrations, prop: st /** * Sets the doc's migrationVersion to be the most recent version */ -function markAsUpToDate(doc: SavedObjectUnsanitizedDoc, migrations: ActiveMigrations) { +function markAsUpToDate( + doc: SavedObjectUnsanitizedDoc, + migrations: ActiveMigrations, + kibanaVersion: string +) { return { ...doc, migrationVersion: props(doc).reduce((acc, prop) => { const version = propVersion(migrations, prop); return version ? set(acc, prop, version) : acc; }, {}), + coreMigrationVersion: kibanaVersion, }; } +/** + * Converts a single-namespace object to a multi-namespace object. This primarily entails removing the `namespace` field and adding the + * `namespaces` field. + * + * If the object does not exist in the default namespace (undefined), its ID is also regenerated, and an "originId" is added to preserve + * legacy import/copy behavior. + */ +function convertNamespaceType(doc: SavedObjectUnsanitizedDoc) { + const { namespace, ...otherAttrs } = doc; + const additionalDocs: SavedObjectUnsanitizedDoc[] = []; + + // If this object exists in the default namespace, return it with the appropriate `namespaces` field without changing its ID. + if (namespace === undefined) { + return { + transformedDoc: { ...otherAttrs, namespaces: [DEFAULT_NAMESPACE_STRING] }, + additionalDocs, + }; + } + + const { id: originId, type } = otherAttrs; + const id = deterministicallyRegenerateObjectId(namespace, type, originId!); + if (namespace !== undefined) { + const legacyUrlAlias: SavedObjectUnsanitizedDoc = { + id: `${namespace}:${type}:${originId}`, + type: LEGACY_URL_ALIAS_TYPE, + attributes: { + targetNamespace: namespace, + targetType: type, + targetId: id, + }, + }; + additionalDocs.push(legacyUrlAlias); + } + return { + transformedDoc: { ...otherAttrs, id, originId, namespaces: [namespace] }, + additionalDocs, + }; +} + +/** + * Returns all applicable conversion transforms for a given object type. + */ +function getConversionTransforms(type: SavedObjectsType): Transform[] { + const { convertToMultiNamespaceTypeVersion } = type; + if (!convertToMultiNamespaceTypeVersion) { + return []; + } + return [ + { + version: convertToMultiNamespaceTypeVersion, + transform: convertNamespaceType, + transformType: 'convert', + }, + ]; +} + +/** + * Returns all applicable reference transforms for all object types. + */ +function getReferenceTransforms(typeRegistry: ISavedObjectTypeRegistry): Transform[] { + const transformMap = typeRegistry + .getAllTypes() + .filter((type) => type.convertToMultiNamespaceTypeVersion) + .reduce((acc, { convertToMultiNamespaceTypeVersion: version, name }) => { + const types = acc.get(version!) ?? new Set(); + return acc.set(version!, types.add(name)); + }, new Map>()); + + return Array.from(transformMap, ([version, types]) => ({ + version, + transform: (doc) => { + const { namespace, references } = doc; + if (namespace && references?.length) { + return { + transformedDoc: { + ...doc, + references: references.map(({ type, id, ...attrs }) => ({ + ...attrs, + type, + id: types.has(type) ? deterministicallyRegenerateObjectId(namespace, type, id) : id, + })), + }, + additionalDocs: [], + }; + } + return { transformedDoc: doc, additionalDocs: [] }; + }, + transformType: 'reference', + })); +} + +/** + * Transforms are sorted in ascending order by version. One version may contain multiple transforms; 'reference' transforms always run + * first, 'convert' transforms always run second, and 'migrate' transforms always run last. This is because: + * 1. 'convert' transforms get rid of the `namespace` field, which must be present for 'reference' transforms to function correctly. + * 2. 'migrate' transforms are defined by the consumer, and may change the object type or migrationVersion which resets the migration loop + * and could cause any remaining transforms for this version to be skipped. + */ +function transformComparator(a: Transform, b: Transform) { + const semver = Semver.compare(a.version, b.version); + if (semver !== 0) { + return semver; + } else if (a.transformType !== b.transformType) { + if (a.transformType === 'migrate') { + return 1; + } else if (b.transformType === 'migrate') { + return -1; + } else if (a.transformType === 'convert') { + return 1; + } else if (b.transformType === 'convert') { + return -1; + } + } + return 0; +} + /** * If a specific transform function fails, this tacks on a bit of information * about the document and transform that caused the failure. @@ -342,7 +671,7 @@ function wrapWithTry( throw new Error(`Invalid saved object returned from migration ${type}:${version}.`); } - return result; + return { transformedDoc: result, additionalDocs: [] }; } catch (error) { const failedTransform = `${type}:${version}`; const failedDoc = JSON.stringify(doc); @@ -354,32 +683,52 @@ function wrapWithTry( }; } +/** + * Determines whether or not a document has any pending transforms that should be applied based on its coreMigrationVersion field. + * Currently, only reference transforms qualify. + */ +function getHasPendingCoreMigrationVersionTransform( + doc: SavedObjectUnsanitizedDoc, + migrations: ActiveMigrations, + prop: string +) { + if (!migrations.hasOwnProperty(prop)) { + return false; + } + + const { latestCoreMigrationVersion } = migrations[prop]; + const { coreMigrationVersion } = doc; + return ( + latestCoreMigrationVersion && + (!coreMigrationVersion || Semver.gt(latestCoreMigrationVersion, coreMigrationVersion)) + ); +} + /** * Finds the first unmigrated property in the specified document. */ function nextUnmigratedProp(doc: SavedObjectUnsanitizedDoc, migrations: ActiveMigrations) { return props(doc).find((p) => { - const latestVersion = propVersion(migrations, p); + const latestMigrationVersion = propVersion(migrations, p); const docVersion = propVersion(doc, p); - if (latestVersion === docVersion) { - return false; - } - // We verify that the version is not greater than the version supported by Kibana. // If we didn't, this would cause an infinite loop, as we'd be unable to migrate the property // but it would continue to show up as unmigrated. - // If we have a docVersion and the latestVersion is smaller than it or does not exist, + // If we have a docVersion and the latestMigrationVersion is smaller than it or does not exist, // we are dealing with a document that belongs to a future Kibana / plugin version. - if (docVersion && (!latestVersion || Semver.gt(docVersion, latestVersion))) { + if (docVersion && (!latestMigrationVersion || Semver.gt(docVersion, latestMigrationVersion))) { throw Boom.badData( `Document "${doc.id}" has property "${p}" which belongs to a more recent` + - ` version of Kibana [${docVersion}]. The last known version is [${latestVersion}]`, + ` version of Kibana [${docVersion}]. The last known version is [${latestMigrationVersion}]`, doc ); } - return true; + return ( + (latestMigrationVersion && latestMigrationVersion !== docVersion) || + getHasPendingCoreMigrationVersionTransform(doc, migrations, p) // If the object itself is up-to-date, check if its references are up-to-date too + ); }); } @@ -389,23 +738,42 @@ function nextUnmigratedProp(doc: SavedObjectUnsanitizedDoc, migrations: ActiveMi function migrateProp( doc: SavedObjectUnsanitizedDoc, prop: string, - migrations: ActiveMigrations -): SavedObjectUnsanitizedDoc { + migrations: ActiveMigrations, + convertNamespaceTypes: boolean +): TransformResult { const originalType = doc.type; let migrationVersion = _.clone(doc.migrationVersion) || {}; - const typeChanged = () => !doc.hasOwnProperty(prop) || doc.type !== originalType; + let additionalDocs: SavedObjectUnsanitizedDoc[] = []; - for (const { version, transform } of applicableTransforms(migrations, doc, prop)) { - doc = transform(doc); - migrationVersion = updateMigrationVersion(doc, migrationVersion, prop, version); - doc.migrationVersion = _.clone(migrationVersion); + for (const { version, transform, transformType } of applicableTransforms(migrations, doc, prop)) { + const currentVersion = propVersion(doc, prop); + if (currentVersion && Semver.gt(currentVersion, version)) { + // the previous transform function increased the object's migrationVersion; break out of the loop + break; + } - if (typeChanged()) { + if (convertNamespaceTypes || (transformType !== 'convert' && transformType !== 'reference')) { + // migrate transforms are always applied, but conversion transforms and reference transforms are only applied during index migrations + const result = transform(doc); + doc = result.transformedDoc; + additionalDocs = [...additionalDocs, ...result.additionalDocs]; + } + if (transformType === 'reference') { + // regardless of whether or not the reference transform was applied, update the object's coreMigrationVersion + // this is needed to ensure that we don't have an endless migration loop + doc.coreMigrationVersion = version; + } else { + migrationVersion = updateMigrationVersion(doc, migrationVersion, prop, version); + doc.migrationVersion = _.clone(migrationVersion); + } + + if (doc.type !== originalType) { + // the transform function changed the object's type; break out of the loop break; } } - return doc; + return { transformedDoc: doc, additionalDocs }; } /** @@ -417,9 +785,14 @@ function applicableTransforms( prop: string ) { const minVersion = propVersion(doc, prop); + const minReferenceVersion = doc.coreMigrationVersion || '0.0.0'; const { transforms } = migrations[prop]; return minVersion - ? transforms.filter(({ version }) => Semver.gt(version, minVersion)) + ? transforms.filter(({ version, transformType }) => + transformType === 'reference' + ? Semver.gt(version, minReferenceVersion) + : Semver.gt(version, minVersion) + ) : transforms; } @@ -466,3 +839,14 @@ function assertNoDowngrades( ); } } + +/** + * Deterministically regenerates a saved object's ID based upon it's current namespace, type, and ID. This ensures that we can regenerate + * any existing object IDs without worrying about collisions if two objects that exist in different namespaces share an ID. It also ensures + * that we can later regenerate any inbound object references to match. + * + * @note This is only intended to be used when single-namespace object types are converted into multi-namespace object types. + */ +function deterministicallyRegenerateObjectId(namespace: string, type: string, id: string) { + return uuidv5(`${namespace}:${type}:${id}`, uuidv5.DNS); // the uuidv5 namespace constant (uuidv5.DNS) is arbitrary +} diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.test.ts b/src/core/server/saved_objects/migrations/core/elastic_index.test.ts index 025730e71b92..32ecea94826f 100644 --- a/src/core/server/saved_objects/migrations/core/elastic_index.test.ts +++ b/src/core/server/saved_objects/migrations/core/elastic_index.test.ts @@ -557,6 +557,7 @@ describe('ElasticIndex', () => { mappings, count, migrations, + kibanaVersion, }: any) { client.indices.get = jest.fn().mockReturnValueOnce( elasticsearchClientMock.createSuccessTransportRequestPromise({ @@ -570,7 +571,12 @@ describe('ElasticIndex', () => { }) ); - const hasMigrations = await Index.migrationsUpToDate(client, index, migrations); + const hasMigrations = await Index.migrationsUpToDate( + client, + index, + migrations, + kibanaVersion + ); return { hasMigrations }; } @@ -584,6 +590,7 @@ describe('ElasticIndex', () => { }, count: 0, migrations: { dashy: '2.3.4' }, + kibanaVersion: '7.10.0', }); expect(hasMigrations).toBeFalsy(); @@ -611,6 +618,7 @@ describe('ElasticIndex', () => { }, count: 2, migrations: {}, + kibanaVersion: '7.10.0', }); expect(hasMigrations).toBeTruthy(); @@ -652,6 +660,7 @@ describe('ElasticIndex', () => { }, count: 3, migrations: { dashy: '23.2.5' }, + kibanaVersion: '7.10.0', }); expect(hasMigrations).toBeFalsy(); @@ -677,6 +686,7 @@ describe('ElasticIndex', () => { bashy: '99.9.3', flashy: '3.4.5', }, + kibanaVersion: '7.10.0', }); function shouldClause(type: string, version: string) { @@ -702,6 +712,15 @@ describe('ElasticIndex', () => { shouldClause('dashy', '23.2.5'), shouldClause('bashy', '99.9.3'), shouldClause('flashy', '3.4.5'), + { + bool: { + must_not: { + term: { + coreMigrationVersion: '7.10.0', + }, + }, + }, + }, ], }, }, diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.ts b/src/core/server/saved_objects/migrations/core/elastic_index.ts index c6c00a123295..9cdec926a56b 100644 --- a/src/core/server/saved_objects/migrations/core/elastic_index.ts +++ b/src/core/server/saved_objects/migrations/core/elastic_index.ts @@ -147,6 +147,7 @@ export async function migrationsUpToDate( client: MigrationEsClient, index: string, migrationVersion: SavedObjectsMigrationVersion, + kibanaVersion: string, retryCount: number = 10 ): Promise { try { @@ -165,18 +166,29 @@ export async function migrationsUpToDate( body: { query: { bool: { - should: Object.entries(migrationVersion).map(([type, latestVersion]) => ({ - bool: { - must: [ - { exists: { field: type } }, - { - bool: { - must_not: { term: { [`migrationVersion.${type}`]: latestVersion } }, + should: [ + ...Object.entries(migrationVersion).map(([type, latestVersion]) => ({ + bool: { + must: [ + { exists: { field: type } }, + { + bool: { + must_not: { term: { [`migrationVersion.${type}`]: latestVersion } }, + }, + }, + ], + }, + })), + { + bool: { + must_not: { + term: { + coreMigrationVersion: kibanaVersion, }, }, - ], + }, }, - })), + ], }, }, }, @@ -194,7 +206,7 @@ export async function migrationsUpToDate( await new Promise((r) => setTimeout(r, 1000)); - return await migrationsUpToDate(client, index, migrationVersion, retryCount - 1); + return await migrationsUpToDate(client, index, migrationVersion, kibanaVersion, retryCount - 1); } } diff --git a/src/core/server/saved_objects/migrations/core/index_migrator.test.ts b/src/core/server/saved_objects/migrations/core/index_migrator.test.ts index e82e30ef3003..a8abc75114a9 100644 --- a/src/core/server/saved_objects/migrations/core/index_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/core/index_migrator.test.ts @@ -24,6 +24,7 @@ describe('IndexMigrator', () => { batchSize: 10, client: elasticsearchClientMock.createElasticsearchClient(), index: '.kibana', + kibanaVersion: '7.10.0', log: loggingSystemMock.create().get(), mappingProperties: {}, pollInterval: 1, @@ -31,6 +32,7 @@ describe('IndexMigrator', () => { documentMigrator: { migrationVersion: {}, migrate: _.identity, + migrateAndConvert: _.identity, prepareMigrations: jest.fn(), }, serializer: new SavedObjectsSerializer(new SavedObjectTypeRegistry()), @@ -58,6 +60,7 @@ describe('IndexMigrator', () => { namespaces: '2f4316de49999235636386fe51dc06c1', originId: '2f4316de49999235636386fe51dc06c1', references: '7997cf5a56cc02bdc9c93361bde732b0', + coreMigrationVersion: '2f4316de49999235636386fe51dc06c1', type: '2f4316de49999235636386fe51dc06c1', updated_at: '00da57df13e94e9d98437d13ace4bfe0', }, @@ -78,6 +81,7 @@ describe('IndexMigrator', () => { id: { type: 'keyword' }, }, }, + coreMigrationVersion: { type: 'keyword' }, }, }, settings: { number_of_shards: 1, auto_expand_replicas: '0-1' }, @@ -179,6 +183,7 @@ describe('IndexMigrator', () => { namespaces: '2f4316de49999235636386fe51dc06c1', originId: '2f4316de49999235636386fe51dc06c1', references: '7997cf5a56cc02bdc9c93361bde732b0', + coreMigrationVersion: '2f4316de49999235636386fe51dc06c1', type: '2f4316de49999235636386fe51dc06c1', updated_at: '00da57df13e94e9d98437d13ace4bfe0', }, @@ -200,6 +205,7 @@ describe('IndexMigrator', () => { id: { type: 'keyword' }, }, }, + coreMigrationVersion: { type: 'keyword' }, }, }, settings: { number_of_shards: 1, auto_expand_replicas: '0-1' }, @@ -240,6 +246,7 @@ describe('IndexMigrator', () => { namespaces: '2f4316de49999235636386fe51dc06c1', originId: '2f4316de49999235636386fe51dc06c1', references: '7997cf5a56cc02bdc9c93361bde732b0', + coreMigrationVersion: '2f4316de49999235636386fe51dc06c1', type: '2f4316de49999235636386fe51dc06c1', updated_at: '00da57df13e94e9d98437d13ace4bfe0', }, @@ -261,6 +268,7 @@ describe('IndexMigrator', () => { id: { type: 'keyword' }, }, }, + coreMigrationVersion: { type: 'keyword' }, }, }, settings: { number_of_shards: 1, auto_expand_replicas: '0-1' }, @@ -307,17 +315,15 @@ describe('IndexMigrator', () => { test('transforms all docs from the original index', async () => { let count = 0; const { client } = testOpts; - const migrateDoc = jest.fn((doc: SavedObjectUnsanitizedDoc) => { - return { - ...doc, - attributes: { name: ++count }, - }; + const migrateAndConvertDoc = jest.fn((doc: SavedObjectUnsanitizedDoc) => { + return [{ ...doc, attributes: { name: ++count } }]; }); testOpts.documentMigrator = { migrationVersion: { foo: '1.2.3' }, + migrate: jest.fn(), + migrateAndConvert: migrateAndConvertDoc, prepareMigrations: jest.fn(), - migrate: migrateDoc, }; withIndex(client, { @@ -331,14 +337,14 @@ describe('IndexMigrator', () => { await new IndexMigrator(testOpts).migrate(); expect(count).toEqual(2); - expect(migrateDoc).toHaveBeenCalledWith({ + expect(migrateAndConvertDoc).toHaveBeenNthCalledWith(1, { id: '1', type: 'foo', attributes: { name: 'Bar' }, migrationVersion: {}, references: [], }); - expect(migrateDoc).toHaveBeenCalledWith({ + expect(migrateAndConvertDoc).toHaveBeenNthCalledWith(2, { id: '2', type: 'foo', attributes: { name: 'Baz' }, @@ -363,14 +369,15 @@ describe('IndexMigrator', () => { test('rejects when the migration function throws an error', async () => { const { client } = testOpts; - const migrateDoc = jest.fn((doc: SavedObjectUnsanitizedDoc) => { + const migrateAndConvertDoc = jest.fn((doc: SavedObjectUnsanitizedDoc) => { throw new Error('error migrating document'); }); testOpts.documentMigrator = { migrationVersion: { foo: '1.2.3' }, + migrate: jest.fn(), + migrateAndConvert: migrateAndConvertDoc, prepareMigrations: jest.fn(), - migrate: migrateDoc, }; withIndex(client, { diff --git a/src/core/server/saved_objects/migrations/core/index_migrator.ts b/src/core/server/saved_objects/migrations/core/index_migrator.ts index bb341e6173ae..869729daab4f 100644 --- a/src/core/server/saved_objects/migrations/core/index_migrator.ts +++ b/src/core/server/saved_objects/migrations/core/index_migrator.ts @@ -60,13 +60,14 @@ export class IndexMigrator { * Determines what action the migration system needs to take (none, patch, migrate). */ async function requiresMigration(context: Context): Promise { - const { client, alias, documentMigrator, dest, log } = context; + const { client, alias, documentMigrator, dest, kibanaVersion, log } = context; // Have all of our known migrations been run against the index? const hasMigrations = await Index.migrationsUpToDate( client, alias, - documentMigrator.migrationVersion + documentMigrator.migrationVersion, + kibanaVersion ); if (!hasMigrations) { @@ -184,7 +185,7 @@ async function migrateSourceToDest(context: Context) { await Index.write( client, dest.indexName, - await migrateRawDocs(serializer, documentMigrator.migrate, docs, log) + await migrateRawDocs(serializer, documentMigrator.migrateAndConvert, docs, log) ); } } diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts index 4e6c2d0ddfd5..f3e4b67876b7 100644 --- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts +++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.test.ts @@ -15,7 +15,9 @@ import { createSavedObjectsMigrationLoggerMock } from '../../migrations/mocks'; describe('migrateRawDocs', () => { test('converts raw docs to saved objects', async () => { - const transform = jest.fn((doc: any) => set(doc, 'attributes.name', 'HOI!')); + const transform = jest.fn((doc: any) => [ + set(_.cloneDeep(doc), 'attributes.name', 'HOI!'), + ]); const result = await migrateRawDocs( new SavedObjectsSerializer(new SavedObjectTypeRegistry()), transform, @@ -37,14 +39,30 @@ describe('migrateRawDocs', () => { }, ]); - expect(transform).toHaveBeenCalled(); + const obj1 = { + id: 'b', + type: 'a', + attributes: { name: 'AAA' }, + migrationVersion: {}, + references: [], + }; + const obj2 = { + id: 'd', + type: 'c', + attributes: { name: 'DDD' }, + migrationVersion: {}, + references: [], + }; + expect(transform).toHaveBeenCalledTimes(2); + expect(transform).toHaveBeenNthCalledWith(1, obj1); + expect(transform).toHaveBeenNthCalledWith(2, obj2); }); test('passes invalid docs through untouched and logs error', async () => { const logger = createSavedObjectsMigrationLoggerMock(); - const transform = jest.fn((doc: any) => - set(_.cloneDeep(doc), 'attributes.name', 'TADA') - ); + const transform = jest.fn((doc: any) => [ + set(_.cloneDeep(doc), 'attributes.name', 'TADA'), + ]); const result = await migrateRawDocs( new SavedObjectsSerializer(new SavedObjectTypeRegistry()), transform, @@ -63,23 +81,53 @@ describe('migrateRawDocs', () => { }, ]); - expect(transform.mock.calls).toEqual([ - [ - { - id: 'd', - type: 'c', - attributes: { - name: 'DDD', - }, - migrationVersion: {}, - references: [], - }, - ], - ]); + const obj2 = { + id: 'd', + type: 'c', + attributes: { name: 'DDD' }, + migrationVersion: {}, + references: [], + }; + expect(transform).toHaveBeenCalledTimes(1); + expect(transform).toHaveBeenCalledWith(obj2); expect(logger.error).toBeCalledTimes(1); }); + test('handles when one document is transformed into multiple documents', async () => { + const transform = jest.fn((doc: any) => [ + set(_.cloneDeep(doc), 'attributes.name', 'HOI!'), + { id: 'bar', type: 'foo', attributes: { name: 'baz' } }, + ]); + const result = await migrateRawDocs( + new SavedObjectsSerializer(new SavedObjectTypeRegistry()), + transform, + [{ _id: 'a:b', _source: { type: 'a', a: { name: 'AAA' } } }], + createSavedObjectsMigrationLoggerMock() + ); + + expect(result).toEqual([ + { + _id: 'a:b', + _source: { type: 'a', a: { name: 'HOI!' }, migrationVersion: {}, references: [] }, + }, + { + _id: 'foo:bar', + _source: { type: 'foo', foo: { name: 'baz' }, references: [] }, + }, + ]); + + const obj = { + id: 'b', + type: 'a', + attributes: { name: 'AAA' }, + migrationVersion: {}, + references: [], + }; + expect(transform).toHaveBeenCalledTimes(1); + expect(transform).toHaveBeenCalledWith(obj); + }); + test('rejects when the transform function throws an error', async () => { const transform = jest.fn((doc: any) => { throw new Error('error during transform'); diff --git a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts index 0f939cd08aff..fd1b7db36b4e 100644 --- a/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts +++ b/src/core/server/saved_objects/migrations/core/migrate_raw_docs.ts @@ -15,7 +15,7 @@ import { SavedObjectsSerializer, SavedObjectUnsanitizedDoc, } from '../../serialization'; -import { TransformFn } from './document_migrator'; +import { MigrateAndConvertFn } from './document_migrator'; import { SavedObjectsMigrationLogger } from '.'; /** @@ -28,21 +28,24 @@ import { SavedObjectsMigrationLogger } from '.'; */ export async function migrateRawDocs( serializer: SavedObjectsSerializer, - migrateDoc: TransformFn, + migrateDoc: MigrateAndConvertFn, rawDocs: SavedObjectsRawDoc[], log: SavedObjectsMigrationLogger ): Promise { const migrateDocWithoutBlocking = transformNonBlocking(migrateDoc); const processedDocs = []; for (const raw of rawDocs) { - if (serializer.isRawSavedObject(raw)) { - const savedObject = serializer.rawToSavedObject(raw); + const options = { namespaceTreatment: 'lax' as 'lax' }; + if (serializer.isRawSavedObject(raw, options)) { + const savedObject = serializer.rawToSavedObject(raw, options); savedObject.migrationVersion = savedObject.migrationVersion || {}; processedDocs.push( - serializer.savedObjectToRaw({ - references: [], - ...(await migrateDocWithoutBlocking(savedObject)), - }) + ...(await migrateDocWithoutBlocking(savedObject)).map((attrs) => + serializer.savedObjectToRaw({ + references: [], + ...attrs, + }) + ) ); } else { log.error( @@ -63,8 +66,8 @@ export async function migrateRawDocs( * work in between each transform. */ function transformNonBlocking( - transform: TransformFn -): (doc: SavedObjectUnsanitizedDoc) => Promise { + transform: MigrateAndConvertFn +): (doc: SavedObjectUnsanitizedDoc) => Promise { // promises aren't enough to unblock the event loop return (doc: SavedObjectUnsanitizedDoc) => new Promise((resolve, reject) => { diff --git a/src/core/server/saved_objects/migrations/core/migration_context.ts b/src/core/server/saved_objects/migrations/core/migration_context.ts index a5aaff7df2dd..62e455c2ddb6 100644 --- a/src/core/server/saved_objects/migrations/core/migration_context.ts +++ b/src/core/server/saved_objects/migrations/core/migration_context.ts @@ -32,6 +32,7 @@ export interface MigrationOpts { scrollDuration: string; client: MigrationEsClient; index: string; + kibanaVersion: string; log: Logger; mappingProperties: SavedObjectsTypeMappingDefinitions; documentMigrator: VersionedTransformer; @@ -54,6 +55,7 @@ export interface Context { source: Index.FullIndexInfo; dest: Index.FullIndexInfo; documentMigrator: VersionedTransformer; + kibanaVersion: string; log: SavedObjectsMigrationLogger; batchSize: number; pollInterval: number; @@ -78,6 +80,7 @@ export async function migrationContext(opts: MigrationOpts): Promise { alias, source, dest, + kibanaVersion: opts.kibanaVersion, log: new MigrationLogger(log), batchSize: opts.batchSize, documentMigrator: opts.documentMigrator, diff --git a/src/core/server/saved_objects/migrations/kibana/__snapshots__/kibana_migrator.test.ts.snap b/src/core/server/saved_objects/migrations/kibana/__snapshots__/kibana_migrator.test.ts.snap index 9311292a6a0e..32c2536ab029 100644 --- a/src/core/server/saved_objects/migrations/kibana/__snapshots__/kibana_migrator.test.ts.snap +++ b/src/core/server/saved_objects/migrations/kibana/__snapshots__/kibana_migrator.test.ts.snap @@ -6,6 +6,7 @@ Object { "migrationMappingPropertyHashes": Object { "amap": "510f1f0adb69830cf8a1c5ce2923ed82", "bmap": "510f1f0adb69830cf8a1c5ce2923ed82", + "coreMigrationVersion": "2f4316de49999235636386fe51dc06c1", "migrationVersion": "4a1746014a75ade3a714e1db5763276f", "namespace": "2f4316de49999235636386fe51dc06c1", "namespaces": "2f4316de49999235636386fe51dc06c1", @@ -31,6 +32,9 @@ Object { }, }, }, + "coreMigrationVersion": Object { + "type": "keyword", + }, "migrationVersion": Object { "dynamic": "true", "type": "object", diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts index aea29479d2af..c8bc4b2e1412 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts @@ -90,6 +90,7 @@ export class KibanaMigrator { }: KibanaMigratorOptions) { this.client = client; this.kibanaConfig = kibanaConfig; + this.kibanaVersion = kibanaVersion; this.savedObjectsConfig = savedObjectsConfig; this.typeRegistry = typeRegistry; this.serializer = new SavedObjectsSerializer(this.typeRegistry); @@ -177,7 +178,7 @@ export class KibanaMigrator { transformRawDocs: (rawDocs: SavedObjectsRawDoc[]) => migrateRawDocs( this.serializer, - this.documentMigrator.migrate, + this.documentMigrator.migrateAndConvert, rawDocs, new MigrationLogger(this.log) ), @@ -192,6 +193,7 @@ export class KibanaMigrator { client: createMigrationEsClient(this.client, this.log, this.migrationsRetryDelay), documentMigrator: this.documentMigrator, index, + kibanaVersion: this.kibanaVersion, log: this.log, mappingProperties: indexMap[index].typeMappings, pollInterval: this.savedObjectsConfig.pollInterval, diff --git a/src/core/server/saved_objects/migrations/types.ts b/src/core/server/saved_objects/migrations/types.ts index b54d0222a147..52b4f50d599d 100644 --- a/src/core/server/saved_objects/migrations/types.ts +++ b/src/core/server/saved_objects/migrations/types.ts @@ -61,7 +61,7 @@ export interface SavedObjectMigrationContext { /** * A map of {@link SavedObjectMigrationFn | migration functions} to be used for a given type. - * The map's keys must be valid semver versions. + * The map's keys must be valid semver versions, and they cannot exceed the current Kibana version. * * For a given document, only migrations with a higher version number than that of the document will be applied. * Migrations are executed in order, starting from the lowest version and ending with the highest one. diff --git a/src/core/server/saved_objects/migrationsv2/model.test.ts b/src/core/server/saved_objects/migrationsv2/model.test.ts index 43e32fd61107..d5ab85c54a72 100644 --- a/src/core/server/saved_objects/migrationsv2/model.test.ts +++ b/src/core/server/saved_objects/migrationsv2/model.test.ts @@ -911,7 +911,7 @@ describe('migrations v2 model', () => { expect(newState.retryCount).toEqual(0); expect(newState.retryDelay).toEqual(0); }); - test('MARK_VERSION_INDEX_READY -> MARK_VERSION_INDEX_CONFLICT if someone else removed the current alias from the source index', () => { + test('MARK_VERSION_INDEX_READY -> MARK_VERSION_INDEX_CONFLICT if another removed the current alias from the source index', () => { const res: ResponseType<'MARK_VERSION_INDEX_READY'> = Either.left({ type: 'alias_not_found_exception', }); @@ -920,6 +920,16 @@ describe('migrations v2 model', () => { expect(newState.retryCount).toEqual(0); expect(newState.retryDelay).toEqual(0); }); + test('MARK_VERSION_INDEX_READY -> MARK_VERSION_INDEX_CONFLICT if another node removed the temporary index', () => { + const res: ResponseType<'MARK_VERSION_INDEX_READY'> = Either.left({ + type: 'index_not_found_exception', + index: '.kibana_7.11.0_reindex_temp', + }); + const newState = model(markVersionIndexReadyState, res); + expect(newState.controlState).toEqual('MARK_VERSION_INDEX_READY_CONFLICT'); + expect(newState.retryCount).toEqual(0); + expect(newState.retryDelay).toEqual(0); + }); }); describe('MARK_VERSION_INDEX_READY_CONFLICT', () => { const aliasActions = Option.some([Symbol('alias action')] as unknown) as Option.Some< diff --git a/src/core/server/saved_objects/migrationsv2/model.ts b/src/core/server/saved_objects/migrationsv2/model.ts index ba2508bf73cc..1119edde8e26 100644 --- a/src/core/server/saved_objects/migrationsv2/model.ts +++ b/src/core/server/saved_objects/migrationsv2/model.ts @@ -638,12 +638,20 @@ export const model = (currentState: State, resW: ResponseType): // alias_not_found_exception another instance has completed a // migration from the same source. return { ...stateP, controlState: 'MARK_VERSION_INDEX_READY_CONFLICT' }; - } else if ( - left.type === 'remove_index_not_a_concrete_index' || - left.type === 'index_not_found_exception' - ) { - // We don't handle these errors as the migration algorithm will never - // cause them to occur (these are only relevant to the LEGACY_DELETE + } else if (left.type === 'index_not_found_exception') { + if (left.index === stateP.tempIndex) { + // another instance has already completed the migration and deleted + // the temporary index + return { ...stateP, controlState: 'MARK_VERSION_INDEX_READY_CONFLICT' }; + } else { + // The migration algorithm will never cause a + // index_not_found_exception for an index other than the temporary + // index handled above. + throwBadResponse(stateP, left as never); + } + } else if (left.type === 'remove_index_not_a_concrete_index') { + // We don't handle this error as the migration algorithm will never + // cause it to occur (this error is only relevant to the LEGACY_DELETE // step). throwBadResponse(stateP, left as never); } else { diff --git a/src/core/server/saved_objects/object_types/constants.ts b/src/core/server/saved_objects/object_types/constants.ts new file mode 100644 index 000000000000..4e05c406c653 --- /dev/null +++ b/src/core/server/saved_objects/object_types/constants.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +/** + * @internal + */ +export const LEGACY_URL_ALIAS_TYPE = 'legacy-url-alias'; diff --git a/src/core/server/saved_objects/object_types/index.ts b/src/core/server/saved_objects/object_types/index.ts new file mode 100644 index 000000000000..1a9bccdc17c2 --- /dev/null +++ b/src/core/server/saved_objects/object_types/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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +export { LEGACY_URL_ALIAS_TYPE } from './constants'; +export { LegacyUrlAlias } from './types'; +export { registerCoreObjectTypes } from './registration'; diff --git a/src/core/server/saved_objects/object_types/registration.test.ts b/src/core/server/saved_objects/object_types/registration.test.ts new file mode 100644 index 000000000000..9bd7b3d61e09 --- /dev/null +++ b/src/core/server/saved_objects/object_types/registration.test.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { typeRegistryMock } from '../saved_objects_type_registry.mock'; +import { LEGACY_URL_ALIAS_TYPE } from './constants'; +import { registerCoreObjectTypes } from './registration'; + +describe('Core saved object types registration', () => { + describe('#registerCoreObjectTypes', () => { + it('registers all expected types', () => { + const typeRegistry = typeRegistryMock.create(); + registerCoreObjectTypes(typeRegistry); + + expect(typeRegistry.registerType).toHaveBeenCalledTimes(1); + expect(typeRegistry.registerType).toHaveBeenCalledWith( + expect.objectContaining({ name: LEGACY_URL_ALIAS_TYPE }) + ); + }); + }); +}); diff --git a/src/core/server/saved_objects/object_types/registration.ts b/src/core/server/saved_objects/object_types/registration.ts new file mode 100644 index 000000000000..82562ac53a10 --- /dev/null +++ b/src/core/server/saved_objects/object_types/registration.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { LEGACY_URL_ALIAS_TYPE } from './constants'; +import { ISavedObjectTypeRegistry, SavedObjectsType, SavedObjectTypeRegistry } from '..'; + +const legacyUrlAliasType: SavedObjectsType = { + name: LEGACY_URL_ALIAS_TYPE, + namespaceType: 'agnostic', + mappings: { + dynamic: false, // we aren't querying or aggregating over this data, so we don't need to specify any fields + properties: {}, + }, + hidden: true, +}; + +/** + * @internal + */ +export function registerCoreObjectTypes( + typeRegistry: ISavedObjectTypeRegistry & Pick +) { + typeRegistry.registerType(legacyUrlAliasType); +} diff --git a/examples/state_containers_examples/server/types.ts b/src/core/server/saved_objects/object_types/types.ts similarity index 61% rename from examples/state_containers_examples/server/types.ts rename to src/core/server/saved_objects/object_types/types.ts index 86dc8d556e4c..8391311cbefd 100644 --- a/examples/state_containers_examples/server/types.ts +++ b/src/core/server/saved_objects/object_types/types.ts @@ -6,7 +6,14 @@ * Public License, v 1. */ -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface StateDemoPluginSetup {} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface StateDemoPluginStart {} +/** + * @internal + */ +export interface LegacyUrlAlias { + targetNamespace: string; + targetType: string; + targetId: string; + lastResolved?: string; + resolveCounter?: number; + disabled?: boolean; +} diff --git a/src/core/server/saved_objects/routes/bulk_create.ts b/src/core/server/saved_objects/routes/bulk_create.ts index eee9b9b1a0bf..6d57eaa3777e 100644 --- a/src/core/server/saved_objects/routes/bulk_create.ts +++ b/src/core/server/saved_objects/routes/bulk_create.ts @@ -29,6 +29,7 @@ export const registerBulkCreateRoute = (router: IRouter, { coreUsageData }: Rout attributes: schema.recordOf(schema.string(), schema.any()), version: schema.maybe(schema.string()), migrationVersion: schema.maybe(schema.recordOf(schema.string(), schema.string())), + coreMigrationVersion: schema.maybe(schema.string()), references: schema.maybe( schema.arrayOf( schema.object({ diff --git a/src/core/server/saved_objects/routes/create.ts b/src/core/server/saved_objects/routes/create.ts index e486580320da..fd256abac352 100644 --- a/src/core/server/saved_objects/routes/create.ts +++ b/src/core/server/saved_objects/routes/create.ts @@ -29,6 +29,7 @@ export const registerCreateRoute = (router: IRouter, { coreUsageData }: RouteDep body: schema.object({ attributes: schema.recordOf(schema.string(), schema.any()), migrationVersion: schema.maybe(schema.recordOf(schema.string(), schema.string())), + coreMigrationVersion: schema.maybe(schema.string()), references: schema.maybe( schema.arrayOf( schema.object({ @@ -45,12 +46,25 @@ export const registerCreateRoute = (router: IRouter, { coreUsageData }: RouteDep router.handleLegacyErrors(async (context, req, res) => { const { type, id } = req.params; const { overwrite } = req.query; - const { attributes, migrationVersion, references, initialNamespaces } = req.body; + const { + attributes, + migrationVersion, + coreMigrationVersion, + references, + initialNamespaces, + } = req.body; const usageStatsClient = coreUsageData.getClient(); usageStatsClient.incrementSavedObjectsCreate({ request: req }).catch(() => {}); - const options = { id, overwrite, migrationVersion, references, initialNamespaces }; + const options = { + id, + overwrite, + migrationVersion, + coreMigrationVersion, + references, + initialNamespaces, + }; const result = await context.core.savedObjects.client.create(type, attributes, options); return res.ok({ body: result }); }) diff --git a/src/core/server/saved_objects/routes/export.ts b/src/core/server/saved_objects/routes/export.ts index aac3f98898e4..9b40855afec2 100644 --- a/src/core/server/saved_objects/routes/export.ts +++ b/src/core/server/saved_objects/routes/export.ts @@ -10,7 +10,7 @@ import { schema } from '@kbn/config-schema'; import stringify from 'json-stable-stringify'; import { createPromiseFromStreams, createMapStream, createConcatStream } from '@kbn/utils'; -import { IRouter } from '../../http'; +import { IRouter, KibanaRequest } from '../../http'; import { CoreUsageDataSetup } from '../../core_usage_data'; import { SavedObjectConfig } from '../saved_objects_config'; import { @@ -78,7 +78,11 @@ const validateOptions = ( includeReferencesDeep, search, }: ExportOptions, - { exportSizeLimit, supportedTypes }: { exportSizeLimit: number; supportedTypes: string[] } + { + exportSizeLimit, + supportedTypes, + request, + }: { exportSizeLimit: number; supportedTypes: string[]; request: KibanaRequest } ): EitherExportOptions => { const hasTypes = (types?.length ?? 0) > 0; const hasObjects = (objects?.length ?? 0) > 0; @@ -106,6 +110,7 @@ const validateOptions = ( objects: objects!, excludeExportDetails, includeReferencesDeep, + request, }; } else { const validationError = validateTypes(types!, supportedTypes); @@ -118,6 +123,7 @@ const validateOptions = ( search, excludeExportDetails, includeReferencesDeep, + request, }; } }; @@ -165,6 +171,7 @@ export const registerExportRoute = ( let options: EitherExportOptions; try { options = validateOptions(cleaned, { + request: req, exportSizeLimit: maxImportExportSize, supportedTypes, }); diff --git a/src/core/server/saved_objects/routes/index.ts b/src/core/server/saved_objects/routes/index.ts index 67a4e305e87a..412dd0e7ffbc 100644 --- a/src/core/server/saved_objects/routes/index.ts +++ b/src/core/server/saved_objects/routes/index.ts @@ -12,6 +12,7 @@ import { Logger } from '../../logging'; import { SavedObjectConfig } from '../saved_objects_config'; import { IKibanaMigrator } from '../migrations'; import { registerGetRoute } from './get'; +import { registerResolveRoute } from './resolve'; import { registerCreateRoute } from './create'; import { registerDeleteRoute } from './delete'; import { registerFindRoute } from './find'; @@ -41,6 +42,7 @@ export function registerRoutes({ const router = http.createRouter('/api/saved_objects/'); registerGetRoute(router, { coreUsageData }); + registerResolveRoute(router, { coreUsageData }); registerCreateRoute(router, { coreUsageData }); registerDeleteRoute(router, { coreUsageData }); registerFindRoute(router, { coreUsageData }); diff --git a/src/core/server/saved_objects/routes/integration_tests/import.test.ts b/src/core/server/saved_objects/routes/integration_tests/import.test.ts index af1e3257479a..af4f57f30f96 100644 --- a/src/core/server/saved_objects/routes/integration_tests/import.test.ts +++ b/src/core/server/saved_objects/routes/integration_tests/import.test.ts @@ -101,7 +101,7 @@ describe(`POST ${URL}`, () => { ) .expect(200); - expect(result.body).toEqual({ success: true, successCount: 0 }); + expect(result.body).toEqual({ success: true, successCount: 0, warnings: [] }); expect(savedObjectsClient.bulkCreate).not.toHaveBeenCalled(); // no objects were created expect(coreUsageStatsClient.incrementSavedObjectsImport).toHaveBeenCalledWith({ request: expect.anything(), @@ -138,6 +138,7 @@ describe(`POST ${URL}`, () => { meta: { title: 'my-pattern-*', icon: 'index-pattern-icon' }, }, ], + warnings: [], }); expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); // successResults objects were created because no resolvable errors are present expect(savedObjectsClient.bulkCreate).toHaveBeenCalledWith( @@ -187,6 +188,7 @@ describe(`POST ${URL}`, () => { meta: { title: mockDashboard.attributes.title, icon: 'dashboard-icon' }, }, ], + warnings: [], }); expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); // successResults objects were created because no resolvable errors are present }); @@ -235,6 +237,7 @@ describe(`POST ${URL}`, () => { error: { type: 'conflict' }, }, ], + warnings: [], }); expect(savedObjectsClient.bulkCreate).not.toHaveBeenCalled(); // successResults objects were not created because resolvable errors are present }); @@ -283,6 +286,7 @@ describe(`POST ${URL}`, () => { meta: { title: mockDashboard.attributes.title, icon: 'dashboard-icon' }, }, ], + warnings: [], }); expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); // successResults objects were created because no resolvable errors are present }); @@ -336,6 +340,7 @@ describe(`POST ${URL}`, () => { meta: { title: mockDashboard.attributes.title, icon: 'dashboard-icon' }, }, ], + warnings: [], }); expect(savedObjectsClient.bulkGet).toHaveBeenCalledTimes(1); expect(savedObjectsClient.bulkGet).toHaveBeenCalledWith( @@ -406,6 +411,7 @@ describe(`POST ${URL}`, () => { meta: { title: mockDashboard.attributes.title, icon: 'dashboard-icon' }, }, ], + warnings: [], }); expect(savedObjectsClient.bulkGet).toHaveBeenCalledTimes(1); expect(savedObjectsClient.bulkGet).toHaveBeenCalledWith( @@ -470,6 +476,7 @@ describe(`POST ${URL}`, () => { meta: { title: mockDashboard.attributes.title, icon: 'dashboard-icon' }, }, ], + warnings: [], }); expect(savedObjectsClient.bulkGet).toHaveBeenCalledTimes(1); expect(savedObjectsClient.bulkGet).toHaveBeenCalledWith( @@ -534,6 +541,7 @@ describe(`POST ${URL}`, () => { destinationId: obj2.id, }, ], + warnings: [], }); expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); // successResults objects were created because no resolvable errors are present expect(savedObjectsClient.bulkCreate).toHaveBeenCalledWith( diff --git a/src/core/server/saved_objects/routes/integration_tests/resolve.test.ts b/src/core/server/saved_objects/routes/integration_tests/resolve.test.ts new file mode 100644 index 000000000000..5ddeb29b8c2d --- /dev/null +++ b/src/core/server/saved_objects/routes/integration_tests/resolve.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import supertest from 'supertest'; +import { registerResolveRoute } from '../resolve'; +import { ContextService } from '../../../context'; +import { savedObjectsClientMock } from '../../service/saved_objects_client.mock'; +import { CoreUsageStatsClient } from '../../../core_usage_data'; +import { coreUsageStatsClientMock } from '../../../core_usage_data/core_usage_stats_client.mock'; +import { coreUsageDataServiceMock } from '../../../core_usage_data/core_usage_data_service.mock'; +import { HttpService, InternalHttpServiceSetup } from '../../../http'; +import { createHttpServer, createCoreContext } from '../../../http/test_utils'; +import { coreMock } from '../../../mocks'; + +const coreId = Symbol('core'); + +describe('GET /api/saved_objects/resolve/{type}/{id}', () => { + let server: HttpService; + let httpSetup: InternalHttpServiceSetup; + let handlerContext: ReturnType; + let savedObjectsClient: ReturnType; + let coreUsageStatsClient: jest.Mocked; + + beforeEach(async () => { + const coreContext = createCoreContext({ coreId }); + server = createHttpServer(coreContext); + + const contextService = new ContextService(coreContext); + httpSetup = await server.setup({ + context: contextService.setup({ pluginDependencies: new Map() }), + }); + + handlerContext = coreMock.createRequestHandlerContext(); + savedObjectsClient = handlerContext.savedObjects.client; + + httpSetup.registerRouteHandlerContext(coreId, 'core', async (ctx, req, res) => { + return handlerContext; + }); + + const router = httpSetup.createRouter('/api/saved_objects/'); + coreUsageStatsClient = coreUsageStatsClientMock.create(); + coreUsageStatsClient.incrementSavedObjectsResolve.mockRejectedValue(new Error('Oh no!')); // intentionally throw this error, which is swallowed, so we can assert that the operation does not fail + const coreUsageData = coreUsageDataServiceMock.createSetupContract(coreUsageStatsClient); + registerResolveRoute(router, { coreUsageData }); + + await server.start(); + }); + + afterEach(async () => { + await server.stop(); + }); + + it('formats successful response', async () => { + const clientResponse = { + saved_object: { + id: 'logstash-*', + title: 'logstash-*', + type: 'logstash-type', + attributes: {}, + timeFieldName: '@timestamp', + notExpandable: true, + references: [], + }, + outcome: 'exactMatch' as 'exactMatch', + }; + + savedObjectsClient.resolve.mockResolvedValue(clientResponse); + + const result = await supertest(httpSetup.server.listener) + .get('/api/saved_objects/resolve/index-pattern/logstash-*') + .expect(200); + + expect(result.body).toEqual(clientResponse); + }); + + it('calls upon savedObjectClient.resolve', async () => { + await supertest(httpSetup.server.listener) + .get('/api/saved_objects/resolve/index-pattern/logstash-*') + .expect(200); + + expect(savedObjectsClient.resolve).toHaveBeenCalled(); + + const args = savedObjectsClient.resolve.mock.calls[0]; + expect(args).toEqual(['index-pattern', 'logstash-*']); + }); +}); diff --git a/src/core/server/saved_objects/routes/integration_tests/resolve_import_errors.test.ts b/src/core/server/saved_objects/routes/integration_tests/resolve_import_errors.test.ts index 7df3b62ab610..6bb2660af06f 100644 --- a/src/core/server/saved_objects/routes/integration_tests/resolve_import_errors.test.ts +++ b/src/core/server/saved_objects/routes/integration_tests/resolve_import_errors.test.ts @@ -113,7 +113,7 @@ describe(`POST ${URL}`, () => { ) .expect(200); - expect(result.body).toEqual({ success: true, successCount: 0 }); + expect(result.body).toEqual({ success: true, successCount: 0, warnings: [] }); expect(savedObjectsClient.bulkCreate).not.toHaveBeenCalled(); // no objects were created expect(coreUsageStatsClient.incrementSavedObjectsResolveImportErrors).toHaveBeenCalledWith({ request: expect.anything(), @@ -153,6 +153,7 @@ describe(`POST ${URL}`, () => { success: true, successCount: 1, successResults: [{ type, id, meta }], + warnings: [], }); expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); // successResults objects were created because no resolvable errors are present expect(savedObjectsClient.bulkCreate).toHaveBeenCalledWith( @@ -190,6 +191,7 @@ describe(`POST ${URL}`, () => { success: true, successCount: 1, successResults: [{ type, id, meta }], + warnings: [], }); expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); // successResults objects were created because no resolvable errors are present expect(savedObjectsClient.bulkCreate).toHaveBeenCalledWith( @@ -228,6 +230,7 @@ describe(`POST ${URL}`, () => { success: true, successCount: 1, successResults: [{ type, id, meta, overwrite: true }], + warnings: [], }); expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); // successResults objects were created because no resolvable errors are present expect(savedObjectsClient.bulkCreate).toHaveBeenCalledWith( @@ -271,6 +274,7 @@ describe(`POST ${URL}`, () => { meta: { title: 'Look at my visualization', icon: 'visualization-icon' }, }, ], + warnings: [], }); expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); // successResults objects were created because no resolvable errors are present expect(savedObjectsClient.bulkCreate).toHaveBeenCalledWith( @@ -319,6 +323,7 @@ describe(`POST ${URL}`, () => { meta: { title: 'Look at my visualization', icon: 'visualization-icon' }, }, ], + warnings: [], }); expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); // successResults objects were created because no resolvable errors are present expect(savedObjectsClient.bulkCreate).toHaveBeenCalledWith( @@ -383,6 +388,7 @@ describe(`POST ${URL}`, () => { destinationId: obj2.id, }, ], + warnings: [], }); expect(savedObjectsClient.bulkCreate).toHaveBeenCalledTimes(1); // successResults objects were created because no resolvable errors are present expect(savedObjectsClient.bulkCreate).toHaveBeenCalledWith( diff --git a/src/core/server/saved_objects/routes/resolve.ts b/src/core/server/saved_objects/routes/resolve.ts new file mode 100644 index 000000000000..28a3f4b87646 --- /dev/null +++ b/src/core/server/saved_objects/routes/resolve.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { schema } from '@kbn/config-schema'; +import { IRouter } from '../../http'; +import { CoreUsageDataSetup } from '../../core_usage_data'; + +interface RouteDependencies { + coreUsageData: CoreUsageDataSetup; +} + +export const registerResolveRoute = (router: IRouter, { coreUsageData }: RouteDependencies) => { + router.get( + { + path: '/resolve/{type}/{id}', + validate: { + params: schema.object({ + type: schema.string(), + id: schema.string(), + }), + }, + }, + router.handleLegacyErrors(async (context, req, res) => { + const { type, id } = req.params; + + const usageStatsClient = coreUsageData.getClient(); + usageStatsClient.incrementSavedObjectsResolve({ request: req }).catch(() => {}); + + const result = await context.core.savedObjects.client.resolve(type, id); + return res.ok({ body: result }); + }) + ); +}; diff --git a/src/core/server/saved_objects/saved_objects_service.test.ts b/src/core/server/saved_objects/saved_objects_service.test.ts index 3c1e217dcc22..4a8caa768660 100644 --- a/src/core/server/saved_objects/saved_objects_service.test.ts +++ b/src/core/server/saved_objects/saved_objects_service.test.ts @@ -25,8 +25,10 @@ import { httpServerMock } from '../http/http_server.mocks'; import { SavedObjectsClientFactoryProvider } from './service/lib'; import { NodesVersionCompatibility } from '../elasticsearch/version_check/ensure_es_version'; import { SavedObjectsRepository } from './service/lib/repository'; +import { registerCoreObjectTypes } from './object_types'; jest.mock('./service/lib/repository'); +jest.mock('./object_types'); describe('SavedObjectsService', () => { const createCoreContext = ({ @@ -67,6 +69,16 @@ describe('SavedObjectsService', () => { }); describe('#setup()', () => { + it('calls registerCoreObjectTypes', async () => { + const coreContext = createCoreContext(); + const soService = new SavedObjectsService(coreContext); + + const mockedRegisterCoreObjectTypes = registerCoreObjectTypes as jest.Mock; + expect(mockedRegisterCoreObjectTypes).not.toHaveBeenCalled(); + await soService.setup(createSetupDeps()); + expect(mockedRegisterCoreObjectTypes).toHaveBeenCalledTimes(1); + }); + describe('#setClientFactoryProvider', () => { it('registers the factory to the clientProvider', async () => { const coreContext = createCoreContext(); @@ -130,6 +142,7 @@ describe('SavedObjectsService', () => { describe('#registerType', () => { it('registers the type to the internal typeRegistry', async () => { + // we mocked registerCoreObjectTypes above, so this test case only reflects direct calls to the registerType method const coreContext = createCoreContext(); const soService = new SavedObjectsService(coreContext); const setup = await soService.setup(createSetupDeps()); diff --git a/src/core/server/saved_objects/saved_objects_service.ts b/src/core/server/saved_objects/saved_objects_service.ts index 9021d7f56c41..131873d14b79 100644 --- a/src/core/server/saved_objects/saved_objects_service.ts +++ b/src/core/server/saved_objects/saved_objects_service.ts @@ -43,6 +43,8 @@ import { SavedObjectsImporter, ISavedObjectsImporter } from './import'; import { registerRoutes } from './routes'; import { ServiceStatus } from '../status'; import { calculateStatus$ } from './status'; +import { registerCoreObjectTypes } from './object_types'; + /** * Saved Objects is Kibana's data persistence mechanism allowing plugins to * use Elasticsearch for storing and querying state. The SavedObjectsServiceSetup API exposes methods @@ -304,6 +306,8 @@ export class SavedObjectsService migratorPromise: this.migrator$.pipe(first()).toPromise(), }); + registerCoreObjectTypes(this.typeRegistry); + return { status$: calculateStatus$( this.migrator$.pipe(switchMap((migrator) => migrator.getStatus$())), @@ -453,6 +457,7 @@ export class SavedObjectsService createExporter: (savedObjectsClient) => new SavedObjectsExporter({ savedObjectsClient, + typeRegistry: this.typeRegistry, exportSizeLimit: this.config!.maxImportExportSize, }), createImporter: (savedObjectsClient) => diff --git a/src/core/server/saved_objects/saved_objects_type_registry.test.ts b/src/core/server/saved_objects/saved_objects_type_registry.test.ts index 7c91baa73c67..0186af6e7628 100644 --- a/src/core/server/saved_objects/saved_objects_type_registry.test.ts +++ b/src/core/server/saved_objects/saved_objects_type_registry.test.ts @@ -25,25 +25,68 @@ describe('SavedObjectTypeRegistry', () => { registry = new SavedObjectTypeRegistry(); }); - it('allows to register types', () => { - registry.registerType(createType({ name: 'typeA' })); - registry.registerType(createType({ name: 'typeB' })); - registry.registerType(createType({ name: 'typeC' })); - - expect( - registry - .getAllTypes() - .map((type) => type.name) - .sort() - ).toEqual(['typeA', 'typeB', 'typeC']); - }); + describe('#registerType', () => { + it('allows to register types', () => { + registry.registerType(createType({ name: 'typeA' })); + registry.registerType(createType({ name: 'typeB' })); + registry.registerType(createType({ name: 'typeC' })); + + expect( + registry + .getAllTypes() + .map((type) => type.name) + .sort() + ).toEqual(['typeA', 'typeB', 'typeC']); + }); - it('throws when trying to register the same type twice', () => { - registry.registerType(createType({ name: 'typeA' })); - registry.registerType(createType({ name: 'typeB' })); - expect(() => { + it('throws when trying to register the same type twice', () => { registry.registerType(createType({ name: 'typeA' })); - }).toThrowErrorMatchingInlineSnapshot(`"Type 'typeA' is already registered"`); + registry.registerType(createType({ name: 'typeB' })); + expect(() => { + registry.registerType(createType({ name: 'typeA' })); + }).toThrowErrorMatchingInlineSnapshot(`"Type 'typeA' is already registered"`); + }); + + it('throws when `management.onExport` is specified but `management.importableAndExportable` is undefined or false', () => { + expect(() => { + registry.registerType( + createType({ + name: 'typeA', + management: { + onExport: (ctx, objs) => objs, + }, + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Type typeA: 'management.importableAndExportable' must be 'true' when specifying 'management.onExport'"` + ); + expect(() => { + registry.registerType( + createType({ + name: 'typeA', + management: { + importableAndExportable: false, + onExport: (ctx, objs) => objs, + }, + }) + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Type typeA: 'management.importableAndExportable' must be 'true' when specifying 'management.onExport'"` + ); + expect(() => { + registry.registerType( + createType({ + name: 'typeA', + management: { + importableAndExportable: true, + onExport: (ctx, objs) => objs, + }, + }) + ); + }).not.toThrow(); + }); + + // TODO: same test with 'onImport' }); describe('#getType', () => { diff --git a/src/core/server/saved_objects/saved_objects_type_registry.ts b/src/core/server/saved_objects/saved_objects_type_registry.ts index 2194353a0758..d2cee700bf66 100644 --- a/src/core/server/saved_objects/saved_objects_type_registry.ts +++ b/src/core/server/saved_objects/saved_objects_type_registry.ts @@ -32,6 +32,7 @@ export class SavedObjectTypeRegistry { if (this.types.has(type.name)) { throw new Error(`Type '${type.name}' is already registered`); } + validateType(type); this.types.set(type.name, deepFreeze(type)); } @@ -116,3 +117,13 @@ export class SavedObjectTypeRegistry { return this.types.get(type)?.management?.importableAndExportable ?? false; } } + +const validateType = ({ name, management }: SavedObjectsType) => { + if (management) { + if (management.onExport && !management.importableAndExportable) { + throw new Error( + `Type ${name}: 'management.importableAndExportable' must be 'true' when specifying 'management.onExport'` + ); + } + } +}; diff --git a/src/core/server/saved_objects/serialization/index.ts b/src/core/server/saved_objects/serialization/index.ts index ba6115dbff3a..3ffaf9cf1e7c 100644 --- a/src/core/server/saved_objects/serialization/index.ts +++ b/src/core/server/saved_objects/serialization/index.ts @@ -15,6 +15,7 @@ export { SavedObjectUnsanitizedDoc, SavedObjectSanitizedDoc, SavedObjectsRawDoc, + SavedObjectsRawDocParseOptions, SavedObjectsRawDocSource, } from './types'; export { SavedObjectsSerializer } from './serializer'; diff --git a/src/core/server/saved_objects/serialization/serializer.test.ts b/src/core/server/saved_objects/serialization/serializer.test.ts index 4d0527aee01b..b09fb1ab30c7 100644 --- a/src/core/server/saved_objects/serialization/serializer.test.ts +++ b/src/core/server/saved_objects/serialization/serializer.test.ts @@ -11,6 +11,7 @@ import { SavedObjectsSerializer } from './serializer'; import { SavedObjectsRawDoc } from './types'; import { typeRegistryMock } from '../saved_objects_type_registry.mock'; import { encodeVersion } from '../version'; +import { LEGACY_URL_ALIAS_TYPE } from '../object_types'; let typeRegistry = typeRegistryMock.create(); typeRegistry.isNamespaceAgnostic.mockReturnValue(true); @@ -131,6 +132,27 @@ describe('#rawToSavedObject', () => { expect(expected).toEqual(actual); }); + test('if specified it copies the _source.coreMigrationVersion property to coreMigrationVersion', () => { + const actual = singleNamespaceSerializer.rawToSavedObject({ + _id: 'foo:bar', + _source: { + type: 'foo', + coreMigrationVersion: '1.2.3', + }, + }); + expect(actual).toHaveProperty('coreMigrationVersion', '1.2.3'); + }); + + test(`if _source.coreMigrationVersion is unspecified it doesn't set coreMigrationVersion`, () => { + const actual = singleNamespaceSerializer.rawToSavedObject({ + _id: 'foo:bar', + _source: { + type: 'foo', + }, + }); + expect(actual).not.toHaveProperty('coreMigrationVersion'); + }); + test(`if version is unspecified it doesn't set version`, () => { const actual = singleNamespaceSerializer.rawToSavedObject({ _id: 'foo:bar', @@ -288,6 +310,7 @@ describe('#rawToSavedObject', () => { foo: '1.2.3', bar: '9.8.7', }, + coreMigrationVersion: '4.5.6', namespace: 'foo-namespace', updated_at: String(new Date()), references: [], @@ -412,6 +435,41 @@ describe('#rawToSavedObject', () => { test(`doesn't copy _source.namespace to namespace`, () => { expect(actual).not.toHaveProperty('namespace'); }); + + describe('with lax namespaceTreatment', () => { + const options = { namespaceTreatment: 'lax' as 'lax' }; + + test(`removes type prefix from _id and, and does not copy _source.namespace to namespace`, () => { + const _actual = multiNamespaceSerializer.rawToSavedObject(raw, options); + expect(_actual).toHaveProperty('id', 'bar'); + expect(_actual).not.toHaveProperty('namespace'); + }); + + test(`removes type and namespace prefix from _id, and copies _source.namespace to namespace`, () => { + const _id = `${raw._source.namespace}:${raw._id}`; + const _actual = multiNamespaceSerializer.rawToSavedObject({ ...raw, _id }, options); + expect(_actual).toHaveProperty('id', 'bar'); + expect(_actual).toHaveProperty('namespace', raw._source.namespace); // "baz" + }); + + test(`removes type and namespace prefix from _id when the namespace matches the type`, () => { + const _raw = createSampleDoc({ _id: 'foo:foo:bar', _source: { namespace: 'foo' } }); + const _actual = multiNamespaceSerializer.rawToSavedObject(_raw, options); + expect(_actual).toHaveProperty('id', 'bar'); + expect(_actual).toHaveProperty('namespace', 'foo'); + }); + + test(`does not remove the entire _id when the namespace matches the type`, () => { + // This is not a realistic/valid document, but we defensively check to ensure we aren't trimming the entire ID. + // In this test case, a multi-namespace document has a raw ID with the type prefix "foo:" and an object ID of "foo:" (no namespace + // prefix). This document *also* has a `namespace` field the same as the type, while it should not have a `namespace` field at all + // since it has no namespace prefix in its raw ID. + const _raw = createSampleDoc({ _id: 'foo:foo:', _source: { namespace: 'foo' } }); + const _actual = multiNamespaceSerializer.rawToSavedObject(_raw, options); + expect(_actual).toHaveProperty('id', 'foo:'); + expect(_actual).not.toHaveProperty('namespace'); + }); + }); }); describe('multi-namespace type with namespaces', () => { @@ -515,6 +573,25 @@ describe('#savedObjectToRaw', () => { expect(actual._source).not.toHaveProperty('migrationVersion'); }); + test('it copies the coreMigrationVersion property to _source.coreMigrationVersion', () => { + const actual = singleNamespaceSerializer.savedObjectToRaw({ + type: '', + attributes: {}, + coreMigrationVersion: '1.2.3', + } as any); + + expect(actual._source).toHaveProperty('coreMigrationVersion', '1.2.3'); + }); + + test(`if unspecified it doesn't add coreMigrationVersion property to _source`, () => { + const actual = singleNamespaceSerializer.savedObjectToRaw({ + type: '', + attributes: {}, + } as any); + + expect(actual._source).not.toHaveProperty('coreMigrationVersion'); + }); + test('it decodes the version property to _seq_no and _primary_term', () => { const actual = singleNamespaceSerializer.savedObjectToRaw({ type: '', @@ -841,6 +918,116 @@ describe('#isRawSavedObject', () => { }); }); + describe('multi-namespace type with a namespace', () => { + test('is true if the id is prefixed with type and the type matches', () => { + expect( + multiNamespaceSerializer.isRawSavedObject({ + _id: 'hello:world', + _source: { + type: 'hello', + hello: {}, + namespace: 'foo', + }, + }) + ).toBeTruthy(); + }); + + test('is false if the id is not prefixed', () => { + expect( + multiNamespaceSerializer.isRawSavedObject({ + _id: 'world', + _source: { + type: 'hello', + hello: {}, + namespace: 'foo', + }, + }) + ).toBeFalsy(); + }); + + test('is false if the id is prefixed with type and namespace', () => { + expect( + multiNamespaceSerializer.isRawSavedObject({ + _id: 'foo:hello:world', + _source: { + type: 'hello', + hello: {}, + namespace: 'foo', + }, + }) + ).toBeFalsy(); + }); + + test('is true if the id is prefixed with type and namespace, and namespaceTreatment is lax', () => { + const options = { namespaceTreatment: 'lax' as 'lax' }; + expect( + multiNamespaceSerializer.isRawSavedObject( + { + _id: 'foo:hello:world', + _source: { + type: 'hello', + hello: {}, + namespace: 'foo', + }, + }, + options + ) + ).toBeTruthy(); + }); + + test(`is false if the type prefix omits the :`, () => { + expect( + namespaceAgnosticSerializer.isRawSavedObject({ + _id: 'helloworld', + _source: { + type: 'hello', + hello: {}, + namespace: 'foo', + }, + }) + ).toBeFalsy(); + }); + + test('is false if the type attribute is missing', () => { + expect( + multiNamespaceSerializer.isRawSavedObject({ + _id: 'hello:world', + _source: { + hello: {}, + namespace: 'foo', + } as any, + }) + ).toBeFalsy(); + }); + + test('is false if the type attribute does not match the id', () => { + expect( + multiNamespaceSerializer.isRawSavedObject({ + _id: 'hello:world', + _source: { + type: 'jam', + jam: {}, + hello: {}, + namespace: 'foo', + }, + }) + ).toBeFalsy(); + }); + + test('is false if there is no [type] attribute', () => { + expect( + multiNamespaceSerializer.isRawSavedObject({ + _id: 'hello:world', + _source: { + type: 'hello', + jam: {}, + namespace: 'foo', + }, + }) + ).toBeFalsy(); + }); + }); + describe('namespace-agnostic type with a namespace', () => { test('is true if the id is prefixed with type and the type matches', () => { expect( @@ -950,6 +1137,13 @@ describe('#generateRawId', () => { }); }); + describe('multi-namespace type with a namespace', () => { + test(`uses the id that is specified and doesn't prefix the namespace`, () => { + const id = multiNamespaceSerializer.generateRawId('foo', 'hello', 'world'); + expect(id).toEqual('hello:world'); + }); + }); + describe('namespace-agnostic type with a namespace', () => { test(`uses the id that is specified and doesn't prefix the namespace`, () => { const id = namespaceAgnosticSerializer.generateRawId('foo', 'hello', 'world'); @@ -957,3 +1151,24 @@ describe('#generateRawId', () => { }); }); }); + +describe('#generateRawLegacyUrlAliasId', () => { + describe(`returns expected value`, () => { + const expected = `${LEGACY_URL_ALIAS_TYPE}:foo:bar:baz`; + + test(`for single-namespace types`, () => { + const id = singleNamespaceSerializer.generateRawLegacyUrlAliasId('foo', 'bar', 'baz'); + expect(id).toEqual(expected); + }); + + test(`for multi-namespace types`, () => { + const id = multiNamespaceSerializer.generateRawLegacyUrlAliasId('foo', 'bar', 'baz'); + expect(id).toEqual(expected); + }); + + test(`for namespace-agnostic types`, () => { + const id = namespaceAgnosticSerializer.generateRawLegacyUrlAliasId('foo', 'bar', 'baz'); + expect(id).toEqual(expected); + }); + }); +}); diff --git a/src/core/server/saved_objects/serialization/serializer.ts b/src/core/server/saved_objects/serialization/serializer.ts index 7a1de0ed2c96..4e9c3b6be03c 100644 --- a/src/core/server/saved_objects/serialization/serializer.ts +++ b/src/core/server/saved_objects/serialization/serializer.ts @@ -6,9 +6,14 @@ * Public License, v 1. */ +import { LEGACY_URL_ALIAS_TYPE } from '../object_types'; import { decodeVersion, encodeVersion } from '../version'; import { ISavedObjectTypeRegistry } from '../saved_objects_type_registry'; -import { SavedObjectsRawDoc, SavedObjectSanitizedDoc } from './types'; +import { + SavedObjectsRawDoc, + SavedObjectSanitizedDoc, + SavedObjectsRawDocParseOptions, +} from './types'; /** * A serializer that can be used to manually convert {@link SavedObjectsRawDoc | raw} or @@ -30,42 +35,60 @@ export class SavedObjectsSerializer { /** * Determines whether or not the raw document can be converted to a saved object. * - * @param {SavedObjectsRawDoc} rawDoc - The raw ES document to be tested + * @param {SavedObjectsRawDoc} doc - The raw ES document to be tested + * @param {SavedObjectsRawDocParseOptions} options - Options for parsing the raw document. */ - public isRawSavedObject(rawDoc: SavedObjectsRawDoc) { - const { type, namespace } = rawDoc._source; - const namespacePrefix = - namespace && this.registry.isSingleNamespace(type) ? `${namespace}:` : ''; - return Boolean( - type && - rawDoc._id.startsWith(`${namespacePrefix}${type}:`) && - rawDoc._source.hasOwnProperty(type) - ); + public isRawSavedObject(doc: SavedObjectsRawDoc, options: SavedObjectsRawDocParseOptions = {}) { + const { namespaceTreatment = 'strict' } = options; + const { _id, _source } = doc; + const { type, namespace } = _source; + if (!type) { + return false; + } + const { idMatchesPrefix } = this.parseIdPrefix(namespace, type, _id, namespaceTreatment); + return idMatchesPrefix && _source.hasOwnProperty(type); } /** * Converts a document from the format that is stored in elasticsearch to the saved object client format. * - * @param {SavedObjectsRawDoc} doc - The raw ES document to be converted to saved object format. + * @param {SavedObjectsRawDoc} doc - The raw ES document to be converted to saved object format. + * @param {SavedObjectsRawDocParseOptions} options - Options for parsing the raw document. */ - public rawToSavedObject(doc: SavedObjectsRawDoc): SavedObjectSanitizedDoc { + public rawToSavedObject( + doc: SavedObjectsRawDoc, + options: SavedObjectsRawDocParseOptions = {} + ): SavedObjectSanitizedDoc { + const { namespaceTreatment = 'strict' } = options; const { _id, _source, _seq_no, _primary_term } = doc; - const { type, namespace, namespaces, originId } = _source; + const { + type, + namespaces, + originId, + migrationVersion, + references, + coreMigrationVersion, + } = _source; const version = _seq_no != null || _primary_term != null ? encodeVersion(_seq_no!, _primary_term!) : undefined; + const { id, namespace } = this.trimIdPrefix(_source.namespace, type, _id, namespaceTreatment); + const includeNamespace = + namespace && (namespaceTreatment === 'lax' || this.registry.isSingleNamespace(type)); + const includeNamespaces = this.registry.isMultiNamespace(type); return { type, - id: this.trimIdPrefix(namespace, type, _id), - ...(namespace && this.registry.isSingleNamespace(type) && { namespace }), - ...(namespaces && this.registry.isMultiNamespace(type) && { namespaces }), + id, + ...(includeNamespace && { namespace }), + ...(includeNamespaces && { namespaces }), ...(originId && { originId }), attributes: _source[type], - references: _source.references || [], - ...(_source.migrationVersion && { migrationVersion: _source.migrationVersion }), + references: references || [], + ...(migrationVersion && { migrationVersion }), + ...(coreMigrationVersion && { coreMigrationVersion }), ...(_source.updated_at && { updated_at: _source.updated_at }), ...(version && { version }), }; @@ -89,6 +112,7 @@ export class SavedObjectsSerializer { updated_at, version, references, + coreMigrationVersion, } = savedObj; const source = { [type]: attributes, @@ -98,6 +122,7 @@ export class SavedObjectsSerializer { ...(namespaces && this.registry.isMultiNamespace(type) && { namespaces }), ...(originId && { originId }), ...(migrationVersion && { migrationVersion }), + ...(coreMigrationVersion && { coreMigrationVersion }), ...(updated_at && { updated_at }), }; @@ -121,22 +146,77 @@ export class SavedObjectsSerializer { return `${namespacePrefix}${type}:${id}`; } - private trimIdPrefix(namespace: string | undefined, type: string, id: string) { + /** + * Given a saved object type and id, generates the compound id that is stored in the raw document for its legacy URL alias. + * + * @param {string} namespace - The namespace of the saved object + * @param {string} type - The saved object type + * @param {string} id - The id of the saved object + */ + public generateRawLegacyUrlAliasId(namespace: string, type: string, id: string) { + return `${LEGACY_URL_ALIAS_TYPE}:${namespace}:${type}:${id}`; + } + + /** + * Given a document's source namespace, type, and raw ID, trim the ID prefix (based on the namespaceType), returning the object ID and the + * detected namespace. A single-namespace object is only considered to exist in a namespace if its raw ID is prefixed by that *and* it has + * the namespace field in its source. + */ + private trimIdPrefix( + sourceNamespace: string | undefined, + type: string, + id: string, + namespaceTreatment: 'strict' | 'lax' + ) { assertNonEmptyString(id, 'document id'); assertNonEmptyString(type, 'saved object type'); - const namespacePrefix = - namespace && this.registry.isSingleNamespace(type) ? `${namespace}:` : ''; - const prefix = `${namespacePrefix}${type}:`; + const { prefix, idMatchesPrefix, namespace } = this.parseIdPrefix( + sourceNamespace, + type, + id, + namespaceTreatment + ); + return { + id: idMatchesPrefix ? id.slice(prefix.length) : id, + namespace, + }; + } - if (!id.startsWith(prefix)) { - return id; + private parseIdPrefix( + sourceNamespace: string | undefined, + type: string, + id: string, + namespaceTreatment: 'strict' | 'lax' + ) { + let prefix: string; // the prefix that is used to validate this raw object ID + let namespace: string | undefined; // the namespace that is in the raw object ID (only for single-namespace objects) + const parseFlexibly = namespaceTreatment === 'lax' && this.registry.isMultiNamespace(type); + if (sourceNamespace && (this.registry.isSingleNamespace(type) || parseFlexibly)) { + prefix = `${sourceNamespace}:${type}:`; + if (parseFlexibly && !checkIdMatchesPrefix(id, prefix)) { + prefix = `${type}:`; + } else { + // this is either a single-namespace object, or is being converted into a multi-namespace object + namespace = sourceNamespace; + } + } else { + // there is no source namespace, OR there is a source namespace but this is not a single-namespace object + prefix = `${type}:`; } - return id.slice(prefix.length); + return { + prefix, + idMatchesPrefix: checkIdMatchesPrefix(id, prefix), + namespace, + }; } } +function checkIdMatchesPrefix(id: string, prefix: string) { + return id.startsWith(prefix) && id.length > prefix.length; +} + function assertNonEmptyString(value: string, name: string) { if (!value || typeof value !== 'string') { throw new TypeError(`Expected "${value}" to be a ${name}`); diff --git a/src/core/server/saved_objects/serialization/types.ts b/src/core/server/saved_objects/serialization/types.ts index 95deedbb7d9c..5de168a08f1d 100644 --- a/src/core/server/saved_objects/serialization/types.ts +++ b/src/core/server/saved_objects/serialization/types.ts @@ -43,6 +43,7 @@ interface SavedObjectDoc { namespace?: string; namespaces?: string[]; migrationVersion?: SavedObjectsMigrationVersion; + coreMigrationVersion?: string; version?: string; updated_at?: string; originId?: string; @@ -68,3 +69,19 @@ export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial * @public */ export type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; + +/** + * Options that can be specified when using the saved objects serializer to parse a raw document. + * + * @public + */ +export interface SavedObjectsRawDocParseOptions { + /** + * Optional setting to allow for lax handling of the raw document ID and namespace field. This is needed when a previously + * single-namespace object type is converted to a multi-namespace object type, and it is only intended to be used during upgrade + * migrations. + * + * If not specified, the default treatment is `strict`. + */ + namespaceTreatment?: 'strict' | 'lax'; +} diff --git a/src/core/server/saved_objects/service/lib/included_fields.test.ts b/src/core/server/saved_objects/service/lib/included_fields.test.ts index 6b00816e4c17..3f2a2d677c42 100644 --- a/src/core/server/saved_objects/service/lib/included_fields.test.ts +++ b/src/core/server/saved_objects/service/lib/included_fields.test.ts @@ -8,7 +8,7 @@ import { includedFields } from './included_fields'; -const BASE_FIELD_COUNT = 9; +const BASE_FIELD_COUNT = 10; describe('includedFields', () => { it('returns undefined if fields are not provided', () => { @@ -32,6 +32,7 @@ Array [ "type", "references", "migrationVersion", + "coreMigrationVersion", "updated_at", "originId", "foo", @@ -66,6 +67,7 @@ Array [ "type", "references", "migrationVersion", + "coreMigrationVersion", "updated_at", "originId", "foo", diff --git a/src/core/server/saved_objects/service/lib/included_fields.ts b/src/core/server/saved_objects/service/lib/included_fields.ts index 3e11d8d8ad4e..7aaca2caf003 100644 --- a/src/core/server/saved_objects/service/lib/included_fields.ts +++ b/src/core/server/saved_objects/service/lib/included_fields.ts @@ -30,6 +30,7 @@ export function includedFields(type: string | string[] = '*', fields?: string[] .concat('type') .concat('references') .concat('migrationVersion') + .concat('coreMigrationVersion') .concat('updated_at') .concat('originId') .concat(fields); // v5 compatibility diff --git a/src/core/server/saved_objects/service/lib/repository.mock.ts b/src/core/server/saved_objects/service/lib/repository.mock.ts index f5b7d30aee4f..93e73f3255b8 100644 --- a/src/core/server/saved_objects/service/lib/repository.mock.ts +++ b/src/core/server/saved_objects/service/lib/repository.mock.ts @@ -17,6 +17,7 @@ const create = (): jest.Mocked => ({ bulkGet: jest.fn(), find: jest.fn(), get: jest.fn(), + resolve: jest.fn(), update: jest.fn(), addToNamespaces: jest.fn(), deleteFromNamespaces: jest.fn(), diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js index d8cdec1e0b8a..216e1c4bd2d3 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.js +++ b/src/core/server/saved_objects/service/lib/repository.test.js @@ -13,6 +13,7 @@ import { ALL_NAMESPACES_STRING } from './utils'; import { SavedObjectsSerializer } from '../../serialization'; import { encodeHitVersion } from '../../version'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; +import { LEGACY_URL_ALIAS_TYPE } from '../../object_types'; import { DocumentMigrator } from '../../migrations/core/document_migrator'; import { mockKibanaMigrator } from '../../migrations/kibana/kibana_migrator.mock'; import { elasticsearchClientMock } from '../../../elasticsearch/client/mocks'; @@ -44,6 +45,7 @@ describe('SavedObjectsRepository', () => { const mockVersionProps = { _seq_no: 1, _primary_term: 1 }; const mockVersion = encodeHitVersion(mockVersionProps); + const KIBANA_VERSION = '2.0.0'; const CUSTOM_INDEX_TYPE = 'customIndex'; const NAMESPACE_AGNOSTIC_TYPE = 'globalType'; const MULTI_NAMESPACE_TYPE = 'shareableType'; @@ -142,7 +144,7 @@ describe('SavedObjectsRepository', () => { const documentMigrator = new DocumentMigrator({ typeRegistry: registry, - kibanaVersion: '2.0.0', + kibanaVersion: KIBANA_VERSION, log: {}, }); @@ -216,6 +218,7 @@ describe('SavedObjectsRepository', () => { rawToSavedObject: jest.fn(), savedObjectToRaw: jest.fn(), generateRawId: jest.fn(), + generateRawLegacyUrlAliasId: jest.fn(), trimIdPrefix: jest.fn(), }; const _serializer = new SavedObjectsSerializer(registry); @@ -501,6 +504,7 @@ describe('SavedObjectsRepository', () => { const expectSuccessResult = (obj) => ({ ...obj, migrationVersion: { [obj.type]: '1.1.1' }, + coreMigrationVersion: KIBANA_VERSION, version: mockVersion, namespaces: obj.namespaces ?? [obj.namespace ?? 'default'], ...mockTimestampFields, @@ -954,6 +958,7 @@ describe('SavedObjectsRepository', () => { ...response.items[0].create, _source: { ...response.items[0].create._source, + coreMigrationVersion: '2.0.0', // the document migrator adds this to all objects before creation namespaces: response.items[0].create._source.namespaces, }, _id: expect.stringMatching(/^myspace:config:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/), @@ -962,6 +967,7 @@ describe('SavedObjectsRepository', () => { ...response.items[1].create, _source: { ...response.items[1].create._source, + coreMigrationVersion: '2.0.0', // the document migrator adds this to all objects before creation namespaces: response.items[1].create._source.namespaces, }, }); @@ -2140,6 +2146,7 @@ describe('SavedObjectsRepository', () => { references, namespaces: [namespace ?? 'default'], migrationVersion: { [type]: '1.1.1' }, + coreMigrationVersion: KIBANA_VERSION, }); }); }); @@ -2724,6 +2731,7 @@ describe('SavedObjectsRepository', () => { 'type', 'references', 'migrationVersion', + 'coreMigrationVersion', 'updated_at', 'originId', 'title', @@ -3254,6 +3262,231 @@ describe('SavedObjectsRepository', () => { }); }); + describe('#resolve', () => { + const type = 'index-pattern'; + const id = 'logstash-*'; + const aliasTargetId = 'some-other-id'; // only used for 'aliasMatch' and 'conflict' outcomes + const namespace = 'foo-namespace'; + + const getMockAliasDocument = (resolveCounter) => ({ + body: { + get: { + _source: { + [LEGACY_URL_ALIAS_TYPE]: { + targetId: aliasTargetId, + ...(resolveCounter && { resolveCounter }), + // other fields are not used by the repository + }, + }, + }, + }, + }); + + describe('outcomes', () => { + describe('error', () => { + const expectNotFoundError = async (type, id, options) => { + await expect(savedObjectsRepository.resolve(type, id, options)).rejects.toThrowError( + createGenericNotFoundError(type, id) + ); + }; + + it('because type is invalid', async () => { + await expectNotFoundError('unknownType', id); + expect(client.update).not.toHaveBeenCalled(); + expect(client.get).not.toHaveBeenCalled(); + expect(client.mget).not.toHaveBeenCalled(); + }); + + it('because type is hidden', async () => { + await expectNotFoundError(HIDDEN_TYPE, id); + expect(client.update).not.toHaveBeenCalled(); + expect(client.get).not.toHaveBeenCalled(); + expect(client.mget).not.toHaveBeenCalled(); + }); + + it('because alias is not used and actual object is not found', async () => { + const options = { namespace: undefined }; + const response = { found: false }; + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) // for actual target + ); + + await expectNotFoundError(type, id, options); + expect(client.update).not.toHaveBeenCalled(); + expect(client.get).toHaveBeenCalledTimes(1); // retrieved actual target + expect(client.mget).not.toHaveBeenCalled(); + }); + + it('because actual object and alias object are both not found', async () => { + const options = { namespace }; + const objectResults = [ + { type, id, found: false }, + { type, id: aliasTargetId, found: false }, + ]; + client.update.mockResolvedValueOnce(getMockAliasDocument()); // for alias object + const response = getMockMgetResponse(objectResults, options.namespace); + client.mget.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) // for actual target + ); + + await expectNotFoundError(type, id, options); + expect(client.update).toHaveBeenCalledTimes(1); // retrieved alias object + expect(client.get).not.toHaveBeenCalled(); + expect(client.mget).toHaveBeenCalledTimes(1); // retrieved actual target and alias target + }); + }); + + describe('exactMatch', () => { + it('because namespace is undefined', async () => { + const options = { namespace: undefined }; + const response = getMockGetResponse({ type, id }); + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) // for actual target + ); + + const result = await savedObjectsRepository.resolve(type, id, options); + expect(client.update).not.toHaveBeenCalled(); + expect(client.get).toHaveBeenCalledTimes(1); // retrieved actual target + expect(client.mget).not.toHaveBeenCalled(); + expect(result).toEqual({ + saved_object: expect.objectContaining({ type, id }), + outcome: 'exactMatch', + }); + }); + + describe('because alias is not used', () => { + const expectExactMatchResult = async (aliasResult) => { + const options = { namespace }; + client.update.mockResolvedValueOnce(aliasResult); // for alias object + const response = getMockGetResponse({ type, id }, options.namespace); + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) // for actual target + ); + + const result = await savedObjectsRepository.resolve(type, id, options); + expect(client.update).toHaveBeenCalledTimes(1); // retrieved alias object + expect(client.get).toHaveBeenCalledTimes(1); // retrieved actual target + expect(client.mget).not.toHaveBeenCalled(); + expect(result).toEqual({ + saved_object: expect.objectContaining({ type, id }), + outcome: 'exactMatch', + }); + }; + + it('since alias call resulted in 404', async () => { + await expectExactMatchResult({ statusCode: 404 }); + }); + + it('since alias is not found', async () => { + await expectExactMatchResult({ body: { get: { found: false } } }); + }); + + it('since alias is disabled', async () => { + await expectExactMatchResult({ + body: { get: { _source: { [LEGACY_URL_ALIAS_TYPE]: { disabled: true } } } }, + }); + }); + }); + + describe('because alias is used', () => { + const expectExactMatchResult = async (objectResults) => { + const options = { namespace }; + client.update.mockResolvedValueOnce(getMockAliasDocument()); // for alias object + const response = getMockMgetResponse(objectResults, options.namespace); + client.mget.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) // for actual target and alias target + ); + + const result = await savedObjectsRepository.resolve(type, id, options); + expect(client.update).toHaveBeenCalledTimes(1); // retrieved alias object + expect(client.get).not.toHaveBeenCalled(); + expect(client.mget).toHaveBeenCalledTimes(1); // retrieved actual target and alias target + expect(result).toEqual({ + saved_object: expect.objectContaining({ type, id }), + outcome: 'exactMatch', + }); + }; + + it('but alias target is not found', async () => { + const objects = [ + { type, id }, + { type, id: aliasTargetId, found: false }, + ]; + await expectExactMatchResult(objects); + }); + + it('but alias target does not exist in this namespace', async () => { + const objects = [ + { type: MULTI_NAMESPACE_TYPE, id }, // correct namespace field is added by getMockMgetResponse + { type: MULTI_NAMESPACE_TYPE, id: aliasTargetId, namespace: `not-${namespace}` }, // overrides namespace field that would otherwise be added by getMockMgetResponse + ]; + await expectExactMatchResult(objects); + }); + }); + }); + + describe('aliasMatch', () => { + const expectAliasMatchResult = async (objectResults) => { + const options = { namespace }; + client.update.mockResolvedValueOnce(getMockAliasDocument()); // for alias object + const response = getMockMgetResponse(objectResults, options.namespace); + client.mget.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) // for actual target and alias target + ); + + const result = await savedObjectsRepository.resolve(type, id, options); + expect(client.update).toHaveBeenCalledTimes(1); // retrieved alias object + expect(client.get).not.toHaveBeenCalled(); + expect(client.mget).toHaveBeenCalledTimes(1); // retrieved actual target and alias target + expect(result).toEqual({ + saved_object: expect.objectContaining({ type, id: aliasTargetId }), + outcome: 'aliasMatch', + }); + }; + + it('because actual target is not found', async () => { + const objects = [ + { type, id, found: false }, + { type, id: aliasTargetId }, + ]; + await expectAliasMatchResult(objects); + }); + + it('because actual target does not exist in this namespace', async () => { + const objects = [ + { type: MULTI_NAMESPACE_TYPE, id, namespace: `not-${namespace}` }, // overrides namespace field that would otherwise be added by getMockMgetResponse + { type: MULTI_NAMESPACE_TYPE, id: aliasTargetId }, // correct namespace field is added by getMockMgetResponse + ]; + await expectAliasMatchResult(objects); + }); + }); + + describe('conflict', () => { + it('because actual target and alias target are both found', async () => { + const options = { namespace }; + const objectResults = [ + { type, id }, // correct namespace field is added by getMockMgetResponse + { type, id: aliasTargetId }, // correct namespace field is added by getMockMgetResponse + ]; + client.update.mockResolvedValueOnce(getMockAliasDocument()); // for alias object + const response = getMockMgetResponse(objectResults, options.namespace); + client.mget.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) // for actual target and alias target + ); + + const result = await savedObjectsRepository.resolve(type, id, options); + expect(client.update).toHaveBeenCalledTimes(1); // retrieved alias object + expect(client.get).not.toHaveBeenCalled(); + expect(client.mget).toHaveBeenCalledTimes(1); // retrieved actual target and alias target + expect(result).toEqual({ + saved_object: expect.objectContaining({ type, id }), + outcome: 'conflict', + }); + }); + }); + }); + }); + describe('#incrementCounter', () => { const type = 'config'; const id = 'one'; diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index 685760e81a2b..2993d4234bd2 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -47,6 +47,7 @@ import { SavedObjectsDeleteFromNamespacesResponse, SavedObjectsRemoveReferencesToOptions, SavedObjectsRemoveReferencesToResponse, + SavedObjectsResolveResponse, } from '../saved_objects_client'; import { SavedObject, @@ -55,6 +56,7 @@ import { SavedObjectsMigrationVersion, MutatingOperationRefreshSetting, } from '../../types'; +import { LegacyUrlAlias, LEGACY_URL_ALIAS_TYPE } from '../../object_types'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { validateConvertFilterToKueryNode } from './filter_utils'; import { @@ -920,25 +922,7 @@ export class SavedObjectsRepository { } as any) as SavedObject; } - const { originId, updated_at: updatedAt } = doc._source; - let namespaces = []; - if (!this._registry.isNamespaceAgnostic(type)) { - namespaces = doc._source.namespaces ?? [ - SavedObjectsUtils.namespaceIdToString(doc._source.namespace), - ]; - } - - return { - id, - type, - namespaces, - ...(originId && { originId }), - ...(updatedAt && { updated_at: updatedAt }), - version: encodeHitVersion(doc), - attributes: doc._source[type], - references: doc._source.references || [], - migrationVersion: doc._source.migrationVersion, - }; + return this.getSavedObjectFromSource(type, id, doc); }), }; } @@ -978,26 +962,122 @@ export class SavedObjectsRepository { throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); } - const { originId, updated_at: updatedAt } = body._source; + return this.getSavedObjectFromSource(type, id, body); + } - let namespaces: string[] = []; - if (!this._registry.isNamespaceAgnostic(type)) { - namespaces = body._source.namespaces ?? [ - SavedObjectsUtils.namespaceIdToString(body._source.namespace), - ]; + /** + * Resolves a single object, using any legacy URL alias if it exists + * + * @param {string} type + * @param {string} id + * @param {object} [options={}] + * @property {string} [options.namespace] + * @returns {promise} - { saved_object, outcome } + */ + async resolve( + type: string, + id: string, + options: SavedObjectsBaseOptions = {} + ): Promise> { + if (!this._allowedTypes.includes(type)) { + throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); } - return { - id, - type, - namespaces, - ...(originId && { originId }), - ...(updatedAt && { updated_at: updatedAt }), - version: encodeHitVersion(body), - attributes: body._source[type], - references: body._source.references || [], - migrationVersion: body._source.migrationVersion, - }; + const namespace = normalizeNamespace(options.namespace); + if (namespace === undefined) { + // legacy URL aliases cannot exist for the default namespace; just attempt to get the object + return this.resolveExactMatch(type, id, options); + } + + const rawAliasId = this._serializer.generateRawLegacyUrlAliasId(namespace, type, id); + const time = this._getCurrentTime(); + + // retrieve the alias, and if it is not disabled, update it + const aliasResponse = await this.client.update( + { + id: rawAliasId, + index: this.getIndexForType(LEGACY_URL_ALIAS_TYPE), + refresh: false, + _source: 'true', + body: { + script: { + source: ` + if (ctx._source[params.type].disabled != true) { + if (ctx._source[params.type].resolveCounter == null) { + ctx._source[params.type].resolveCounter = 1; + } + else { + ctx._source[params.type].resolveCounter += 1; + } + ctx._source[params.type].lastResolved = params.time; + ctx._source.updated_at = params.time; + } + `, + lang: 'painless', + params: { + type: LEGACY_URL_ALIAS_TYPE, + time, + }, + }, + }, + }, + { ignore: [404] } + ); + + if ( + aliasResponse.statusCode === 404 || + aliasResponse.body.get.found === false || + aliasResponse.body.get._source[LEGACY_URL_ALIAS_TYPE]?.disabled === true + ) { + // no legacy URL alias exists, or one exists but it's disabled; just attempt to get the object + return this.resolveExactMatch(type, id, options); + } + const legacyUrlAlias: LegacyUrlAlias = aliasResponse.body.get._source[LEGACY_URL_ALIAS_TYPE]; + const objectIndex = this.getIndexForType(type); + const bulkGetResponse = await this.client.mget( + { + body: { + docs: [ + { + // attempt to find an exact match for the given ID + _id: this._serializer.generateRawId(namespace, type, id), + _index: objectIndex, + }, + { + // also attempt to find a match for the legacy URL alias target ID + _id: this._serializer.generateRawId(namespace, type, legacyUrlAlias.targetId), + _index: objectIndex, + }, + ], + }, + }, + { ignore: [404] } + ); + + const exactMatchDoc = bulkGetResponse?.body.docs[0]; + const aliasMatchDoc = bulkGetResponse?.body.docs[1]; + const foundExactMatch = + exactMatchDoc.found && this.rawDocExistsInNamespace(exactMatchDoc, namespace); + const foundAliasMatch = + aliasMatchDoc.found && this.rawDocExistsInNamespace(aliasMatchDoc, namespace); + + if (foundExactMatch && foundAliasMatch) { + return { + saved_object: this.getSavedObjectFromSource(type, id, exactMatchDoc), + outcome: 'conflict', + }; + } else if (foundExactMatch) { + return { + saved_object: this.getSavedObjectFromSource(type, id, exactMatchDoc), + outcome: 'exactMatch', + }; + } else if (foundAliasMatch) { + return { + saved_object: this.getSavedObjectFromSource(type, legacyUrlAlias.targetId, aliasMatchDoc), + outcome: 'aliasMatch', + }; + } + throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); } /** @@ -1718,7 +1798,7 @@ export class SavedObjectsRepository { if (this._registry.isSingleNamespace(type)) { savedObject.namespaces = [SavedObjectsUtils.namespaceIdToString(namespace)]; } - return omit(savedObject, 'namespace') as SavedObject; + return omit(savedObject, ['namespace']) as SavedObject; } /** @@ -1814,6 +1894,43 @@ export class SavedObjectsRepository { } return body as SavedObjectsRawDoc; } + + private getSavedObjectFromSource( + type: string, + id: string, + doc: { _seq_no: number; _primary_term: number; _source: SavedObjectsRawDocSource } + ): SavedObject { + const { originId, updated_at: updatedAt } = doc._source; + + let namespaces: string[] = []; + if (!this._registry.isNamespaceAgnostic(type)) { + namespaces = doc._source.namespaces ?? [ + SavedObjectsUtils.namespaceIdToString(doc._source.namespace), + ]; + } + + return { + id, + type, + namespaces, + ...(originId && { originId }), + ...(updatedAt && { updated_at: updatedAt }), + version: encodeHitVersion(doc), + attributes: doc._source[type], + references: doc._source.references || [], + migrationVersion: doc._source.migrationVersion, + coreMigrationVersion: doc._source.coreMigrationVersion, + }; + } + + private async resolveExactMatch( + type: string, + id: string, + options: SavedObjectsBaseOptions + ): Promise> { + const object = await this.get(type, id, options); + return { saved_object: object, outcome: 'exactMatch' }; + } } function getBulkOperationError(error: { type: string; reason?: string }, type: string, id: string) { diff --git a/src/core/server/saved_objects/service/saved_objects_client.mock.ts b/src/core/server/saved_objects/service/saved_objects_client.mock.ts index 75269d3a77f6..2dd2bcce7a24 100644 --- a/src/core/server/saved_objects/service/saved_objects_client.mock.ts +++ b/src/core/server/saved_objects/service/saved_objects_client.mock.ts @@ -20,6 +20,7 @@ const create = () => bulkGet: jest.fn(), find: jest.fn(), get: jest.fn(), + resolve: jest.fn(), update: jest.fn(), addToNamespaces: jest.fn(), deleteFromNamespaces: jest.fn(), diff --git a/src/core/server/saved_objects/service/saved_objects_client.test.js b/src/core/server/saved_objects/service/saved_objects_client.test.js index 5cee6bc274f9..e6409fb853bd 100644 --- a/src/core/server/saved_objects/service/saved_objects_client.test.js +++ b/src/core/server/saved_objects/service/saved_objects_client.test.js @@ -115,6 +115,22 @@ test(`#get`, async () => { expect(result).toBe(returnValue); }); +test(`#resolve`, async () => { + const returnValue = Symbol(); + const mockRepository = { + resolve: jest.fn().mockResolvedValue(returnValue), + }; + const client = new SavedObjectsClient(mockRepository); + + const type = Symbol(); + const id = Symbol(); + const options = Symbol(); + const result = await client.resolve(type, id, options); + + expect(mockRepository.resolve).toHaveBeenCalledWith(type, id, options); + expect(result).toBe(returnValue); +}); + test(`#update`, async () => { const returnValue = Symbol(); const mockRepository = { diff --git a/src/core/server/saved_objects/service/saved_objects_client.ts b/src/core/server/saved_objects/service/saved_objects_client.ts index ca1d404e010b..d17f6b082096 100644 --- a/src/core/server/saved_objects/service/saved_objects_client.ts +++ b/src/core/server/saved_objects/service/saved_objects_client.ts @@ -34,6 +34,16 @@ export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions { version?: string; /** {@inheritDoc SavedObjectsMigrationVersion} */ migrationVersion?: SavedObjectsMigrationVersion; + /** + * A semver value that is used when upgrading objects between Kibana versions. If undefined, this will be automatically set to the current + * Kibana version when the object is created. If this is set to a non-semver value, or it is set to a semver value greater than the + * current Kibana version, it will result in an error. + * + * @remarks + * Do not attempt to set this manually. It should only be used if you retrieved an existing object that had the `coreMigrationVersion` + * field set and you want to create it again. + */ + coreMigrationVersion?: string; references?: SavedObjectReference[]; /** The Elasticsearch Refresh setting for this operation */ refresh?: MutatingOperationRefreshSetting; @@ -60,6 +70,16 @@ export interface SavedObjectsBulkCreateObject { references?: SavedObjectReference[]; /** {@inheritDoc SavedObjectsMigrationVersion} */ migrationVersion?: SavedObjectsMigrationVersion; + /** + * A semver value that is used when upgrading objects between Kibana versions. If undefined, this will be automatically set to the current + * Kibana version when the object is created. If this is set to a non-semver value, or it is set to a semver value greater than the + * current Kibana version, it will result in an error. + * + * @remarks + * Do not attempt to set this manually. It should only be used if you retrieved an existing object that had the `coreMigrationVersion` + * field set and you want to create it again. + */ + coreMigrationVersion?: string; /** Optional ID of the original saved object, if this object's `id` was regenerated */ originId?: string; /** @@ -273,6 +293,24 @@ export interface SavedObjectsUpdateResponse references: SavedObjectReference[] | undefined; } +/** + * + * @public + */ +export interface SavedObjectsResolveResponse { + saved_object: SavedObject; + /** + * The outcome for a successful `resolve` call is one of the following values: + * + * * `'exactMatch'` -- One document exactly matched the given ID. + * * `'aliasMatch'` -- One document with a legacy URL alias matched the given ID; in this case the `saved_object.id` field is different + * than the given ID. + * * `'conflict'` -- Two documents matched the given ID, one was an exact match and another with a legacy URL alias; in this case the + * `saved_object` object is the exact match, and the `saved_object.id` field is the same as the given ID. + */ + outcome: 'exactMatch' | 'aliasMatch' | 'conflict'; +} + /** * * @public @@ -379,6 +417,21 @@ export class SavedObjectsClient { return await this._repository.get(type, id, options); } + /** + * Resolves a single object, using any legacy URL alias if it exists + * + * @param type - The type of SavedObject to retrieve + * @param id - The ID of the SavedObject to retrieve + * @param options + */ + async resolve( + type: string, + id: string, + options: SavedObjectsBaseOptions = {} + ): Promise> { + return await this._repository.resolve(type, id, options); + } + /** * Updates an SavedObject * diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts index aa4ab623fe7a..4f47579741a5 100644 --- a/src/core/server/saved_objects/types.ts +++ b/src/core/server/saved_objects/types.ts @@ -9,6 +9,8 @@ import { SavedObjectsClient } from './service/saved_objects_client'; import { SavedObjectsTypeMappingDefinition } from './mappings'; import { SavedObjectMigrationMap } from './migrations'; +import { SavedObjectsExportTransform } from './export'; +import { SavedObjectsImportHook } from './import/types'; export { SavedObjectsImportResponse, @@ -20,6 +22,9 @@ export { SavedObjectsImportUnknownError, SavedObjectsImportFailure, SavedObjectsImportRetry, + SavedObjectsImportActionRequiredWarning, + SavedObjectsImportSimpleWarning, + SavedObjectsImportWarning, } from './import/types'; import { SavedObject } from '../../types'; @@ -237,6 +242,41 @@ export interface SavedObjectsType { * An optional map of {@link SavedObjectMigrationFn | migrations} or a function returning a map of {@link SavedObjectMigrationFn | migrations} to be used to migrate the type. */ migrations?: SavedObjectMigrationMap | (() => SavedObjectMigrationMap); + /** + * If defined, objects of this type will be converted to multi-namespace objects when migrating to this version. + * + * Requirements: + * + * 1. This string value must be a valid semver version + * 2. This type must have previously specified {@link SavedObjectsNamespaceType | `namespaceType: 'single'`} + * 3. This type must also specify {@link SavedObjectsNamespaceType | `namespaceType: 'multiple'`} + * + * Example of a single-namespace type in 7.10: + * + * ```ts + * { + * name: 'foo', + * hidden: false, + * namespaceType: 'single', + * mappings: {...} + * } + * ``` + * + * Example after converting to a multi-namespace type in 7.11: + * + * ```ts + * { + * name: 'foo', + * hidden: false, + * namespaceType: 'multiple', + * mappings: {...}, + * convertToMultiNamespaceTypeVersion: '7.11.0' + * } + * ``` + * + * Note: a migration function can be optionally specified for the same version. + */ + convertToMultiNamespaceTypeVersion?: string; /** * An optional {@link SavedObjectsTypeManagementDefinition | saved objects management section} definition for the type. */ @@ -281,4 +321,58 @@ export interface SavedObjectsTypeManagementDefinition { * {@link Capabilities | uiCapabilities} to check if the user has permission to access the object. */ getInAppUrl?: (savedObject: SavedObject) => { path: string; uiCapabilitiesPath: string }; + /** + * An optional export transform function that can be used transform the objects of the registered type during + * the export process. + * + * It can be used to either mutate the exported objects, or add additional objects (of any type) to the export list. + * + * See {@link SavedObjectsExportTransform | the transform type documentation} for more info and examples. + * + * @remarks `importableAndExportable` must be `true` to specify this property. + */ + onExport?: SavedObjectsExportTransform; + /** + * An optional {@link SavedObjectsImportHook | import hook} to use when importing given type. + * + * Import hooks are executed during the savedObjects import process and allow to interact + * with the imported objects. See the {@link SavedObjectsImportHook | hook documentation} + * for more info. + * + * @example + * Registering a hook displaying a warning about a specific type of object + * ```ts + * // src/plugins/my_plugin/server/plugin.ts + * import { myType } from './saved_objects'; + * + * export class Plugin() { + * setup: (core: CoreSetup) => { + * core.savedObjects.registerType({ + * ...myType, + * management: { + * ...myType.management, + * onImport: (objects) => { + * if(someActionIsNeeded(objects)) { + * return { + * warnings: [ + * { + * type: 'action_required', + * message: 'Objects need to be manually enabled after import', + * actionPath: '/app/my-app/require-activation', + * }, + * ] + * } + * } + * return {}; + * } + * }, + * }); + * } + * } + * ``` + * + * @remarks messages returned in the warnings are user facing and must be translated. + * @remarks `importableAndExportable` must be `true` to specify this property. + */ + onImport?: SavedObjectsImportHook; } diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index a86e556136f7..ceab69a6cdb1 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -132,6 +132,7 @@ import { ReindexParams } from 'elasticsearch'; import { ReindexRethrottleParams } from 'elasticsearch'; import { RenderSearchTemplateParams } from 'elasticsearch'; import { Request } from '@hapi/hapi'; +import { RequestHandlerContext as RequestHandlerContext_2 } from 'src/core/server'; import { ResponseObject } from '@hapi/hapi'; import { ResponseToolkit } from '@hapi/hapi'; import { SchemaTypeError } from '@kbn/config-schema'; @@ -680,6 +681,20 @@ export interface CoreUsageStats { // (undocumented) 'apiCalls.savedObjectsImport.total'?: number; // (undocumented) + 'apiCalls.savedObjectsResolve.namespace.custom.kibanaRequest.no'?: number; + // (undocumented) + 'apiCalls.savedObjectsResolve.namespace.custom.kibanaRequest.yes'?: number; + // (undocumented) + 'apiCalls.savedObjectsResolve.namespace.custom.total'?: number; + // (undocumented) + 'apiCalls.savedObjectsResolve.namespace.default.kibanaRequest.no'?: number; + // (undocumented) + 'apiCalls.savedObjectsResolve.namespace.default.kibanaRequest.yes'?: number; + // (undocumented) + 'apiCalls.savedObjectsResolve.namespace.default.total'?: number; + // (undocumented) + 'apiCalls.savedObjectsResolve.total'?: number; + // (undocumented) 'apiCalls.savedObjectsResolveImportErrors.createNewCopiesEnabled.no'?: number; // (undocumented) 'apiCalls.savedObjectsResolveImportErrors.createNewCopiesEnabled.yes'?: number; @@ -968,7 +983,7 @@ export interface HttpAuth { // @public export interface HttpResources { - register: (route: RouteConfig, handler: HttpResourcesRequestHandler) => void; + register: (route: RouteConfig, handler: HttpResourcesRequestHandler) => void; } // @public @@ -977,7 +992,7 @@ export interface HttpResourcesRenderOptions { } // @public -export type HttpResourcesRequestHandler

= RequestHandler; +export type HttpResourcesRequestHandler

= RequestHandler; // @public export type HttpResourcesResponseOptions = HttpResponseOptions; @@ -1013,7 +1028,7 @@ export interface HttpServiceSetup { auth: HttpAuth; basePath: IBasePath; createCookieSessionStorageFactory: (cookieOptions: SessionStorageCookieOptions) => Promise>; - createRouter: () => IRouter; + createRouter: () => IRouter; csp: ICspConfig; getServerInfo: () => HttpServerInfo; registerAuth: (handler: AuthenticationHandler) => void; @@ -1021,7 +1036,7 @@ export interface HttpServiceSetup { registerOnPreAuth: (handler: OnPreAuthHandler) => void; registerOnPreResponse: (handler: OnPreResponseHandler) => void; registerOnPreRouting: (handler: OnPreRoutingHandler) => void; - registerRouteHandlerContext: (contextName: T, provider: RequestHandlerContextProvider) => RequestHandlerContextContainer; + registerRouteHandlerContext: (contextName: ContextName, provider: RequestHandlerContextProvider) => RequestHandlerContextContainer; } // @public (undocumented) @@ -1047,15 +1062,13 @@ export interface IClusterClient { } // @public -export interface IContextContainer> { +export interface IContextContainer { createHandler(pluginOpaqueId: PluginOpaqueId, handler: THandler): (...rest: HandlerParameters) => ShallowPromise>; - registerContext>(pluginOpaqueId: PluginOpaqueId, contextName: TContextName, provider: IContextProvider): this; + registerContext(pluginOpaqueId: PluginOpaqueId, contextName: ContextName, provider: IContextProvider): this; } -// Warning: (ae-forgotten-export) The symbol "PartialExceptFor" needs to be exported by the entry point index.d.ts -// // @public -export type IContextProvider, TContextName extends keyof HandlerContextType> = (context: PartialExceptFor, 'core'>, ...rest: HandlerParameters) => Promise[TContextName]> | HandlerContextType[TContextName]; +export type IContextProvider = (context: Omit, ...rest: HandlerParameters) => Promise | Context[ContextName]; // @public export interface ICspConfig { @@ -1140,17 +1153,17 @@ export interface IRenderOptions { } // @public -export interface IRouter { - delete: RouteRegistrar<'delete'>; - get: RouteRegistrar<'get'>; +export interface IRouter { + delete: RouteRegistrar<'delete', Context>; + get: RouteRegistrar<'get', Context>; // Warning: (ae-forgotten-export) The symbol "RouterRoute" needs to be exported by the entry point index.d.ts // // @internal getRoutes: () => RouterRoute[]; handleLegacyErrors: RequestHandlerWrapper; - patch: RouteRegistrar<'patch'>; - post: RouteRegistrar<'post'>; - put: RouteRegistrar<'put'>; + patch: RouteRegistrar<'patch', Context>; + post: RouteRegistrar<'post', Context>; + put: RouteRegistrar<'put', Context>; routerPath: string; } @@ -1549,7 +1562,7 @@ export type LegacyElasticsearchClientConfig = Pick = (context: RequestHandlerContext, request: KibanaRequest, response: ResponseFactory) => IKibanaResponse | Promise>; +export type RequestHandler

= (context: Context, request: KibanaRequest, response: ResponseFactory) => IKibanaResponse | Promise>; // @public export interface RequestHandlerContext { @@ -1916,13 +1929,13 @@ export interface RequestHandlerContext { } // @public -export type RequestHandlerContextContainer = IContextContainer>; +export type RequestHandlerContextContainer = IContextContainer; // @public -export type RequestHandlerContextProvider = IContextProvider, TContextName>; +export type RequestHandlerContextProvider = IContextProvider; // @public -export type RequestHandlerWrapper = (handler: RequestHandler) => RequestHandler; +export type RequestHandlerWrapper = (handler: RequestHandler) => RequestHandler; // @public export interface ResolveCapabilitiesOptions { @@ -1975,7 +1988,7 @@ export type RouteContentType = 'application/json' | 'application/*+json' | 'appl export type RouteMethod = SafeRouteMethod | DestructiveRouteMethod; // @public -export type RouteRegistrar = (route: RouteConfig, handler: RequestHandler) => void; +export type RouteRegistrar = (route: RouteConfig, handler: RequestHandler) => void; // @public export class RouteValidationError extends SchemaTypeError { @@ -2033,6 +2046,7 @@ export type SafeRouteMethod = 'get' | 'options'; // @public (undocumented) export interface SavedObject { attributes: T; + coreMigrationVersion?: string; // Warning: (ae-forgotten-export) The symbol "SavedObjectError" needs to be exported by the entry point index.d.ts // // (undocumented) @@ -2064,6 +2078,7 @@ export interface SavedObjectExportBaseOptions { excludeExportDetails?: boolean; includeReferencesDeep?: boolean; namespace?: string; + request: KibanaRequest; } // @public @@ -2116,6 +2131,7 @@ export interface SavedObjectsBaseOptions { export interface SavedObjectsBulkCreateObject { // (undocumented) attributes: T; + coreMigrationVersion?: string; // (undocumented) id?: string; initialNamespaces?: string[]; @@ -2206,6 +2222,7 @@ export class SavedObjectsClient { find(options: SavedObjectsFindOptions): Promise>; get(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>; removeReferencesTo(type: string, id: string, options?: SavedObjectsRemoveReferencesToOptions): Promise; + resolve(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>; update(type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions): Promise>; } @@ -2276,6 +2293,7 @@ export interface SavedObjectsCoreFieldMapping { // @public (undocumented) export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions { + coreMigrationVersion?: string; id?: string; initialNamespaces?: string[]; migrationVersion?: SavedObjectsMigrationVersion; @@ -2385,8 +2403,9 @@ export interface SavedObjectsExportByTypeOptions extends SavedObjectExportBaseOp export class SavedObjectsExporter { // (undocumented) #private; - constructor({ savedObjectsClient, exportSizeLimit, }: { + constructor({ savedObjectsClient, typeRegistry, exportSizeLimit, }: { savedObjectsClient: SavedObjectsClientContract; + typeRegistry: ISavedObjectTypeRegistry; exportSizeLimit: number; }); exportByObjects(options: SavedObjectsExportByObjectOptions): Promise; @@ -2400,8 +2419,10 @@ export class SavedObjectsExportError extends Error { readonly attributes?: Record | undefined; // (undocumented) static exportSizeExceeded(limit: number): SavedObjectsExportError; + static invalidTransformError(objectKeys: string[]): SavedObjectsExportError; // (undocumented) static objectFetchError(objects: SavedObject[]): SavedObjectsExportError; + static objectTransformError(objects: SavedObject[], cause: Error): SavedObjectsExportError; // (undocumented) readonly type: string; } @@ -2416,6 +2437,14 @@ export interface SavedObjectsExportResultDetails { }>; } +// @public +export type SavedObjectsExportTransform = (context: SavedObjectsExportTransformContext, objects: Array>) => SavedObject[] | Promise; + +// @public +export interface SavedObjectsExportTransformContext { + request: KibanaRequest; +} + // @public export type SavedObjectsFieldMapping = SavedObjectsCoreFieldMapping | SavedObjectsComplexFieldMapping; @@ -2473,6 +2502,15 @@ export interface SavedObjectsFindResult extends SavedObject { score: number; } +// @public +export interface SavedObjectsImportActionRequiredWarning { + actionPath: string; + buttonLabel?: string; + message: string; + // (undocumented) + type: 'action_required'; +} + // @public export interface SavedObjectsImportAmbiguousConflictError { // (undocumented) @@ -2542,6 +2580,14 @@ export interface SavedObjectsImportFailure { type: string; } +// @public +export type SavedObjectsImportHook = (objects: Array>) => SavedObjectsImportHookResult | Promise; + +// @public +export interface SavedObjectsImportHookResult { + warnings?: SavedObjectsImportWarning[]; +} + // @public export interface SavedObjectsImportMissingReferencesError { // (undocumented) @@ -2571,6 +2617,8 @@ export interface SavedObjectsImportResponse { successCount: number; // (undocumented) successResults?: SavedObjectsImportSuccess[]; + // (undocumented) + warnings: SavedObjectsImportWarning[]; } // @public @@ -2592,6 +2640,13 @@ export interface SavedObjectsImportRetry { type: string; } +// @public +export interface SavedObjectsImportSimpleWarning { + message: string; + // (undocumented) + type: 'simple'; +} + // @public export interface SavedObjectsImportSuccess { // @deprecated (undocumented) @@ -2625,6 +2680,9 @@ export interface SavedObjectsImportUnsupportedTypeError { type: 'unsupported_type'; } +// @public +export type SavedObjectsImportWarning = SavedObjectsImportSimpleWarning | SavedObjectsImportActionRequiredWarning; + // @public (undocumented) export interface SavedObjectsIncrementCounterField { fieldName: string; @@ -2682,6 +2740,11 @@ export interface SavedObjectsRawDoc { _source: SavedObjectsRawDocSource; } +// @public +export interface SavedObjectsRawDocParseOptions { + namespaceTreatment?: 'strict' | 'lax'; +} + // @public (undocumented) export interface SavedObjectsRemoveReferencesToOptions extends SavedObjectsBaseOptions { refresh?: boolean; @@ -2712,6 +2775,7 @@ export class SavedObjectsRepository { get(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>; incrementCounter(type: string, id: string, counterFields: Array, options?: SavedObjectsIncrementCounterOptions): Promise>; removeReferencesTo(type: string, id: string, options?: SavedObjectsRemoveReferencesToOptions): Promise; + resolve(type: string, id: string, options?: SavedObjectsBaseOptions): Promise>; update(type: string, id: string, attributes: Partial, options?: SavedObjectsUpdateOptions): Promise>; } @@ -2729,13 +2793,21 @@ export interface SavedObjectsResolveImportErrorsOptions { retries: SavedObjectsImportRetry[]; } +// @public (undocumented) +export interface SavedObjectsResolveResponse { + outcome: 'exactMatch' | 'aliasMatch' | 'conflict'; + // (undocumented) + saved_object: SavedObject; +} + // @public export class SavedObjectsSerializer { // @internal constructor(registry: ISavedObjectTypeRegistry); generateRawId(namespace: string | undefined, type: string, id: string): string; - isRawSavedObject(rawDoc: SavedObjectsRawDoc): boolean; - rawToSavedObject(doc: SavedObjectsRawDoc): SavedObjectSanitizedDoc; + generateRawLegacyUrlAliasId(namespace: string, type: string, id: string): string; + isRawSavedObject(doc: SavedObjectsRawDoc, options?: SavedObjectsRawDocParseOptions): boolean; + rawToSavedObject(doc: SavedObjectsRawDoc, options?: SavedObjectsRawDocParseOptions): SavedObjectSanitizedDoc; savedObjectToRaw(savedObj: SavedObjectSanitizedDoc): SavedObjectsRawDoc; } @@ -2770,6 +2842,7 @@ export interface SavedObjectStatusMeta { // @public (undocumented) export interface SavedObjectsType { convertToAliasScript?: string; + convertToMultiNamespaceTypeVersion?: string; hidden: boolean; indexPattern?: string; management?: SavedObjectsTypeManagementDefinition; @@ -2790,6 +2863,8 @@ export interface SavedObjectsTypeManagementDefinition { getTitle?: (savedObject: SavedObject) => string; icon?: string; importableAndExportable?: boolean; + onExport?: SavedObjectsExportTransform; + onImport?: SavedObjectsImportHook; } // @public diff --git a/src/core/server/server.ts b/src/core/server/server.ts index a58aae7e0ff2..60f3f90428d4 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -294,7 +294,7 @@ export class Server { coreSetup.http.registerRouteHandlerContext( coreId, 'core', - async (context, req, res): Promise => { + (context, req, res): RequestHandlerContext['core'] => { return new CoreRouteHandlerContext(this.coreStart!, req); } ); diff --git a/src/core/server/types.ts b/src/core/server/types.ts index 900b4bd3f4b9..74f9fb65db54 100644 --- a/src/core/server/types.ts +++ b/src/core/server/types.ts @@ -8,7 +8,34 @@ /** This module is intended for consumption by public to avoid import issues with server-side code */ export { PluginOpaqueId } from './plugins/types'; -export * from './saved_objects/types'; +export type { + SavedObjectsImportResponse, + SavedObjectsImportSuccess, + SavedObjectsImportConflictError, + SavedObjectsImportAmbiguousConflictError, + SavedObjectsImportUnsupportedTypeError, + SavedObjectsImportMissingReferencesError, + SavedObjectsImportUnknownError, + SavedObjectsImportFailure, + SavedObjectsImportRetry, + SavedObjectsImportWarning, + SavedObjectsImportActionRequiredWarning, + SavedObjectsImportSimpleWarning, + SavedObjectAttributes, + SavedObjectAttribute, + SavedObjectAttributeSingle, + SavedObject, + SavedObjectError, + SavedObjectReference, + SavedObjectsMigrationVersion, + SavedObjectStatusMeta, + SavedObjectsFindOptionsReference, + SavedObjectsFindOptions, + SavedObjectsBaseOptions, + MutatingOperationRefreshSetting, + SavedObjectsClientContract, + SavedObjectsNamespaceType, +} from './saved_objects/types'; export * from './ui_settings/types'; export * from './legacy/types'; export type { EnvironmentMode, PackageInfo } from '@kbn/config'; diff --git a/src/core/types/saved_objects.ts b/src/core/types/saved_objects.ts index 38b8ad0fc532..c19f1febc97b 100644 --- a/src/core/types/saved_objects.ts +++ b/src/core/types/saved_objects.ts @@ -82,6 +82,8 @@ export interface SavedObject { references: SavedObjectReference[]; /** {@inheritdoc SavedObjectsMigrationVersion} */ migrationVersion?: SavedObjectsMigrationVersion; + /** A semver value that is used when upgrading objects between Kibana versions. */ + coreMigrationVersion?: string; /** Namespace(s) that this saved object exists in. This attribute is only used for multi-namespace saved object types. */ namespaces?: string[]; /** diff --git a/src/core/utils/context.test.ts b/src/core/utils/context.test.ts deleted file mode 100644 index 42cb8d635ae0..000000000000 --- a/src/core/utils/context.test.ts +++ /dev/null @@ -1,268 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { ContextContainer } from './context'; -import { PluginOpaqueId } from '../server'; - -const pluginA = Symbol('pluginA'); -const pluginB = Symbol('pluginB'); -const pluginC = Symbol('pluginC'); -const pluginD = Symbol('pluginD'); -const plugins: ReadonlyMap = new Map([ - [pluginA, []], - [pluginB, [pluginA]], - [pluginC, [pluginA, pluginB]], - [pluginD, []], -]); - -interface MyContext { - core1: string; - core2: number; - ctxFromA: string; - ctxFromB: number; - ctxFromC: boolean; - ctxFromD: object; -} - -const coreId = Symbol(); - -describe('ContextContainer', () => { - it('does not allow the same context to be registered twice', () => { - const contextContainer = new ContextContainer<(context: MyContext) => string>(plugins, coreId); - contextContainer.registerContext(coreId, 'ctxFromA', () => 'aString'); - - expect(() => - contextContainer.registerContext(coreId, 'ctxFromA', () => 'aString') - ).toThrowErrorMatchingInlineSnapshot( - `"Context provider for ctxFromA has already been registered."` - ); - }); - - describe('registerContext', () => { - it('throws error if called with an unknown symbol', async () => { - const contextContainer = new ContextContainer<(context: MyContext) => string>( - plugins, - coreId - ); - await expect(() => - contextContainer.registerContext(Symbol('unknown'), 'ctxFromA', jest.fn()) - ).toThrowErrorMatchingInlineSnapshot( - `"Cannot register context for unknown plugin: Symbol(unknown)"` - ); - }); - }); - - describe('context building', () => { - it('resolves dependencies', async () => { - const contextContainer = new ContextContainer<(context: MyContext) => string>( - plugins, - coreId - ); - expect.assertions(8); - contextContainer.registerContext(coreId, 'core1', (context) => { - expect(context).toEqual({}); - return 'core'; - }); - - contextContainer.registerContext(pluginA, 'ctxFromA', (context) => { - expect(context).toEqual({ core1: 'core' }); - return 'aString'; - }); - contextContainer.registerContext(pluginB, 'ctxFromB', (context) => { - expect(context).toEqual({ core1: 'core', ctxFromA: 'aString' }); - return 299; - }); - contextContainer.registerContext(pluginC, 'ctxFromC', (context) => { - expect(context).toEqual({ core1: 'core', ctxFromA: 'aString', ctxFromB: 299 }); - return false; - }); - contextContainer.registerContext(pluginD, 'ctxFromD', (context) => { - expect(context).toEqual({ core1: 'core' }); - return {}; - }); - - const rawHandler1 = jest.fn(() => 'handler1'); - const handler1 = contextContainer.createHandler(pluginC, rawHandler1); - - const rawHandler2 = jest.fn(() => 'handler2'); - const handler2 = contextContainer.createHandler(pluginD, rawHandler2); - - await handler1(); - await handler2(); - - // Should have context from pluginC, its deps, and core - expect(rawHandler1).toHaveBeenCalledWith({ - core1: 'core', - ctxFromA: 'aString', - ctxFromB: 299, - ctxFromC: false, - }); - - // Should have context from pluginD, and core - expect(rawHandler2).toHaveBeenCalledWith({ - core1: 'core', - ctxFromD: {}, - }); - }); - - it('exposes all core context to all providers regardless of registration order', async () => { - expect.assertions(4); - - const contextContainer = new ContextContainer<(context: MyContext) => string>( - plugins, - coreId - ); - contextContainer - .registerContext(pluginA, 'ctxFromA', (context) => { - expect(context).toEqual({ core1: 'core', core2: 101 }); - return `aString ${context.core1} ${context.core2}`; - }) - .registerContext(coreId, 'core1', () => 'core') - .registerContext(coreId, 'core2', () => 101) - .registerContext(pluginB, 'ctxFromB', (context) => { - expect(context).toEqual({ core1: 'core', core2: 101, ctxFromA: 'aString core 101' }); - return 277; - }); - - const rawHandler1 = jest.fn(() => 'handler1'); - const handler1 = contextContainer.createHandler(pluginB, rawHandler1); - - expect(await handler1()).toEqual('handler1'); - - expect(rawHandler1).toHaveBeenCalledWith({ - core1: 'core', - core2: 101, - ctxFromA: 'aString core 101', - ctxFromB: 277, - }); - }); - - it('exposes all core context to core providers', async () => { - expect.assertions(4); - const contextContainer = new ContextContainer<(context: MyContext) => string>( - plugins, - coreId - ); - - contextContainer - .registerContext(coreId, 'core1', (context) => { - expect(context).toEqual({}); - return 'core'; - }) - .registerContext(coreId, 'core2', (context) => { - expect(context).toEqual({ core1: 'core' }); - return 101; - }); - - const rawHandler1 = jest.fn(() => 'handler1'); - const handler1 = contextContainer.createHandler(pluginA, rawHandler1); - - expect(await handler1()).toEqual('handler1'); - - // If no context is registered for pluginA, only core contexts should be exposed - expect(rawHandler1).toHaveBeenCalledWith({ - core1: 'core', - core2: 101, - }); - }); - - it('does not expose plugin contexts to core handler', async () => { - const contextContainer = new ContextContainer<(context: MyContext) => string>( - plugins, - coreId - ); - - contextContainer - .registerContext(coreId, 'core1', (context) => 'core') - .registerContext(pluginA, 'ctxFromA', (context) => 'aString'); - - const rawHandler1 = jest.fn(() => 'handler1'); - const handler1 = contextContainer.createHandler(coreId, rawHandler1); - - expect(await handler1()).toEqual('handler1'); - // pluginA context should not be present in a core handler - expect(rawHandler1).toHaveBeenCalledWith({ - core1: 'core', - }); - }); - - it('passes additional arguments to providers', async () => { - expect.assertions(6); - const contextContainer = new ContextContainer< - (context: MyContext, arg1: string, arg2: number) => string - >(plugins, coreId); - - contextContainer.registerContext(coreId, 'core1', (context, str, num) => { - expect(str).toEqual('passed string'); - expect(num).toEqual(77); - return `core ${str}`; - }); - - contextContainer.registerContext(pluginD, 'ctxFromD', (context, str, num) => { - expect(str).toEqual('passed string'); - expect(num).toEqual(77); - return { - num: 77, - }; - }); - - const rawHandler1 = jest.fn(() => 'handler1'); - const handler1 = contextContainer.createHandler(pluginD, rawHandler1); - - expect(await handler1('passed string', 77)).toEqual('handler1'); - - expect(rawHandler1).toHaveBeenCalledWith( - { - core1: 'core passed string', - ctxFromD: { - num: 77, - }, - }, - 'passed string', - 77 - ); - }); - }); - - describe('createHandler', () => { - it('throws error if called with an unknown symbol', async () => { - const contextContainer = new ContextContainer<(context: MyContext) => string>( - plugins, - coreId - ); - await expect(() => - contextContainer.createHandler(Symbol('unknown'), jest.fn()) - ).toThrowErrorMatchingInlineSnapshot( - `"Cannot create handler for unknown plugin: Symbol(unknown)"` - ); - }); - - it('returns value from original handler', async () => { - const contextContainer = new ContextContainer<(context: MyContext) => string>( - plugins, - coreId - ); - const rawHandler1 = jest.fn(() => 'handler1'); - const handler1 = contextContainer.createHandler(pluginA, rawHandler1); - - expect(await handler1()).toEqual('handler1'); - }); - - it('passes additional arguments to handlers', async () => { - const contextContainer = new ContextContainer< - (context: MyContext, arg1: string, arg2: number) => string - >(plugins, coreId); - - const rawHandler1 = jest.fn(() => 'handler1'); - const handler1 = contextContainer.createHandler(pluginA, rawHandler1); - - await handler1('passed string', 77); - expect(rawHandler1).toHaveBeenCalledWith({}, 'passed string', 77); - }); - }); -}); diff --git a/src/core/utils/index.ts b/src/core/utils/index.ts index 0a480606b999..d365b15866c3 100644 --- a/src/core/utils/index.ts +++ b/src/core/utils/index.ts @@ -6,12 +6,4 @@ * Public License, v 1. */ -export { - ContextContainer, - HandlerContextType, - HandlerFunction, - HandlerParameters, - IContextContainer, - IContextProvider, -} from './context'; export { DEFAULT_APP_CATEGORIES } from './default_app_categories'; diff --git a/src/dev/build/lib/fs.ts b/src/dev/build/lib/fs.ts index 451462d0cb14..f01fbc6283ec 100644 --- a/src/dev/build/lib/fs.ts +++ b/src/dev/build/lib/fs.ts @@ -153,7 +153,7 @@ export async function copy(source: string, destination: string, options: CopyOpt interface CopyAllOptions { select?: string[]; dot?: boolean; - time?: string | number | Date; + time?: Date; } export async function copyAll( @@ -161,7 +161,7 @@ export async function copyAll( destination: string, options: CopyAllOptions = {} ) { - const { select = ['**/*'], dot = false, time = Date.now() } = options; + const { select = ['**/*'], dot = false, time = new Date() } = options; assertAbsolute(sourceDir); assertAbsolute(destination); diff --git a/src/dev/build/lib/integration_tests/fs.test.ts b/src/dev/build/lib/integration_tests/fs.test.ts index 6052924f3492..fa8a534d6e6b 100644 --- a/src/dev/build/lib/integration_tests/fs.test.ts +++ b/src/dev/build/lib/integration_tests/fs.test.ts @@ -244,6 +244,20 @@ describe('copyAll()', () => { expect(Math.abs(fooDir.atimeMs - time.getTime())).toBeLessThan(oneDay); expect(Math.abs(barTxt.mtimeMs - time.getTime())).toBeLessThan(oneDay); }); + + it('defaults atime and mtime to now', async () => { + const destination = resolve(TMP, 'a/b/c/d/e/f'); + await copyAll(FIXTURES, destination); + const barTxt = statSync(resolve(destination, 'foo_dir/bar.txt')); + const fooDir = statSync(resolve(destination, 'foo_dir')); + + // precision is platform specific + const now = new Date(); + const oneDay = 86400000; + expect(Math.abs(barTxt.atimeMs - now.getTime())).toBeLessThan(oneDay); + expect(Math.abs(fooDir.atimeMs - now.getTime())).toBeLessThan(oneDay); + expect(Math.abs(barTxt.mtimeMs - now.getTime())).toBeLessThan(oneDay); + }); }); describe('getFileHash()', () => { diff --git a/src/dev/run_find_plugins_with_circular_deps.ts b/src/dev/run_find_plugins_with_circular_deps.ts index f86802f67c62..6e1667dbedfc 100644 --- a/src/dev/run_find_plugins_with_circular_deps.ts +++ b/src/dev/run_find_plugins_with_circular_deps.ts @@ -20,8 +20,6 @@ interface Options { type CircularDepList = Set; const allowedList: CircularDepList = new Set([ - 'x-pack/plugins/actions -> x-pack/plugins/case', - 'x-pack/plugins/case -> x-pack/plugins/security_solution', 'x-pack/plugins/apm -> x-pack/plugins/infra', 'x-pack/plugins/lists -> x-pack/plugins/security_solution', 'x-pack/plugins/security -> x-pack/plugins/spaces', diff --git a/src/plugins/advanced_settings/kibana.json b/src/plugins/advanced_settings/kibana.json index df0d31a904c5..68b133f382c3 100644 --- a/src/plugins/advanced_settings/kibana.json +++ b/src/plugins/advanced_settings/kibana.json @@ -5,5 +5,5 @@ "ui": true, "requiredPlugins": ["management"], "optionalPlugins": ["home", "usageCollection"], - "requiredBundles": ["kibanaReact", "home"] + "requiredBundles": ["kibanaReact", "kibanaUtils", "home"] } diff --git a/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx b/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx index f6490e2560c5..aaa7b1c10a41 100644 --- a/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx +++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { Observable } from 'rxjs'; import { ReactWrapper } from 'enzyme'; -import { mountWithI18nProvider } from '@kbn/test/jest'; +import { mountWithI18nProvider, shallowWithI18nProvider } from '@kbn/test/jest'; import dedent from 'dedent'; import { PublicUiSettingsParams, @@ -17,9 +17,10 @@ import { UiSettingsType, } from '../../../../core/public'; import { FieldSetting } from './types'; -import { AdvancedSettingsComponent } from './advanced_settings'; +import { AdvancedSettings } from './advanced_settings'; import { notificationServiceMock, docLinksServiceMock } from '../../../../core/public/mocks'; import { ComponentRegistry } from '../component_registry'; +import { Search } from './components/search'; jest.mock('./components/field', () => ({ Field: () => { @@ -222,10 +223,30 @@ function mockConfig() { } describe('AdvancedSettings', () => { + const defaultQuery = 'test:string:setting'; + const mockHistory = { + listen: jest.fn(), + } as any; + const locationSpy = jest.spyOn(window, 'location', 'get'); + + afterAll(() => { + locationSpy.mockRestore(); + }); + + const mockQuery = (query = defaultQuery) => { + locationSpy.mockImplementation( + () => + ({ + search: `?query=${query}`, + } as any) + ); + }; + it('should render specific setting if given setting key', async () => { + mockQuery(); const component = mountWithI18nProvider( - { component .find('Field') .filterWhere( - (n: ReactWrapper) => - (n.prop('setting') as Record).name === 'test:string:setting' + (n: ReactWrapper) => (n.prop('setting') as Record).name === defaultQuery ) ).toHaveLength(1); }); it('should render read-only when saving is disabled', async () => { + mockQuery(); const component = mountWithI18nProvider( - { component .find('Field') .filterWhere( - (n: ReactWrapper) => - (n.prop('setting') as Record).name === 'test:string:setting' + (n: ReactWrapper) => (n.prop('setting') as Record).name === defaultQuery ) .prop('enableSaving') ).toBe(false); }); + + it('should render unfiltered with query parsing error', async () => { + const badQuery = 'category:(accessibility))'; + mockQuery(badQuery); + const { toasts } = notificationServiceMock.createStartContract(); + const getComponent = () => + shallowWithI18nProvider( + + ); + + expect(getComponent).not.toThrow(); + expect(toasts.addWarning).toHaveBeenCalledTimes(1); + const component = getComponent(); + expect(component.find(Search).prop('query').text).toEqual(''); + }); }); diff --git a/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx b/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx index 1b38e9356cbb..b9b447f739fa 100644 --- a/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx +++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.tsx @@ -8,22 +8,35 @@ import React, { Component } from 'react'; import { Subscription } from 'rxjs'; -import { Comparators, EuiFlexGroup, EuiFlexItem, EuiSpacer, Query } from '@elastic/eui'; +import { UnregisterCallback } from 'history'; +import { parse } from 'query-string'; -import { useParams } from 'react-router-dom'; import { UiCounterMetricType } from '@kbn/analytics'; +import { Comparators, EuiFlexGroup, EuiFlexItem, EuiSpacer, Query } from '@elastic/eui'; + +import { + IUiSettingsClient, + DocLinksStart, + ToastsStart, + ScopedHistory, +} from '../../../../core/public'; +import { url } from '../../../kibana_utils/public'; + import { CallOuts } from './components/call_outs'; import { Search } from './components/search'; import { Form } from './components/form'; import { AdvancedSettingsVoiceAnnouncement } from './components/advanced_settings_voice_announcement'; -import { IUiSettingsClient, DocLinksStart, ToastsStart } from '../../../../core/public/'; import { ComponentRegistry } from '../'; import { getAriaName, toEditableConfig, DEFAULT_CATEGORY } from './lib'; import { FieldSetting, SettingsChanges } from './types'; +import { parseErrorMsg } from './components/search/search'; + +export const QUERY = 'query'; interface AdvancedSettingsProps { + history: ScopedHistory; enableSaving: boolean; uiSettings: IUiSettingsClient; dockLinks: DocLinksStart['links']; @@ -32,10 +45,6 @@ interface AdvancedSettingsProps { trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; } -interface AdvancedSettingsComponentProps extends AdvancedSettingsProps { - queryText: string; -} - interface AdvancedSettingsState { footerQueryMatched: boolean; query: Query; @@ -44,30 +53,25 @@ interface AdvancedSettingsState { type GroupedSettings = Record; -export class AdvancedSettingsComponent extends Component< - AdvancedSettingsComponentProps, - AdvancedSettingsState -> { +export class AdvancedSettings extends Component { private settings: FieldSetting[]; private groupedSettings: GroupedSettings; private categoryCounts: Record; private categories: string[] = []; private uiSettingsSubscription?: Subscription; + private unregister: UnregisterCallback; - constructor(props: AdvancedSettingsComponentProps) { + constructor(props: AdvancedSettingsProps) { super(props); this.settings = this.initSettings(this.props.uiSettings); this.groupedSettings = this.initGroupedSettings(this.settings); this.categories = this.initCategories(this.groupedSettings); this.categoryCounts = this.initCategoryCounts(this.groupedSettings); - - const parsedQuery = Query.parse(this.props.queryText ? getAriaName(this.props.queryText) : ''); - this.state = { - query: parsedQuery, - footerQueryMatched: false, - filteredSettings: this.mapSettings(Query.execute(parsedQuery, this.settings)), - }; + this.state = this.getQueryState(undefined, true); + this.unregister = this.props.history.listen(({ search }) => { + this.setState(this.getQueryState(search)); + }); } init(config: IUiSettingsClient) { @@ -134,11 +138,50 @@ export class AdvancedSettingsComponent extends Component< } componentWillUnmount() { - if (this.uiSettingsSubscription) { - this.uiSettingsSubscription.unsubscribe(); + this.uiSettingsSubscription?.unsubscribe?.(); + this.unregister?.(); + } + + private getQuery(queryString: string, intialQuery = false): Query { + try { + const query = intialQuery ? getAriaName(queryString) : queryString ?? ''; + return Query.parse(query); + } catch ({ message }) { + this.props.toasts.addWarning({ + title: parseErrorMsg, + text: message, + }); + return Query.parse(''); } } + private getQueryText(search?: string): string { + const queryParams = parse(search ?? window.location.search) ?? {}; + return (queryParams[QUERY] as string) ?? ''; + } + + private getQueryState(search?: string, intialQuery = false): AdvancedSettingsState { + const queryString = this.getQueryText(search); + const query = this.getQuery(queryString, intialQuery); + const filteredSettings = this.mapSettings(Query.execute(query, this.settings)); + const footerQueryMatched = Object.keys(filteredSettings).length > 0; + + return { + query, + filteredSettings, + footerQueryMatched, + }; + } + + setUrlQuery(q: string = '') { + const search = url.addQueryParam(window.location.search, QUERY, q); + + this.props.history.push({ + pathname: '', // remove any route query param + search, + }); + } + mapConfig(config: IUiSettingsClient) { const all = config.getAll(); return Object.entries(all) @@ -167,18 +210,11 @@ export class AdvancedSettingsComponent extends Component< } onQueryChange = ({ query }: { query: Query }) => { - this.setState({ - query, - filteredSettings: this.mapSettings(Query.execute(query, this.settings)), - }); + this.setUrlQuery(query.text); }; clearQuery = () => { - this.setState({ - query: Query.parse(''), - footerQueryMatched: false, - filteredSettings: this.groupedSettings, - }); + this.setUrlQuery(''); }; onFooterQueryMatchChange = (matched: boolean) => { @@ -244,18 +280,3 @@ export class AdvancedSettingsComponent extends Component< ); } } - -export const AdvancedSettings = (props: AdvancedSettingsProps) => { - const { query } = useParams<{ query: string }>(); - return ( - - ); -}; diff --git a/src/plugins/advanced_settings/public/management_app/components/search/search.tsx b/src/plugins/advanced_settings/public/management_app/components/search/search.tsx index cd22c3d6db72..bc875dd8de78 100644 --- a/src/plugins/advanced_settings/public/management_app/components/search/search.tsx +++ b/src/plugins/advanced_settings/public/management_app/components/search/search.tsx @@ -12,12 +12,19 @@ import { EuiSearchBar, EuiFormErrorText, Query } from '@elastic/eui'; import { getCategoryName } from '../../lib'; +export const CATEGORY_FIELD = 'category'; + interface SearchProps { categories: string[]; query: Query; onQueryChange: ({ query }: { query: Query }) => void; } +export const parseErrorMsg = i18n.translate( + 'advancedSettings.searchBar.unableToParseQueryErrorMessage', + { defaultMessage: 'Unable to parse query' } +); + export class Search extends PureComponent { private categories: Array<{ value: string; name: string }> = []; @@ -67,7 +74,7 @@ export class Search extends PureComponent { const filters = [ { type: 'field_value_selection' as const, - field: 'category', + field: CATEGORY_FIELD, name: i18n.translate('advancedSettings.categorySearchLabel', { defaultMessage: 'Category', }), @@ -78,10 +85,6 @@ export class Search extends PureComponent { let queryParseError; if (!this.state.isSearchTextValid) { - const parseErrorMsg = i18n.translate( - 'advancedSettings.searchBar.unableToParseQueryErrorMessage', - { defaultMessage: 'Unable to parse query' } - ); queryParseError = ( {`${parseErrorMsg}. ${this.state.parseErrorMessage}`} ); diff --git a/src/plugins/advanced_settings/public/management_app/lib/get_aria_name.test.ts b/src/plugins/advanced_settings/public/management_app/lib/get_aria_name.test.ts index abe0e37d606a..c00ae028dfc4 100644 --- a/src/plugins/advanced_settings/public/management_app/lib/get_aria_name.test.ts +++ b/src/plugins/advanced_settings/public/management_app/lib/get_aria_name.test.ts @@ -22,6 +22,15 @@ describe('Settings', function () { expect(getAriaName()).to.be(''); expect(getAriaName(undefined)).to.be(''); }); + + it('should preserve category string', function () { + expect(getAriaName('xPack:fooBar:foo_bar_baz category:(general)')).to.be( + 'x pack foo bar foo bar baz category:(general)' + ); + expect(getAriaName('xPack:fooBar:foo_bar_baz category:(general or discover)')).to.be( + 'x pack foo bar foo bar baz category:(general or discover)' + ); + }); }); }); }); diff --git a/src/plugins/advanced_settings/public/management_app/lib/get_aria_name.ts b/src/plugins/advanced_settings/public/management_app/lib/get_aria_name.ts index 1fe43fc6ba86..6d4817934083 100644 --- a/src/plugins/advanced_settings/public/management_app/lib/get_aria_name.ts +++ b/src/plugins/advanced_settings/public/management_app/lib/get_aria_name.ts @@ -8,15 +8,46 @@ import { words } from 'lodash'; +import { Query } from '@elastic/eui'; + +import { CATEGORY_FIELD } from '../components/search/search'; + +const mapWords = (name?: string): string => + words(name ?? '') + .map((word) => word.toLowerCase()) + .join(' '); + /** * @name {string} the name of the configuration object * @returns {string} a space delimited, lowercase string with * special characters removed. * - * Example: 'xPack:fooBar:foo_bar_baz' -> 'x pack foo bar foo bar baz' + * Examples: + * - `xPack:fooBar:foo_bar_baz` -> `x pack foo bar foo bar baz` + * - `xPack:fooBar:foo_bar_baz category:(general)` -> `x pack foo bar foo bar baz category:(general)` */ export function getAriaName(name?: string) { - return words(name || '') - .map((word) => word.toLowerCase()) - .join(' '); + if (!name) { + return ''; + } + + const query = Query.parse(name); + + if (query.hasOrFieldClause(CATEGORY_FIELD)) { + const categories = query.getOrFieldClause(CATEGORY_FIELD); + const termValue = mapWords(query.removeOrFieldClauses(CATEGORY_FIELD).text); + + if (!categories || !Array.isArray(categories.value)) { + return termValue; + } + + let categoriesQuery = Query.parse(''); + categories.value.forEach((v) => { + categoriesQuery = categoriesQuery.addOrFieldValue(CATEGORY_FIELD, v); + }); + + return `${termValue} ${categoriesQuery.text}`; + } + + return mapWords(name); } diff --git a/src/plugins/advanced_settings/public/management_app/mount_management_section.tsx b/src/plugins/advanced_settings/public/management_app/mount_management_section.tsx index b48f3eff7453..21a8a8cbd05a 100644 --- a/src/plugins/advanced_settings/public/management_app/mount_management_section.tsx +++ b/src/plugins/advanced_settings/public/management_app/mount_management_section.tsx @@ -8,18 +8,21 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { Router, Switch, Route } from 'react-router-dom'; +import { Router, Switch, Route, Redirect, RouteChildrenProps } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; -import { StartServicesAccessor } from 'src/core/public'; -import { AdvancedSettings } from './advanced_settings'; +import { LocationDescriptor } from 'history'; +import { url } from '../../../kibana_utils/public'; import { ManagementAppMountParams } from '../../../management/public'; +import { UsageCollectionSetup } from '../../../usage_collection/public'; +import { StartServicesAccessor } from '../../../../core/public'; + +import { AdvancedSettings, QUERY } from './advanced_settings'; import { ComponentRegistry } from '../types'; import './index.scss'; -import { UsageCollectionSetup } from '../../../usage_collection/public'; const title = i18n.translate('advancedSettings.advancedSettingsLabel', { defaultMessage: 'Advanced Settings', @@ -36,6 +39,18 @@ const readOnlyBadge = { iconType: 'glasses', }; +const redirectUrl = ({ + match, + location, +}: RouteChildrenProps<{ [QUERY]: string }>): LocationDescriptor => { + const search = url.addQueryParam(location.search, QUERY, match?.params[QUERY]); + + return { + pathname: '/', + search, + }; +}; + export async function mountManagementSection( getStartServices: StartServicesAccessor, params: ManagementAppMountParams, @@ -56,8 +71,11 @@ export async function mountManagementSection( - + {/* TODO: remove route param (`query`) in 7.13 */} + {(props) => } + ( + createStreamingRequestHandler: < + Response, + P, + Q, + B, + Context extends RequestHandlerContext = RequestHandlerContext, + Method extends RouteMethod = any + >( streamHandler: StreamingRequestHandler - ) => RequestHandler; + ) => RequestHandler; } // eslint-disable-next-line diff --git a/src/plugins/data/README.mdx b/src/plugins/data/README.mdx index 2448d5f22ced..145aaa64fa3a 100644 --- a/src/plugins/data/README.mdx +++ b/src/plugins/data/README.mdx @@ -20,7 +20,7 @@ It is wired into the `TopNavMenu` component, but can be used independently. ### Fetch Query Suggestions -The `getQuerySuggestions` function helps to construct a query. +The `getQuerySuggestions` function helps to construct a query. KQL suggestion functions are registered in X-Pack, so this API does not return results in OSS. ```.ts @@ -37,7 +37,7 @@ KQL suggestion functions are registered in X-Pack, so this API does not return r ### Fetch Value Suggestions The `getValueSuggestions` function returns suggestions for field values. -This is helpful when you want to provide a user with options, for example when constructing a filter. +This is helpful when you want to provide a user with options, for example when constructing a filter. ```.ts @@ -56,7 +56,81 @@ Coming soon. ## Index Patterns -Coming soon. +The Index Patterns API provides a consistent method of structuring and formatting documents +and field lists across the various Kibana apps. Its typically used in conjunction with +SearchSource for composing queries. + +### Index Patterns API + +- Get list of index patterns +- Get default index pattern and examine fields +- Get index pattern by id +- Find index pattern by title +- Create index pattern +- Create index pattern and save it +- Modify index pattern and save it +- Delete index pattern + +#### Get list of index pattern titles and ids + +``` +const idsAndTitles = await data.indexPatterns.getIdsWithTitle(); +idsAndTitles.forEach(({id, title}) => console.log(`Index pattern id: ${id} title: ${title}`)); +``` + +#### Get default index pattern and examine fields + +``` +const defaultIndexPattern = await data.indexPatterns.getDefault(); +defaultIndexPattern.fields.forEach(({name}) => { console.log(name); }) +``` + +#### Get index pattern by id + +``` +const id = 'xxxxxx-xxx-xxxxxx'; +const indexPattern = await data.indexPatterns.get(id); +``` + +#### Find index pattern by title + +``` +const title = 'kibana-*'; +const [indexPattern] = await data.indexPatterns.find(title); +``` + +#### Create index pattern + +``` +const indexPattern = await data.indexPatterns.create({ title: 'kibana-*' }); +``` + +#### Create index pattern and save it immediately + +``` +const indexPattern = await data.indexPatterns.createAndSave({ title: 'kibana-*' }); +``` + +#### Create index pattern, modify, and save + +``` +const indexPattern = await data.indexPatterns.create({ title: 'kibana-*' }); +indexPattern.setFieldCustomLabel('customer_name', 'Customer Name'); +data.indexPatterns.createSavedObject(indexPattern); +``` + +#### Modify index pattern and save it + +``` +indexPattern.setFieldCustomLabel('customer_name', 'Customer Name'); +await data.indexPatterns.updateSavedObject(indexPattern); +``` + +#### Delete index pattern + +``` +await data.indexPatterns.delete(indexPatternId); +``` ### Index Patterns HTTP API @@ -79,7 +153,7 @@ Index patterns provide Rest-like HTTP CRUD+ API with the following endpoints: ### Index Patterns API -Index Patterns CURD API allows you to create, retrieve and delete index patterns. I also +Index Patterns REST API allows you to create, retrieve and delete index patterns. I also exposes an update endpoint which allows you to update specific fields without changing the rest of the index pattern object. @@ -146,7 +220,7 @@ The endpoint returns the created index pattern object. #### Fetch an index pattern by ID -Retrieve and index pattern by its ID. +Retrieve an index pattern by its ID. ``` GET /api/index_patterns/index_pattern/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx @@ -477,7 +551,7 @@ It contains sub-services for each of those configurations: // Constuct the query portion of the search request const query = data.query.getEsQuery(indexPattern); - + // Construct a request const request = { params: { @@ -522,7 +596,7 @@ The `SearchSource` API is a convenient way to construct and run an Elasticsearch #### Default Search Strategy One benefit of using the low-level search API, is partial response support in X-Pack, allowing for a better and more responsive user experience. -In OSS only the final result is returned. +In OSS only the final result is returned. ```.ts import { isCompleteResponse } from '../plugins/data/public'; @@ -538,8 +612,8 @@ In OSS only the final result is returned. } }, error: (e: Error) => { - // Show customized toast notifications. - // You may choose to handle errors differently if you prefer. + // Show customized toast notifications. + // You may choose to handle errors differently if you prefer. data.search.showError(e); }, }); diff --git a/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap b/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap index 3e09fa449a1a..4ef61ec0f255 100644 --- a/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap +++ b/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap @@ -57,9 +57,16 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": undefined, "lang": "lang", "name": "name", "readFromDocValues": false, + "runtimeField": Object { + "script": Object { + "source": "emit('hello world')", + }, + "type": "keyword", + }, "script": "script", "scripted": true, "searchable": true, diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts index bce75f993247..8a73abb3c7d8 100644 --- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts +++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts @@ -9,7 +9,7 @@ import { IndexPatternField } from './index_pattern_field'; import { IndexPattern } from '../index_patterns'; import { KBN_FIELD_TYPES, FieldFormat } from '../../../common'; -import { FieldSpec } from '../types'; +import { FieldSpec, RuntimeField } from '../types'; describe('Field', function () { function flatten(obj: Record) { @@ -42,6 +42,12 @@ describe('Field', function () { } as unknown) as IndexPattern, $$spec: ({} as unknown) as FieldSpec, conflictDescriptions: { a: ['b', 'c'], d: ['e'] }, + runtimeField: { + type: 'keyword' as RuntimeField['type'], + script: { + source: "emit('hello world')", + }, + }, }; it('the correct properties are writable', () => { diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts index 540563c3a8cf..ed6c4bd40d56 100644 --- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts +++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts @@ -6,9 +6,10 @@ * Public License, v 1. */ +import type { RuntimeField } from '../types'; import { KbnFieldType, getKbnFieldType } from '../../kbn_field_types'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; -import { IFieldType } from './types'; +import type { IFieldType } from './types'; import { FieldSpec, IndexPattern } from '../..'; import { shortenDottedString } from '../../utils'; @@ -35,6 +36,14 @@ export class IndexPatternField implements IFieldType { this.spec.count = count; } + public get runtimeField() { + return this.spec.runtimeField; + } + + public set runtimeField(runtimeField: RuntimeField | undefined) { + this.spec.runtimeField = runtimeField; + } + /** * Script field code */ @@ -117,6 +126,13 @@ export class IndexPatternField implements IFieldType { return this.spec.subType; } + /** + * Is the field part of the index mapping? + */ + public get isMapped() { + return this.spec.isMapped; + } + // not writable, not serialized public get sortable() { return ( @@ -181,6 +197,8 @@ export class IndexPatternField implements IFieldType { format: getFormatterForField ? getFormatterForField(this).toJSON() : undefined, customLabel: this.customLabel, shortDotsEnable: this.spec.shortDotsEnable, + runtimeField: this.runtimeField, + isMapped: this.isMapped, }; } } diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap index 76de2b2662bb..4aadddfad3b9 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap @@ -20,9 +20,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "@tags", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -44,9 +46,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "@timestamp", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -68,9 +72,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "_id", "readFromDocValues": false, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -92,9 +98,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "_source", "readFromDocValues": false, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -116,9 +124,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "_type", "readFromDocValues": false, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -140,9 +150,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "area", "readFromDocValues": false, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -164,9 +176,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "bytes", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -188,9 +202,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "custom_user_field", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -212,9 +228,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "extension", "readFromDocValues": false, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -236,9 +254,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "extension.keyword", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -264,9 +284,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "geo.coordinates", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -288,9 +310,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "geo.src", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -312,9 +336,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "hashed", "readFromDocValues": false, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -336,9 +362,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "ip", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -360,9 +388,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "machine.os", "readFromDocValues": false, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -384,9 +414,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "machine.os.raw", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -412,9 +444,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "non-filterable", "readFromDocValues": false, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": false, @@ -436,9 +470,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "non-sortable", "readFromDocValues": false, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": false, @@ -460,9 +496,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "phpmemory", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -484,9 +522,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "point", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -508,9 +548,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "request_body", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -518,6 +560,35 @@ Object { "subType": undefined, "type": "attachment", }, + "runtime_field": Object { + "aggregatable": false, + "conflictDescriptions": undefined, + "count": 0, + "customLabel": undefined, + "esTypes": undefined, + "format": Object { + "id": "number", + "params": Object { + "pattern": "$0,0.[00]", + }, + }, + "isMapped": undefined, + "lang": undefined, + "name": "runtime_field", + "readFromDocValues": false, + "runtimeField": Object { + "script": Object { + "source": "emit('hello world')", + }, + "type": "keyword", + }, + "script": undefined, + "scripted": false, + "searchable": false, + "shortDotsEnable": false, + "subType": undefined, + "type": undefined, + }, "script date": Object { "aggregatable": true, "conflictDescriptions": undefined, @@ -532,9 +603,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": false, "lang": "painless", "name": "script date", "readFromDocValues": false, + "runtimeField": undefined, "script": "1234", "scripted": true, "searchable": true, @@ -556,9 +629,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": false, "lang": "expression", "name": "script murmur3", "readFromDocValues": false, + "runtimeField": undefined, "script": "1234", "scripted": true, "searchable": true, @@ -580,9 +655,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": false, "lang": "expression", "name": "script number", "readFromDocValues": false, + "runtimeField": undefined, "script": "1234", "scripted": true, "searchable": true, @@ -604,9 +681,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": false, "lang": "expression", "name": "script string", "readFromDocValues": false, + "runtimeField": undefined, "script": "'i am a string'", "scripted": true, "searchable": true, @@ -628,9 +707,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "ssl", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -652,9 +733,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "time", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -676,9 +759,11 @@ Object { "pattern": "$0,0.[00]", }, }, + "isMapped": true, "lang": undefined, "name": "utc_time", "readFromDocValues": true, + "runtimeField": undefined, "script": undefined, "scripted": false, "searchable": true, @@ -689,6 +774,14 @@ Object { }, "id": "test-pattern", "intervalName": undefined, + "runtimeFieldMap": Object { + "runtime_field": Object { + "script": Object { + "source": "emit('hello world')", + }, + "type": "keyword", + }, + }, "sourceFilters": undefined, "timeFieldName": "timestamp", "title": "title", diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap index bad74430b896..d6da4adac81a 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap +++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_patterns.test.ts.snap @@ -10,6 +10,7 @@ Object { "fields": Object {}, "id": "id", "intervalName": undefined, + "runtimeFieldMap": Object {}, "sourceFilters": Array [ Object { "value": "item1", diff --git a/src/plugins/data/common/index_patterns/index_patterns/fixtures/logstash_fields.js b/src/plugins/data/common/index_patterns/index_patterns/fixtures/logstash_fields.js index 3e81b9234ee6..2bcb8df34cf0 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/fixtures/logstash_fields.js +++ b/src/plugins/data/common/index_patterns/index_patterns/fixtures/logstash_fields.js @@ -68,6 +68,7 @@ function stubbedLogstashFields() { lang, scripted, subType, + isMapped: !scripted, }; }); } diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts index bb7ed17f9e60..4f6e83460aec 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.test.ts @@ -18,9 +18,27 @@ import { IndexPatternField } from '../fields'; import { fieldFormatsMock } from '../../field_formats/mocks'; import { FieldFormat } from '../..'; +import { RuntimeField } from '../types'; class MockFieldFormatter {} +const runtimeFieldScript = { + type: 'keyword' as RuntimeField['type'], + script: { + source: "emit('hello world')", + }, +}; + +const runtimeFieldMap = { + runtime_field: runtimeFieldScript, +}; + +const runtimeField = { + name: 'runtime_field', + runtimeField: runtimeFieldScript, + scripted: false, +}; + fieldFormatsMock.getInstance = jest.fn().mockImplementation(() => new MockFieldFormatter()) as any; // helper function to create index patterns @@ -32,7 +50,15 @@ function create(id: string) { } = stubbedSavedObjectIndexPattern(id); return new IndexPattern({ - spec: { id, type, version, timeFieldName, fields, title }, + spec: { + id, + type, + version, + timeFieldName, + fields: { ...fields, runtime_field: runtimeField }, + title, + runtimeFieldMap, + }, fieldFormats: fieldFormatsMock, shortDotsEnable: false, metaFields: [], @@ -53,6 +79,10 @@ describe('IndexPattern', () => { expect(indexPattern).toHaveProperty('getNonScriptedFields'); expect(indexPattern).toHaveProperty('addScriptedField'); expect(indexPattern).toHaveProperty('removeScriptedField'); + expect(indexPattern).toHaveProperty('addScriptedField'); + expect(indexPattern).toHaveProperty('removeScriptedField'); + expect(indexPattern).toHaveProperty('addRuntimeField'); + expect(indexPattern).toHaveProperty('removeRuntimeField'); // properties expect(indexPattern).toHaveProperty('fields'); @@ -65,6 +95,7 @@ describe('IndexPattern', () => { expect(indexPattern.fields[0]).toHaveProperty('filterable'); expect(indexPattern.fields[0]).toHaveProperty('sortable'); expect(indexPattern.fields[0]).toHaveProperty('scripted'); + expect(indexPattern.fields[0]).toHaveProperty('isMapped'); }); }); @@ -98,6 +129,12 @@ describe('IndexPattern', () => { expect(docValueFieldNames).toContain('utc_time'); }); + test('should return runtimeField', () => { + expect(indexPattern.getComputedFields().runtimeFields).toEqual({ + runtime_field: runtimeFieldScript, + }); + }); + test('should request date field doc values in date_time format', () => { const { docvalueFields } = indexPattern.getComputedFields(); const timestampField = docvalueFields.find((field) => field.field === '@timestamp'); @@ -117,6 +154,7 @@ describe('IndexPattern', () => { const notScriptedNames = mockLogStashFields() .filter((item: IndexPatternField) => item.scripted === false) .map((item: IndexPatternField) => item.name); + notScriptedNames.push('runtime_field'); const respNames = map(indexPattern.getNonScriptedFields(), 'name'); expect(respNames).toEqual(notScriptedNames); @@ -185,6 +223,52 @@ describe('IndexPattern', () => { }); }); + describe('addRuntimeField and removeRuntimeField', () => { + const runtime = { + type: 'keyword' as RuntimeField['type'], + script: { + source: "emit('hello world');", + }, + }; + + beforeEach(() => { + const formatter = { + toJSON: () => ({ id: 'bytes' }), + } as FieldFormat; + indexPattern.getFormatterForField = () => formatter; + }); + + test('add and remove runtime field to existing field', () => { + indexPattern.addRuntimeField('@tags', runtime); + expect(indexPattern.toSpec().runtimeFieldMap).toEqual({ + '@tags': runtime, + runtime_field: runtimeField.runtimeField, + }); + expect(indexPattern.toSpec()!.fields!['@tags'].runtimeField).toEqual(runtime); + + indexPattern.removeRuntimeField('@tags'); + expect(indexPattern.toSpec().runtimeFieldMap).toEqual({ + runtime_field: runtimeField.runtimeField, + }); + expect(indexPattern.toSpec()!.fields!['@tags'].runtimeField).toBeUndefined(); + }); + + test('add and remove runtime field as new field', () => { + indexPattern.addRuntimeField('new_field', runtime); + expect(indexPattern.toSpec().runtimeFieldMap).toEqual({ + runtime_field: runtimeField.runtimeField, + new_field: runtime, + }); + expect(indexPattern.toSpec()!.fields!.new_field.runtimeField).toEqual(runtime); + + indexPattern.removeRuntimeField('new_field'); + expect(indexPattern.toSpec().runtimeFieldMap).toEqual({ + runtime_field: runtimeField.runtimeField, + }); + expect(indexPattern.toSpec()!.fields!.new_field).toBeUndefined(); + }); + }); + describe('getFormatterForField', () => { test('should return the default one for empty objects', () => { indexPattern.setFieldFormat('scriptedFieldWithEmptyFormatter', {}); diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts index ec665a80cbe0..144d38fe1590 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts @@ -8,6 +8,7 @@ import _, { each, reject } from 'lodash'; import { FieldAttrs, FieldAttrSet } from '../..'; +import type { RuntimeField } from '../types'; import { DuplicateField } from '../../../../kibana_utils/common'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES, IIndexPattern, IFieldType } from '../../../common'; @@ -17,6 +18,7 @@ import { flattenHitWrapper } from './flatten_hit'; import { FieldFormatsStartCommon, FieldFormat } from '../../field_formats'; import { IndexPatternSpec, TypeMeta, SourceFilter, IndexPatternFieldMap } from '../types'; import { SerializedFieldFormat } from '../../../../expressions/common'; +import { castEsToKbnFieldTypeName } from '../../kbn_field_types'; interface IndexPatternDeps { spec?: IndexPatternSpec; @@ -43,10 +45,20 @@ export class IndexPattern implements IIndexPattern { public id?: string; public title: string = ''; public fieldFormatMap: Record; + /** + * Only used by rollup indices, used by rollup specific endpoint to load field list + */ public typeMeta?: TypeMeta; public fields: IIndexPatternFieldList & { toSpec: () => IndexPatternFieldMap }; public timeFieldName: string | undefined; + /** + * @deprecated + * Deprecated. used by time range index patterns + */ public intervalName: string | undefined; + /** + * Type is used to identify rollup index patterns + */ public type: string | undefined; public formatHit: { (hit: Record, type?: string): any; @@ -55,14 +67,17 @@ export class IndexPattern implements IIndexPattern { public formatField: FormatFieldFn; public flattenHit: (hit: Record, deep?: boolean) => Record; public metaFields: string[]; - // savedObject version + /** + * SavedObject version + */ public version: string | undefined; public sourceFilters?: SourceFilter[]; private originalSavedObjectBody: SavedObjectBody = {}; private shortDotsEnable: boolean = false; private fieldFormats: FieldFormatsStartCommon; - // make private once manual field refresh is removed - public fieldAttrs: FieldAttrs; + private fieldAttrs: FieldAttrs; + private runtimeFieldMap: Record; + /** * prevents errors when index pattern exists before indices */ @@ -104,6 +119,7 @@ export class IndexPattern implements IIndexPattern { this.fieldAttrs = spec.fieldAttrs || {}; this.intervalName = spec.intervalName; this.allowNoIndex = spec.allowNoIndex || false; + this.runtimeFieldMap = spec.runtimeFieldMap || {}; } /** @@ -149,7 +165,8 @@ export class IndexPattern implements IIndexPattern { return { storedFields: ['*'], scriptFields, - docvalueFields: [], + docvalueFields: [] as Array<{ field: string; format: string }>, + runtimeFields: {}, }; } @@ -181,9 +198,13 @@ export class IndexPattern implements IIndexPattern { storedFields: ['*'], scriptFields, docvalueFields, + runtimeFields: this.runtimeFieldMap, }; } + /** + * Create static representation of index pattern + */ public toSpec(): IndexPatternSpec { return { id: this.id, @@ -196,6 +217,7 @@ export class IndexPattern implements IIndexPattern { typeMeta: this.typeMeta, type: this.type, fieldFormats: this.fieldFormatMap, + runtimeFieldMap: this.runtimeFieldMap, fieldAttrs: this.fieldAttrs, intervalName: this.intervalName, allowNoIndex: this.allowNoIndex, @@ -291,6 +313,7 @@ export class IndexPattern implements IIndexPattern { ? undefined : JSON.stringify(this.fieldFormatMap); const fieldAttrs = this.getFieldAttrs(); + const runtimeFieldMap = this.runtimeFieldMap; return { fieldAttrs: fieldAttrs ? JSON.stringify(fieldAttrs) : undefined, @@ -305,6 +328,7 @@ export class IndexPattern implements IIndexPattern { type: this.type, typeMeta: this.typeMeta ? JSON.stringify(this.typeMeta) : undefined, allowNoIndex: this.allowNoIndex ? this.allowNoIndex : undefined, + runtimeFieldMap: runtimeFieldMap ? JSON.stringify(runtimeFieldMap) : undefined, }; } @@ -326,6 +350,51 @@ export class IndexPattern implements IIndexPattern { ); } + /** + * Add a runtime field - Appended to existing mapped field or a new field is + * created as appropriate + * @param name Field name + * @param runtimeField Runtime field definition + */ + + addRuntimeField(name: string, runtimeField: RuntimeField) { + const existingField = this.getFieldByName(name); + if (existingField) { + existingField.runtimeField = runtimeField; + } else { + this.fields.add({ + name, + runtimeField, + type: castEsToKbnFieldTypeName(runtimeField.type), + aggregatable: true, + searchable: true, + count: 0, + readFromDocValues: false, + }); + } + this.runtimeFieldMap[name] = runtimeField; + } + + /** + * Remove a runtime field - removed from mapped field or removed unmapped + * field as appropriate + * @param name Field name + */ + + removeRuntimeField(name: string) { + const existingField = this.getFieldByName(name); + if (existingField) { + if (existingField.isMapped) { + // mapped field, remove runtimeField def + existingField.runtimeField = undefined; + } else { + // runtimeField only + this.fields.remove(existingField); + } + } + delete this.runtimeFieldMap[name]; + } + /** * Get formatter for a given field name. Return undefined if none exists * @param field diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts index 4d5e666a70c0..60436da530b6 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts @@ -11,6 +11,7 @@ import { PublicMethodsOf } from '@kbn/utility-types'; import { SavedObjectsClientCommon } from '../..'; import { createIndexPatternCache } from '.'; +import type { RuntimeField } from '../types'; import { IndexPattern } from './index_pattern'; import { createEnsureDefaultIndexPattern, @@ -34,6 +35,7 @@ import { SavedObjectNotFound } from '../../../../kibana_utils/common'; import { IndexPatternMissingIndices } from '../lib'; import { findByTitle } from '../utils'; import { DuplicateIndexPatternError } from '../errors'; +import { castEsToKbnFieldTypeName } from '../../kbn_field_types'; const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3; const savedObjectType = 'index-pattern'; @@ -127,6 +129,12 @@ export class IndexPatternsService { return this.savedObjectsCache.map((obj) => obj?.attributes?.title); }; + /** + * Find and load index patterns by title + * @param search + * @param size + * @returns IndexPattern[] + */ find = async (search: string, size: number = 10): Promise => { const savedObjects = await this.savedObjectsClient.find({ type: 'index-pattern', @@ -206,6 +214,7 @@ export class IndexPatternsService { /** * Get field list by providing { pattern } * @param options + * @returns FieldSpec[] */ getFieldsForWildcard = async (options: GetFieldsOptions) => { const metaFields = await this.config.get(UI_SETTINGS.META_FIELDS); @@ -221,6 +230,7 @@ export class IndexPatternsService { /** * Get field list by providing an index patttern (or spec) * @param options + * @returns FieldSpec[] */ getFieldsForIndexPattern = async ( indexPattern: IndexPattern | IndexPatternSpec, @@ -239,7 +249,8 @@ export class IndexPatternsService { */ refreshFields = async (indexPattern: IndexPattern) => { try { - const fields = await this.getFieldsForIndexPattern(indexPattern); + const fields = (await this.getFieldsForIndexPattern(indexPattern)) as FieldSpec[]; + fields.forEach((field) => (field.isMapped = true)); const scripted = indexPattern.getScriptedFields().map((field) => field.spec); const fieldAttrs = indexPattern.getFieldAttrs(); const fieldsWithSavedAttrs = Object.values( @@ -266,6 +277,7 @@ export class IndexPatternsService { * @param id * @param title * @param options + * @returns Record */ private refreshFieldSpecMap = async ( fields: IndexPatternFieldMap, @@ -279,6 +291,7 @@ export class IndexPatternsService { try { let updatedFieldList: FieldSpec[]; const newFields = (await this.getFieldsForWildcard(options)) as FieldSpec[]; + newFields.forEach((field) => (field.isMapped = true)); // If allowNoIndex, only update field list if field caps finds fields. To support // beats creating index pattern and dashboard before docs @@ -307,7 +320,9 @@ export class IndexPatternsService { /** * Converts field array to map - * @param fields + * @param fields: FieldSpec[] + * @param fieldAttrs: FieldAttrs + * @returns Record */ fieldArrayToMap = (fields: FieldSpec[], fieldAttrs?: FieldAttrs) => fields.reduce((collector, field) => { @@ -322,6 +337,7 @@ export class IndexPatternsService { /** * Converts index pattern saved object to index pattern spec * @param savedObject + * @returns IndexPatternSpec */ savedObjectToSpec = (savedObject: SavedObject): IndexPatternSpec => { @@ -335,6 +351,7 @@ export class IndexPatternsService { fields, sourceFilters, fieldFormatMap, + runtimeFieldMap, typeMeta, type, fieldAttrs, @@ -347,6 +364,9 @@ export class IndexPatternsService { const parsedFieldFormatMap = fieldFormatMap ? JSON.parse(fieldFormatMap) : {}; const parsedFields: FieldSpec[] = fields ? JSON.parse(fields) : []; const parsedFieldAttrs: FieldAttrs = fieldAttrs ? JSON.parse(fieldAttrs) : {}; + const parsedRuntimeFieldMap: Record = runtimeFieldMap + ? JSON.parse(runtimeFieldMap) + : {}; return { id, @@ -361,6 +381,7 @@ export class IndexPatternsService { fieldFormats: parsedFieldFormatMap, fieldAttrs: parsedFieldAttrs, allowNoIndex, + runtimeFieldMap: parsedRuntimeFieldMap, }; }; @@ -375,7 +396,7 @@ export class IndexPatternsService { } const spec = this.savedObjectToSpec(savedObject); - const { title, type, typeMeta } = spec; + const { title, type, typeMeta, runtimeFieldMap } = spec; spec.fieldAttrs = savedObject.attributes.fieldAttrs ? JSON.parse(savedObject.attributes.fieldAttrs) : {}; @@ -394,6 +415,22 @@ export class IndexPatternsService { }, spec.fieldAttrs ); + // APPLY RUNTIME FIELDS + for (const [key, value] of Object.entries(runtimeFieldMap || {})) { + if (spec.fields[key]) { + spec.fields[key].runtimeField = value; + } else { + spec.fields[key] = { + name: key, + type: castEsToKbnFieldTypeName(value.type), + runtimeField: value, + aggregatable: true, + searchable: true, + count: 0, + readFromDocValues: false, + }; + } + } } catch (err) { if (err instanceof IndexPatternMissingIndices) { this.onNotification({ @@ -442,6 +479,7 @@ export class IndexPatternsService { * Create a new index pattern instance * @param spec * @param skipFetchFields + * @returns IndexPattern */ async create(spec: IndexPatternSpec, skipFetchFields = false): Promise { const shortDotsEnable = await this.config.get(UI_SETTINGS.SHORT_DOTS_ENABLE); diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts index 77c251ada7b2..467b5125f032 100644 --- a/src/plugins/data/common/index_patterns/types.ts +++ b/src/plugins/data/common/index_patterns/types.ts @@ -14,20 +14,41 @@ import { SerializedFieldFormat } from '../../../expressions/common'; import { KBN_FIELD_TYPES, IndexPatternField, FieldFormat } from '..'; export type FieldFormatMap = Record; - +const RUNTIME_FIELD_TYPES = ['keyword', 'long', 'double', 'date', 'ip', 'boolean'] as const; +type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; +export interface RuntimeField { + type: RuntimeType; + script: { + source: string; + }; +} + +/** + * IIndexPattern allows for an IndexPattern OR an index pattern saved object + * too ambiguous, should be avoided + */ export interface IIndexPattern { fields: IFieldType[]; title: string; id?: string; + /** + * Type is used for identifying rollup indices, otherwise left undefined + */ type?: string; timeFieldName?: string; getTimeField?(): IFieldType | undefined; fieldFormatMap?: Record | undefined>; + /** + * Look up a formatter for a given field + */ getFormatterForField?: ( field: IndexPatternField | IndexPatternField['spec'] | IFieldType ) => FieldFormat; } +/** + * Interface for an index pattern saved object + */ export interface IndexPatternAttributes { type: string; fields: string; @@ -38,12 +59,17 @@ export interface IndexPatternAttributes { sourceFilters?: string; fieldFormatMap?: string; fieldAttrs?: string; + runtimeFieldMap?: string; /** * prevents errors when index pattern exists before indices */ allowNoIndex?: boolean; } +/** + * @intenal + * Storage of field attributes. Necessary since the field list isn't saved. + */ export interface FieldAttrs { [key: string]: FieldAttrSet; } @@ -153,9 +179,22 @@ export interface FieldSpecExportFmt { indexed?: boolean; } +/** + * Serialized version of IndexPatternField + */ export interface FieldSpec { + /** + * Popularity count is used by discover + */ count?: number; + /** + * Scripted field painless script + */ script?: string; + /** + * Scripted field langauge + * Painless is the only valid scripted field language + */ lang?: string; conflictDescriptions?: Record; format?: SerializedFieldFormat; @@ -169,16 +208,32 @@ export interface FieldSpec { subType?: IFieldSubType; indexed?: boolean; customLabel?: string; + runtimeField?: RuntimeField; // not persisted shortDotsEnable?: boolean; + isMapped?: boolean; } export type IndexPatternFieldMap = Record; +/** + * Static index pattern format + * Serialized data object, representing index pattern attributes and state + */ export interface IndexPatternSpec { + /** + * saved object id + */ id?: string; + /** + * saved object version string + */ version?: string; title?: string; + /** + * @deprecated + * Deprecated. Was used by time range based index patterns + */ intervalName?: string; timeFieldName?: string; sourceFilters?: SourceFilter[]; @@ -186,6 +241,7 @@ export interface IndexPatternSpec { typeMeta?: TypeMeta; type?: string; fieldFormats?: Record; + runtimeFieldMap?: Record; fieldAttrs?: FieldAttrs; allowNoIndex?: boolean; } diff --git a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts index 83fd22a618fe..3c1a89015252 100644 --- a/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts +++ b/src/plugins/data/common/search/aggs/buckets/lib/time_buckets/calc_auto_interval.ts @@ -6,75 +6,205 @@ * Public License, v 1. */ +import { i18n } from '@kbn/i18n'; import moment from 'moment'; -const boundsDescending = [ +export const boundsDescendingRaw = [ { bound: Infinity, - interval: Number(moment.duration(1, 'year')), + interval: moment.duration(1, 'year'), + boundLabel: i18n.translate('data.search.timeBuckets.infinityLabel', { + defaultMessage: 'More than a year', + }), + intervalLabel: i18n.translate('data.search.timeBuckets.yearLabel', { + defaultMessage: 'a year', + }), }, { - bound: Number(moment.duration(1, 'year')), - interval: Number(moment.duration(1, 'month')), + bound: moment.duration(1, 'year'), + interval: moment.duration(1, 'month'), + boundLabel: i18n.translate('data.search.timeBuckets.yearLabel', { + defaultMessage: 'a year', + }), + intervalLabel: i18n.translate('data.search.timeBuckets.monthLabel', { + defaultMessage: 'a month', + }), }, { - bound: Number(moment.duration(3, 'week')), - interval: Number(moment.duration(1, 'week')), + bound: moment.duration(3, 'week'), + interval: moment.duration(1, 'week'), + boundLabel: i18n.translate('data.search.timeBuckets.dayLabel', { + defaultMessage: '{amount, plural, one {a day} other {# days}}', + values: { amount: 21 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.dayLabel', { + defaultMessage: '{amount, plural, one {a day} other {# days}}', + values: { amount: 7 }, + }), }, { - bound: Number(moment.duration(1, 'week')), - interval: Number(moment.duration(1, 'd')), + bound: moment.duration(1, 'week'), + interval: moment.duration(1, 'd'), + boundLabel: i18n.translate('data.search.timeBuckets.dayLabel', { + defaultMessage: '{amount, plural, one {a day} other {# days}}', + values: { amount: 7 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.dayLabel', { + defaultMessage: '{amount, plural, one {a day} other {# days}}', + values: { amount: 1 }, + }), }, { - bound: Number(moment.duration(24, 'hour')), - interval: Number(moment.duration(12, 'hour')), + bound: moment.duration(24, 'hour'), + interval: moment.duration(12, 'hour'), + boundLabel: i18n.translate('data.search.timeBuckets.dayLabel', { + defaultMessage: '{amount, plural, one {a day} other {# days}}', + values: { amount: 1 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.hourLabel', { + defaultMessage: '{amount, plural, one {an hour} other {# hours}}', + values: { amount: 12 }, + }), }, { - bound: Number(moment.duration(6, 'hour')), - interval: Number(moment.duration(3, 'hour')), + bound: moment.duration(6, 'hour'), + interval: moment.duration(3, 'hour'), + boundLabel: i18n.translate('data.search.timeBuckets.hourLabel', { + defaultMessage: '{amount, plural, one {an hour} other {# hours}}', + values: { amount: 6 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.hourLabel', { + defaultMessage: '{amount, plural, one {an hour} other {# hours}}', + values: { amount: 3 }, + }), }, { - bound: Number(moment.duration(2, 'hour')), - interval: Number(moment.duration(1, 'hour')), + bound: moment.duration(2, 'hour'), + interval: moment.duration(1, 'hour'), + boundLabel: i18n.translate('data.search.timeBuckets.hourLabel', { + defaultMessage: '{amount, plural, one {an hour} other {# hours}}', + values: { amount: 2 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.hourLabel', { + defaultMessage: '{amount, plural, one {an hour} other {# hours}}', + values: { amount: 1 }, + }), }, { - bound: Number(moment.duration(45, 'minute')), - interval: Number(moment.duration(30, 'minute')), + bound: moment.duration(45, 'minute'), + interval: moment.duration(30, 'minute'), + boundLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 45 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 30 }, + }), }, { - bound: Number(moment.duration(20, 'minute')), - interval: Number(moment.duration(10, 'minute')), + bound: moment.duration(20, 'minute'), + interval: moment.duration(10, 'minute'), + boundLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 20 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 10 }, + }), }, { - bound: Number(moment.duration(9, 'minute')), - interval: Number(moment.duration(5, 'minute')), + bound: moment.duration(9, 'minute'), + interval: moment.duration(5, 'minute'), + boundLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 9 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 5 }, + }), }, { - bound: Number(moment.duration(3, 'minute')), - interval: Number(moment.duration(1, 'minute')), + bound: moment.duration(3, 'minute'), + interval: moment.duration(1, 'minute'), + boundLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 3 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.minuteLabel', { + defaultMessage: '{amount, plural, one {a minute} other {# minutes}}', + values: { amount: 1 }, + }), }, { - bound: Number(moment.duration(45, 'second')), - interval: Number(moment.duration(30, 'second')), + bound: moment.duration(45, 'second'), + interval: moment.duration(30, 'second'), + boundLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 45 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 30 }, + }), }, { - bound: Number(moment.duration(15, 'second')), - interval: Number(moment.duration(10, 'second')), + bound: moment.duration(15, 'second'), + interval: moment.duration(10, 'second'), + boundLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 15 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 10 }, + }), }, { - bound: Number(moment.duration(7.5, 'second')), - interval: Number(moment.duration(5, 'second')), + bound: moment.duration(7.5, 'second'), + interval: moment.duration(5, 'second'), + boundLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 7.5 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 5 }, + }), }, { - bound: Number(moment.duration(5, 'second')), - interval: Number(moment.duration(1, 'second')), + bound: moment.duration(5, 'second'), + interval: moment.duration(1, 'second'), + boundLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 5 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.secondLabel', { + defaultMessage: '{amount, plural, one {a second} other {# seconds}}', + values: { amount: 1 }, + }), }, { - bound: Number(moment.duration(500, 'ms')), - interval: Number(moment.duration(100, 'ms')), + bound: moment.duration(500, 'ms'), + interval: moment.duration(100, 'ms'), + boundLabel: i18n.translate('data.search.timeBuckets.millisecondLabel', { + defaultMessage: '{amount, plural, one {a millisecond} other {# milliseconds}}', + values: { amount: 500 }, + }), + intervalLabel: i18n.translate('data.search.timeBuckets.millisecondLabel', { + defaultMessage: '{amount, plural, one {a millisecond} other {# milliseconds}}', + values: { amount: 100 }, + }), }, ]; +const boundsDescending = boundsDescendingRaw.map(({ bound, interval }) => ({ + bound: Number(bound), + interval: Number(interval), +})); + function getPerBucketMs(count: number, duration: number) { const ms = duration / count; return isFinite(ms) ? ms : NaN; diff --git a/src/plugins/data/common/search/search_source/search_source.test.ts b/src/plugins/data/common/search/search_source/search_source.test.ts index 0b9c60e94a19..6d7654c6659f 100644 --- a/src/plugins/data/common/search/search_source/search_source.test.ts +++ b/src/plugins/data/common/search/search_source/search_source.test.ts @@ -20,6 +20,7 @@ const getComputedFields = () => ({ storedFields: [], scriptFields: {}, docvalueFields: [], + runtimeFields: {}, }); const mockSource = { excludes: ['foo-*'] }; @@ -37,6 +38,13 @@ const indexPattern2 = ({ getSourceFiltering: () => mockSource2, } as unknown) as IndexPattern; +const runtimeFieldDef = { + type: 'keyword', + script: { + source: "emit('hello world')", + }, +}; + describe('SearchSource', () => { let mockSearchMethod: any; let searchSourceDependencies: SearchSourceDependencies; @@ -82,12 +90,14 @@ describe('SearchSource', () => { describe('computed fields handling', () => { test('still provides computed fields when no fields are specified', async () => { + const runtimeFields = { runtime_field: runtimeFieldDef }; searchSource.setField('index', ({ ...indexPattern, getComputedFields: () => ({ storedFields: ['hello'], scriptFields: { world: {} }, docvalueFields: ['@timestamp'], + runtimeFields, }), } as unknown) as IndexPattern); @@ -95,6 +105,7 @@ describe('SearchSource', () => { expect(request.stored_fields).toEqual(['hello']); expect(request.script_fields).toEqual({ world: {} }); expect(request.fields).toEqual(['@timestamp']); + expect(request.runtime_mappings).toEqual(runtimeFields); }); test('never includes docvalue_fields', async () => { @@ -390,15 +401,23 @@ describe('SearchSource', () => { }); test('filters request when a specific list of fields is provided with fieldsFromSource', async () => { + const runtimeFields = { runtime_field: runtimeFieldDef, runtime_field_b: runtimeFieldDef }; searchSource.setField('index', ({ ...indexPattern, getComputedFields: () => ({ storedFields: ['*'], scriptFields: { hello: {}, world: {} }, docvalueFields: ['@timestamp', 'date'], + runtimeFields, }), } as unknown) as IndexPattern); - searchSource.setField('fieldsFromSource', ['hello', '@timestamp', 'foo-a', 'bar']); + searchSource.setField('fieldsFromSource', [ + 'hello', + '@timestamp', + 'foo-a', + 'bar', + 'runtime_field', + ]); const request = await searchSource.getSearchRequestBody(); expect(request._source).toEqual({ @@ -407,6 +426,7 @@ describe('SearchSource', () => { expect(request.fields).toEqual(['@timestamp']); expect(request.script_fields).toEqual({ hello: {} }); expect(request.stored_fields).toEqual(['@timestamp', 'bar']); + expect(request.runtime_mappings).toEqual({ runtime_field: runtimeFieldDef }); }); test('filters request when a specific list of fields is provided with fieldsFromSource or fields', async () => { diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index 0f0688c9fc11..554e8385881f 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -461,12 +461,13 @@ export class SearchSource { searchRequest.indexType = this.getIndexType(index); // get some special field types from the index pattern - const { docvalueFields, scriptFields, storedFields } = index + const { docvalueFields, scriptFields, storedFields, runtimeFields } = index ? index.getComputedFields() : { docvalueFields: [], scriptFields: {}, storedFields: ['*'], + runtimeFields: {}, }; const fieldListProvided = !!body.fields; @@ -481,6 +482,7 @@ export class SearchSource { ...scriptFields, }; body.stored_fields = storedFields; + body.runtime_mappings = runtimeFields || {}; // apply source filters from index pattern if specified by the user let filteredDocvalueFields = docvalueFields; @@ -518,13 +520,18 @@ export class SearchSource { body.script_fields, Object.keys(body.script_fields).filter((f) => uniqFieldNames.includes(f)) ); + body.runtime_mappings = pick( + body.runtime_mappings, + Object.keys(body.runtime_mappings).filter((f) => uniqFieldNames.includes(f)) + ); } // request the remaining fields from stored_fields just in case, since the // fields API does not handle stored fields - const remainingFields = difference(uniqFieldNames, Object.keys(body.script_fields)).filter( - Boolean - ); + const remainingFields = difference(uniqFieldNames, [ + ...Object.keys(body.script_fields), + ...Object.keys(body.runtime_mappings), + ]).filter(Boolean); // only include unique values body.stored_fields = [...new Set(remainingFields)]; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 9f0a5b64bde5..ff3e2ebc89a4 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -307,6 +307,7 @@ import { parseEsInterval, parseInterval, toAbsoluteDates, + boundsDescendingRaw, // expressions utils getRequestInspectorStats, getResponseInspectorStats, @@ -416,6 +417,7 @@ export const search = { siblingPipelineType, termsAggFilter, toAbsoluteDates, + boundsDescendingRaw, }, getRequestInspectorStats, getResponseInspectorStats, diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index dd24b1152b22..28997de4517e 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -24,6 +24,7 @@ import * as CSS from 'csstype'; import { Datatable as Datatable_2 } from 'src/plugins/expressions'; import { Datatable as Datatable_3 } from 'src/plugins/expressions/common'; import { DatatableColumn as DatatableColumn_2 } from 'src/plugins/expressions'; +import { DetailedPeerCertificate } from 'tls'; import { Ensure } from '@kbn/utility-types'; import { EnvironmentMode } from '@kbn/config'; import { ErrorToastOptions } from 'src/core/public/notifications'; @@ -45,6 +46,7 @@ import { History } from 'history'; import { Href } from 'history'; import { HttpSetup } from 'kibana/public'; import { IconType } from '@elastic/eui'; +import { IncomingHttpHeaders } from 'http'; import { InjectedIntl } from '@kbn/i18n/react'; import { ISearchOptions as ISearchOptions_2 } from 'src/plugins/data/public'; import { ISearchSource as ISearchSource_2 } from 'src/plugins/data/public'; @@ -60,9 +62,11 @@ import { METRIC_TYPE } from '@kbn/analytics'; import { Moment } from 'moment'; import moment from 'moment'; import { NameList } from 'elasticsearch'; +import { ObjectType } from '@kbn/config-schema'; import { Observable } from 'rxjs'; import { PackageInfo } from '@kbn/config'; import { Path } from 'history'; +import { PeerCertificate } from 'tls'; import { Plugin as Plugin_2 } from 'src/core/public'; import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core/public'; import { PluginInitializerContext as PluginInitializerContext_3 } from 'kibana/public'; @@ -75,6 +79,7 @@ import React from 'react'; import * as React_3 from 'react'; import { RecursiveReadonly } from '@kbn/utility-types'; import { Reporter } from '@kbn/analytics'; +import { Request as Request_2 } from '@hapi/hapi'; import { RequestAdapter } from 'src/plugins/inspector/common'; import { RequestStatistics as RequestStatistics_2 } from 'src/plugins/inspector/common'; import { Required } from '@kbn/utility-types'; @@ -85,6 +90,7 @@ import { SavedObjectReference } from 'src/core/types'; import { SavedObjectsClientContract } from 'src/core/public'; import { SavedObjectsFindOptions } from 'kibana/public'; import { SavedObjectsFindResponse } from 'kibana/server'; +import { SchemaTypeError } from '@kbn/config-schema'; import { Search } from '@elastic/elasticsearch/api/requestParams'; import { SearchResponse } from 'elasticsearch'; import { SerializedFieldFormat as SerializedFieldFormat_2 } from 'src/plugins/expressions/common'; @@ -94,12 +100,14 @@ import { ToastsSetup } from 'kibana/public'; import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport'; import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport'; import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; +import { Type } from '@kbn/config-schema'; import { TypeOf } from '@kbn/config-schema'; import { UiActionsSetup } from 'src/plugins/ui_actions/public'; import { UiActionsStart } from 'src/plugins/ui_actions/public'; import { UiCounterMetricType } from '@kbn/analytics'; import { Unit } from '@elastic/datemath'; import { UnregisterCallback } from 'history'; +import { URL } from 'url'; import { UserProvidedValues } from 'src/core/server/types'; // Warning: (ae-missing-release-tag) "ACTION_GLOBAL_APPLY_FILTER" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1176,7 +1184,7 @@ export interface IFieldType { // Warning: (ae-missing-release-tag) "IIndexPattern" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export interface IIndexPattern { // Warning: (ae-forgotten-export) The symbol "SerializedFieldFormat" needs to be exported by the entry point index.d.ts // @@ -1184,7 +1192,6 @@ export interface IIndexPattern { fieldFormatMap?: Record | undefined>; // (undocumented) fields: IFieldType[]; - // (undocumented) getFormatterForField?: (field: IndexPatternField | IndexPatternField['spec'] | IFieldType) => FieldFormat; // (undocumented) getTimeField?(): IFieldType | undefined; @@ -1194,7 +1201,6 @@ export interface IIndexPattern { timeFieldName?: string; // (undocumented) title: string; - // (undocumented) type?: string; } @@ -1259,14 +1265,11 @@ export type IMetricAggType = MetricAggType; export class IndexPattern implements IIndexPattern { // Warning: (ae-forgotten-export) The symbol "IndexPatternDeps" needs to be exported by the entry point index.d.ts constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); + addRuntimeField(name: string, runtimeField: RuntimeField): void; addScriptedField(name: string, script: string, fieldType?: string): Promise; readonly allowNoIndex: boolean; // (undocumented) readonly deleteFieldFormat: (fieldName: string) => void; - // Warning: (ae-forgotten-export) The symbol "FieldAttrs" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fieldAttrs: FieldAttrs; // (undocumented) fieldFormatMap: Record; // (undocumented) @@ -1302,6 +1305,7 @@ export class IndexPattern implements IIndexPattern { type: string | undefined; typeMeta: string | undefined; allowNoIndex: true | undefined; + runtimeFieldMap: string | undefined; }; // (undocumented) getComputedFields(): { @@ -1311,6 +1315,7 @@ export class IndexPattern implements IIndexPattern { field: any; format: string; }[]; + runtimeFields: Record; }; // (undocumented) getFieldAttrs: () => { @@ -1342,7 +1347,7 @@ export class IndexPattern implements IIndexPattern { getTimeField(): IndexPatternField | undefined; // (undocumented) id?: string; - // (undocumented) + // @deprecated (undocumented) intervalName: string | undefined; // (undocumented) isTimeBased(): boolean; @@ -1350,6 +1355,7 @@ export class IndexPattern implements IIndexPattern { isTimeNanosBased(): boolean; // (undocumented) metaFields: string[]; + removeRuntimeField(name: string): void; removeScriptedField(fieldName: string): void; resetOriginalSavedObjectBody: () => void; // (undocumented) @@ -1368,13 +1374,9 @@ export class IndexPattern implements IIndexPattern { timeFieldName: string | undefined; // (undocumented) title: string; - // (undocumented) toSpec(): IndexPatternSpec; - // (undocumented) type: string | undefined; - // (undocumented) typeMeta?: IndexPatternTypeMeta; - // (undocumented) version: string | undefined; } @@ -1392,7 +1394,7 @@ export type IndexPatternAggRestrictions = Record, 'isLo // Warning: (ae-missing-release-tag) "IndexPatternSpec" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export interface IndexPatternSpec { // (undocumented) allowNoIndex?: boolean; + // Warning: (ae-forgotten-export) The symbol "FieldAttrs" needs to be exported by the entry point index.d.ts + // // (undocumented) fieldAttrs?: FieldAttrs; // (undocumented) fieldFormats?: Record; // (undocumented) fields?: IndexPatternFieldMap; - // (undocumented) id?: string; - // (undocumented) + // @deprecated (undocumented) intervalName?: string; // (undocumented) + runtimeFieldMap?: Record; + // (undocumented) sourceFilters?: SourceFilter[]; // (undocumented) timeFieldName?: string; @@ -1547,7 +1558,6 @@ export interface IndexPatternSpec { type?: string; // (undocumented) typeMeta?: IndexPatternTypeMeta; - // (undocumented) version?: string; } @@ -1567,7 +1577,6 @@ export class IndexPatternsService { // (undocumented) ensureDefaultIndexPattern: EnsureDefaultIndexPattern; fieldArrayToMap: (fields: FieldSpec[], fieldAttrs?: FieldAttrs | undefined) => Record; - // (undocumented) find: (search: string, size?: number) => Promise; get: (id: string) => Promise; // Warning: (ae-forgotten-export) The symbol "IndexPatternSavedObjectAttrs" needs to be exported by the entry point index.d.ts @@ -2205,6 +2214,17 @@ export const search: { siblingPipelineType: string; termsAggFilter: string[]; toAbsoluteDates: typeof toAbsoluteDates; + boundsDescendingRaw: ({ + bound: number; + interval: import("moment").Duration; + boundLabel: string; + intervalLabel: string; + } | { + bound: import("moment").Duration; + interval: import("moment").Duration; + boundLabel: string; + intervalLabel: string; + })[]; }; getRequestInspectorStats: typeof getRequestInspectorStats; getResponseInspectorStats: typeof getResponseInspectorStats; @@ -2369,7 +2389,7 @@ export class SearchSource { removeField(field: K): this; serialize(): { searchSourceJSON: string; - references: import("src/core/server").SavedObjectReference[]; + references: import("../../../../../core/types").SavedObjectReference[]; }; setField(field: K, value: SearchSourceFields[K]): this; setFields(newFields: SearchSourceFields): this; @@ -2572,8 +2592,9 @@ export const UI_SETTINGS: { // src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrase_filter.ts:22:3 - (ae-forgotten-export) The symbol "PhraseFilterMeta" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/phrases_filter.ts:20:3 - (ae-forgotten-export) The symbol "PhrasesFilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:53:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:122:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:138:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:169:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/aggs/types.ts:139:51 - (ae-forgotten-export) The symbol "AggTypesRegistryStart" needs to be exported by the entry point index.d.ts // src/plugins/data/common/search/search_source/search_source.ts:186:7 - (ae-forgotten-export) The symbol "SearchFieldValue" needs to be exported by the entry point index.d.ts // src/plugins/data/public/field_formats/field_formats_service.ts:56:3 - (ae-forgotten-export) The symbol "FormatFactory" needs to be exported by the entry point index.d.ts @@ -2608,21 +2629,21 @@ export const UI_SETTINGS: { // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:398:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:413:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:417:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:418:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:421:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:422:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:425:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:413:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:414:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:418:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:419:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:422:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:423:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:426:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:34:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/search/session/session_service.ts:41:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/search/session/sessions_client.ts b/src/plugins/data/public/search/session/sessions_client.ts index f4ad2df530d1..5b0ba51c2f34 100644 --- a/src/plugins/data/public/search/session/sessions_client.ts +++ b/src/plugins/data/public/search/session/sessions_client.ts @@ -56,8 +56,8 @@ export class SessionsClient { }); } - public find(options: SavedObjectsFindOptions): Promise { - return this.http!.post(`/internal/session`, { + public find(options: Omit): Promise { + return this.http!.post(`/internal/session/_find`, { body: JSON.stringify(options), }); } diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 72eb0015215d..27af11674d06 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -235,6 +235,8 @@ export { SearchUsage, SessionService, ISessionService, + DataApiRequestHandlerContext, + DataRequestHandlerContext, } from './search'; // Search namespace diff --git a/src/plugins/data/server/index_patterns/routes/util/handle_errors.ts b/src/plugins/data/server/index_patterns/routes/util/handle_errors.ts index bede9165de08..9b4268cfd53c 100644 --- a/src/plugins/data/server/index_patterns/routes/util/handle_errors.ts +++ b/src/plugins/data/server/index_patterns/routes/util/handle_errors.ts @@ -6,7 +6,7 @@ * Public License, v 1. */ -import { RequestHandler, RouteMethod } from 'src/core/server'; +import type { RequestHandler, RouteMethod, RequestHandlerContext } from 'src/core/server'; import { ErrorIndexPatternNotFound } from '../../error'; interface ErrorResponseBody { @@ -29,9 +29,15 @@ interface ErrorWithData { * } * ``` */ -export const handleErrors = ( - handler: RequestHandler -): RequestHandler => async (context, request, response) => { +export const handleErrors = < + P, + Q, + B, + Context extends RequestHandlerContext, + Method extends RouteMethod +>( + handler: RequestHandler +): RequestHandler => async (context, request, response) => { try { return await handler(context, request, response); } catch (error) { diff --git a/src/plugins/data/server/search/routes/msearch.ts b/src/plugins/data/server/search/routes/msearch.ts index 5b62d6732430..70bb56afa440 100644 --- a/src/plugins/data/server/search/routes/msearch.ts +++ b/src/plugins/data/server/search/routes/msearch.ts @@ -8,12 +8,11 @@ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; import { SearchRouteDependencies } from '../search_service'; import { getCallMsearch } from './call_msearch'; import { reportServerError } from '../../../../kibana_utils/server'; - +import type { DataPluginRouter } from '../types'; /** * The msearch route takes in an array of searches, each consisting of header * and body json, and reformts them into a single request for the _msearch API. @@ -27,7 +26,10 @@ import { reportServerError } from '../../../../kibana_utils/server'; * * @deprecated */ -export function registerMsearchRoute(router: IRouter, deps: SearchRouteDependencies): void { +export function registerMsearchRoute( + router: DataPluginRouter, + deps: SearchRouteDependencies +): void { router.post( { path: '/internal/_msearch', diff --git a/src/plugins/data/server/search/routes/search.ts b/src/plugins/data/server/search/routes/search.ts index 5969dd132437..6d2da4c1e63d 100644 --- a/src/plugins/data/server/search/routes/search.ts +++ b/src/plugins/data/server/search/routes/search.ts @@ -8,12 +8,12 @@ import { first } from 'rxjs/operators'; import { schema } from '@kbn/config-schema'; -import type { IRouter } from 'src/core/server'; import { getRequestAbortedSignal } from '../../lib'; import { shimHitsTotal } from './shim_hits_total'; import { reportServerError } from '../../../../kibana_utils/server'; +import type { DataPluginRouter } from '../types'; -export function registerSearchRoute(router: IRouter): void { +export function registerSearchRoute(router: DataPluginRouter): void { router.post( { path: '/internal/search/{strategy}/{id?}', diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 762b1bffc7c1..f1a6fc09ee21 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -21,12 +21,13 @@ import { import { catchError, first, map } from 'rxjs/operators'; import { BfetchServerSetup } from 'src/plugins/bfetch/server'; import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; -import { +import type { ISearchSetup, ISearchStart, ISearchStrategy, SearchEnhancements, SearchStrategyDependencies, + DataRequestHandlerContext, } from './types'; import { AggsService } from './aggs'; @@ -64,12 +65,6 @@ import { ConfigSchema } from '../../config'; import { SessionService, IScopedSessionService, ISessionService } from './session'; import { KbnServerError } from '../../../kibana_utils/server'; -declare module 'src/core/server' { - interface RequestHandlerContext { - search?: ISearchClient & { session: IScopedSessionService }; - } -} - type StrategyMap = Record>; /** @internal */ @@ -112,7 +107,7 @@ export class SearchService implements Plugin { ): ISearchSetup { const usage = usageCollection ? usageProvider(core) : undefined; - const router = core.http.createRouter(); + const router = core.http.createRouter(); const routeDependencies = { getStartServices: core.getStartServices, globalConfig$: this.initializerContext.config.legacy.globalConfig$, @@ -124,11 +119,14 @@ export class SearchService implements Plugin { this.coreStart = coreStart; }); - core.http.registerRouteHandlerContext('search', async (context, request) => { - const search = this.asScopedProvider(this.coreStart!)(request); - const session = this.sessionService.asScopedProvider(this.coreStart!)(request); - return { ...search, session }; - }); + core.http.registerRouteHandlerContext( + 'search', + async (context, request) => { + const search = this.asScopedProvider(this.coreStart!)(request); + const session = this.sessionService.asScopedProvider(this.coreStart!)(request); + return { ...search, session }; + } + ); this.registerSearchStrategy( ES_SEARCH_STRATEGY, diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts index d292282f8a43..7e7d22fdb2be 100644 --- a/src/plugins/data/server/search/types.ts +++ b/src/plugins/data/server/search/types.ts @@ -7,11 +7,13 @@ */ import { Observable } from 'rxjs'; -import { +import type { + IRouter, IScopedClusterClient, IUiSettingsClient, SavedObjectsClientContract, KibanaRequest, + RequestHandlerContext, } from 'src/core/server'; import { ISearchOptions, @@ -23,7 +25,7 @@ import { import { AggsSetup, AggsStart } from './aggs'; import { SearchUsage } from './collectors'; import { IEsSearchRequest, IEsSearchResponse } from './es_search'; -import { ISessionService } from './session'; +import { ISessionService, IScopedSessionService } from './session'; export interface SearchEnhancements { defaultStrategy: string; @@ -101,3 +103,16 @@ export interface ISearchStart< asScoped: (request: KibanaRequest) => Promise; }; } + +export interface DataApiRequestHandlerContext extends ISearchClient { + session: IScopedSessionService; +} + +/** + * @internal + */ +export interface DataRequestHandlerContext extends RequestHandlerContext { + search: DataApiRequestHandlerContext; +} + +export type DataPluginRouter = IRouter; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index ccb40c1ea4b8..6a96fd8209a8 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -53,6 +53,7 @@ import { PluginInitializerContext as PluginInitializerContext_2 } from 'src/core import { PublicMethodsOf } from '@kbn/utility-types'; import { RecursiveReadonly } from '@kbn/utility-types'; import { RequestAdapter } from 'src/plugins/inspector/common'; +import { RequestHandlerContext } from 'src/core/server'; import { RequestStatistics } from 'src/plugins/inspector/common'; import { SavedObject } from 'src/core/server'; import { SavedObjectsClientContract } from 'src/core/server'; @@ -304,6 +305,23 @@ export const castEsToKbnFieldTypeName: (esType: ES_FIELD_TYPES | string) => KBN_ // @public (undocumented) export const config: PluginConfigDescriptor; +// Warning: (ae-forgotten-export) The symbol "ISearchClient" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "DataApiRequestHandlerContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface DataApiRequestHandlerContext extends ISearchClient { + // Warning: (ae-forgotten-export) The symbol "IScopedSessionService" needs to be exported by the entry point index.d.ts + // + // (undocumented) + session: IScopedSessionService; +} + +// @internal (undocumented) +export interface DataRequestHandlerContext extends RequestHandlerContext { + // (undocumented) + search: DataApiRequestHandlerContext; +} + // @public (undocumented) export enum ES_FIELD_TYPES { // (undocumented) @@ -687,14 +705,11 @@ export type IMetricAggType = MetricAggType; export class IndexPattern implements IIndexPattern { // Warning: (ae-forgotten-export) The symbol "IndexPatternDeps" needs to be exported by the entry point index.d.ts constructor({ spec, fieldFormats, shortDotsEnable, metaFields, }: IndexPatternDeps); + addRuntimeField(name: string, runtimeField: RuntimeField): void; addScriptedField(name: string, script: string, fieldType?: string): Promise; readonly allowNoIndex: boolean; // (undocumented) readonly deleteFieldFormat: (fieldName: string) => void; - // Warning: (ae-forgotten-export) The symbol "FieldAttrs" needs to be exported by the entry point index.d.ts - // - // (undocumented) - fieldAttrs: FieldAttrs; // (undocumented) fieldFormatMap: Record; // Warning: (ae-forgotten-export) The symbol "IIndexPatternFieldList" needs to be exported by the entry point index.d.ts @@ -732,6 +747,7 @@ export class IndexPattern implements IIndexPattern { type: string | undefined; typeMeta: string | undefined; allowNoIndex: true | undefined; + runtimeFieldMap: string | undefined; }; // (undocumented) getComputedFields(): { @@ -741,6 +757,7 @@ export class IndexPattern implements IIndexPattern { field: any; format: string; }[]; + runtimeFields: Record; }; // (undocumented) getFieldAttrs: () => { @@ -774,7 +791,7 @@ export class IndexPattern implements IIndexPattern { getTimeField(): IndexPatternField | undefined; // (undocumented) id?: string; - // (undocumented) + // @deprecated (undocumented) intervalName: string | undefined; // (undocumented) isTimeBased(): boolean; @@ -782,6 +799,7 @@ export class IndexPattern implements IIndexPattern { isTimeNanosBased(): boolean; // (undocumented) metaFields: string[]; + removeRuntimeField(name: string): void; removeScriptedField(fieldName: string): void; resetOriginalSavedObjectBody: () => void; // (undocumented) @@ -803,22 +821,16 @@ export class IndexPattern implements IIndexPattern { // (undocumented) title: string; // Warning: (ae-forgotten-export) The symbol "IndexPatternSpec" needs to be exported by the entry point index.d.ts - // - // (undocumented) toSpec(): IndexPatternSpec; - // (undocumented) type: string | undefined; // Warning: (ae-forgotten-export) The symbol "TypeMeta" needs to be exported by the entry point index.d.ts - // - // (undocumented) typeMeta?: TypeMeta; - // (undocumented) version: string | undefined; } // Warning: (ae-missing-release-tag) "IndexPatternAttributes" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export interface IndexPatternAttributes { allowNoIndex?: boolean; // (undocumented) @@ -830,6 +842,8 @@ export interface IndexPatternAttributes { // (undocumented) intervalName?: string; // (undocumented) + runtimeFieldMap?: string; + // (undocumented) sourceFilters?: string; // (undocumented) timeFieldName?: string; @@ -933,8 +947,6 @@ export interface ISearchStart ISearchClient; getSearchStrategy: (name?: string) => ISearchStrategy; @@ -960,8 +972,6 @@ export interface ISearchStrategy (request: KibanaRequest) => IScopedSessionService; } @@ -1125,10 +1135,10 @@ export class Plugin implements Plugin_2 Promise; + fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("src/core/server").ElasticsearchClient) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; }; search: ISearchStart>; }; @@ -1390,9 +1400,10 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // // src/plugins/data/common/es_query/filters/meta_filter.ts:42:3 - (ae-forgotten-export) The symbol "FilterState" needs to be exported by the entry point index.d.ts // src/plugins/data/common/es_query/filters/meta_filter.ts:43:3 - (ae-forgotten-export) The symbol "FilterMeta" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:47:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:53:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts -// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:122:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:52:45 - (ae-forgotten-export) The symbol "IndexPatternFieldMap" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:65:5 - (ae-forgotten-export) The symbol "FormatFieldFn" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:138:7 - (ae-forgotten-export) The symbol "FieldAttrSet" needs to be exported by the entry point index.d.ts +// src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts:169:7 - (ae-forgotten-export) The symbol "RuntimeField" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:29:23 - (ae-forgotten-export) The symbol "buildCustomFilter" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:29:23 - (ae-forgotten-export) The symbol "buildFilter" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:46:23 - (ae-forgotten-export) The symbol "datatableToCSV" needs to be exported by the entry point index.d.ts @@ -1415,23 +1426,23 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // src/plugins/data/server/index.ts:100:26 - (ae-forgotten-export) The symbol "TruncateFormat" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:126:27 - (ae-forgotten-export) The symbol "isFilterable" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index.ts:126:27 - (ae-forgotten-export) The symbol "isNestedField" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:241:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:243:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:244:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:253:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:254:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:255:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:259:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:260:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:264:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:267:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/index.ts:268:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:243:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:245:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:246:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:255:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:256:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:257:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:261:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:262:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:266:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:269:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/index.ts:270:1 - (ae-forgotten-export) The symbol "calcAutoIntervalLessThan" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index_patterns/index_patterns_service.ts:59:14 - (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts // src/plugins/data/server/plugin.ts:79:74 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/search/types.ts:101:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/search/types.ts:103:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts index f72d65dd2ee5..1394ceab1dd1 100644 --- a/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts +++ b/src/plugins/discover/public/application/helpers/get_sharing_data.test.ts @@ -49,6 +49,7 @@ describe('getSharingData', () => { "should": Array [], }, }, + "runtime_mappings": Object {}, "script_fields": Object {}, "sort": Array [ Object { diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md index f41d92dfb65a..2f9b43121b45 100644 --- a/src/plugins/embeddable/public/public.api.md +++ b/src/plugins/embeddable/public/public.api.md @@ -12,6 +12,7 @@ import { ApplicationStart as ApplicationStart_2 } from 'kibana/public'; import Boom from '@hapi/boom'; import { ConfigDeprecationProvider } from '@kbn/config'; import * as CSS from 'csstype'; +import { DetailedPeerCertificate } from 'tls'; import { EmbeddableStart as EmbeddableStart_2 } from 'src/plugins/embeddable/public/plugin'; import { EnvironmentMode } from '@kbn/config'; import { EuiBreadcrumb } from '@elastic/eui'; @@ -25,6 +26,7 @@ import { History } from 'history'; import { Href } from 'history'; import { I18nStart as I18nStart_2 } from 'src/core/public'; import { IconType } from '@elastic/eui'; +import { IncomingHttpHeaders } from 'http'; import { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import { Location } from 'history'; import { LocationDescriptorObject } from 'history'; @@ -32,30 +34,36 @@ import { Logger } from '@kbn/logging'; import { LogMeta } from '@kbn/logging'; import { MaybePromise } from '@kbn/utility-types'; import { NotificationsStart as NotificationsStart_2 } from 'src/core/public'; +import { ObjectType } from '@kbn/config-schema'; import { Observable } from 'rxjs'; import { Optional } from '@kbn/utility-types'; import { OverlayRef as OverlayRef_2 } from 'src/core/public'; import { OverlayStart as OverlayStart_2 } from 'src/core/public'; import { PackageInfo } from '@kbn/config'; import { Path } from 'history'; +import { PeerCertificate } from 'tls'; import { PluginInitializerContext } from 'src/core/public'; import * as PropTypes from 'prop-types'; import { PublicMethodsOf } from '@kbn/utility-types'; import { PublicUiSettingsParams } from 'src/core/server/types'; import React from 'react'; import { RecursiveReadonly } from '@kbn/utility-types'; +import { Request } from '@hapi/hapi'; import * as Rx from 'rxjs'; import { SavedObjectAttributes } from 'kibana/server'; import { SavedObjectAttributes as SavedObjectAttributes_2 } from 'src/core/public'; import { SavedObjectAttributes as SavedObjectAttributes_3 } from 'kibana/public'; +import { SchemaTypeError } from '@kbn/config-schema'; import { SimpleSavedObject as SimpleSavedObject_2 } from 'src/core/public'; import { Start as Start_2 } from 'src/plugins/inspector/public'; import { TransportRequestOptions } from '@elastic/elasticsearch/lib/Transport'; import { TransportRequestParams } from '@elastic/elasticsearch/lib/Transport'; import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; +import { Type } from '@kbn/config-schema'; import { TypeOf } from '@kbn/config-schema'; import { UiComponent } from 'src/plugins/kibana_utils/public'; import { UnregisterCallback } from 'history'; +import { URL } from 'url'; import { UserProvidedValues } from 'src/core/server/types'; // Warning: (ae-missing-release-tag) "ACTION_ADD_PANEL" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index 8e068818ec0c..0240ec90cb1e 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -29,6 +29,7 @@ import { getByAlias } from '../util/get_by_alias'; import { ExecutionContract } from './execution_contract'; import { ExpressionExecutionParams } from '../service'; import { TablesAdapter } from '../util/tables_adapter'; +import { ExpressionsInspectorAdapter } from '../util/expressions_inspector_adapter'; /** * AbortController is not available in Node until v15, so we @@ -63,6 +64,7 @@ export interface ExecutionParams { const createDefaultInspectorAdapters = (): DefaultInspectorAdapters => ({ requests: new RequestAdapter(), tables: new TablesAdapter(), + expression: new ExpressionsInspectorAdapter(), }); export class Execution< @@ -208,6 +210,9 @@ export class Execution< this.firstResultFuture.promise .then( (result) => { + if (this.context.inspectorAdapters.expression) { + this.context.inspectorAdapters.expression.logAST(this.state.get().ast); + } this.state.transitions.setResult(result); }, (error) => { diff --git a/examples/state_containers_examples/server/index.ts b/src/plugins/expressions/common/util/expressions_inspector_adapter.ts similarity index 52% rename from examples/state_containers_examples/server/index.ts rename to src/plugins/expressions/common/util/expressions_inspector_adapter.ts index 6ae5d2406671..c82884d373d2 100644 --- a/examples/state_containers_examples/server/index.ts +++ b/src/plugins/expressions/common/util/expressions_inspector_adapter.ts @@ -6,12 +6,17 @@ * Public License, v 1. */ -import { PluginInitializerContext } from '../../../src/core/server'; -import { StateDemoServerPlugin } from './plugin'; +import { EventEmitter } from 'events'; -export function plugin(initializerContext: PluginInitializerContext) { - return new StateDemoServerPlugin(initializerContext); -} +export class ExpressionsInspectorAdapter extends EventEmitter { + private _ast: any = {}; + + public logAST(ast: any): void { + this._ast = ast; + this.emit('change', this._ast); + } -export { StateDemoServerPlugin as Plugin }; -export * from '../common'; + public get ast() { + return this._ast; + } +} diff --git a/src/plugins/expressions/common/util/index.ts b/src/plugins/expressions/common/util/index.ts index ecb7d5cdca81..4762f9979fe4 100644 --- a/src/plugins/expressions/common/util/index.ts +++ b/src/plugins/expressions/common/util/index.ts @@ -9,3 +9,4 @@ export * from './create_error'; export * from './get_by_alias'; export * from './tables_adapter'; +export * from './expressions_inspector_adapter'; diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts index 9485daf49c98..d6dd2fc1f3d3 100644 --- a/src/plugins/expressions/public/index.ts +++ b/src/plugins/expressions/public/index.ts @@ -107,4 +107,5 @@ export { ExpressionsServiceSetup, ExpressionsServiceStart, TablesAdapter, + ExpressionsInspectorAdapter, } from '../common'; diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index 7fa0857be8ab..029d727e82e7 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -551,6 +551,16 @@ export class ExpressionRenderHandler { update$: Observable; } +// Warning: (ae-missing-release-tag) "ExpressionsInspectorAdapter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export class ExpressionsInspectorAdapter extends EventEmitter { + // (undocumented) + get ast(): any; + // (undocumented) + logAST(ast: any): void; +} + // Warning: (ae-missing-release-tag) "ExpressionsPublicPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/src/plugins/kibana_react/kibana.json b/src/plugins/kibana_react/kibana.json index c05490c34991..f2f0da53e628 100644 --- a/src/plugins/kibana_react/kibana.json +++ b/src/plugins/kibana_react/kibana.json @@ -2,6 +2,5 @@ "id": "kibanaReact", "version": "kibana", "ui": true, - "server": false, - "requiredBundles": ["kibanaUtils"] + "server": false } diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts index 4ec96f1db819..c99da5e9b36b 100644 --- a/src/plugins/kibana_react/public/index.ts +++ b/src/plugins/kibana_react/public/index.ts @@ -21,7 +21,6 @@ export { ValidatedDualRange, Value } from './validated_range'; export * from './notifications'; export { Markdown, MarkdownSimple } from './markdown'; export { reactToUiComponent, uiToReactComponent } from './adapters'; -export { useUrlTracker } from './use_url_tracker'; export { toMountPoint, MountPointPortal } from './util'; export { RedirectAppLinks } from './app_links'; diff --git a/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.test.tsx b/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.test.tsx deleted file mode 100644 index ed3eca04943a..000000000000 --- a/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.test.tsx +++ /dev/null @@ -1,59 +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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { renderHook } from '@testing-library/react-hooks'; -import { useUrlTracker } from './use_url_tracker'; -import { StubBrowserStorage } from '@kbn/test/jest'; -import { createMemoryHistory } from 'history'; - -describe('useUrlTracker', () => { - const key = 'key'; - let storage = new StubBrowserStorage(); - let history = createMemoryHistory(); - beforeEach(() => { - storage = new StubBrowserStorage(); - history = createMemoryHistory(); - }); - - it('should track history changes and save them to storage', () => { - expect(storage.getItem(key)).toBeNull(); - const { unmount } = renderHook(() => { - useUrlTracker(key, history, () => false, storage); - }); - expect(storage.getItem(key)).toBe('/'); - history.push('/change'); - expect(storage.getItem(key)).toBe('/change'); - unmount(); - history.push('/other-change'); - expect(storage.getItem(key)).toBe('/change'); - }); - - it('by default should restore initial url', () => { - storage.setItem(key, '/change'); - renderHook(() => { - useUrlTracker(key, history, undefined, storage); - }); - expect(history.location.pathname).toBe('/change'); - }); - - it('should restore initial url if shouldRestoreUrl cb returns true', () => { - storage.setItem(key, '/change'); - renderHook(() => { - useUrlTracker(key, history, () => true, storage); - }); - expect(history.location.pathname).toBe('/change'); - }); - - it('should not restore initial url if shouldRestoreUrl cb returns false', () => { - storage.setItem(key, '/change'); - renderHook(() => { - useUrlTracker(key, history, () => false, storage); - }); - expect(history.location.pathname).toBe('/'); - }); -}); diff --git a/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.tsx b/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.tsx deleted file mode 100644 index 5f3caf03ae44..000000000000 --- a/src/plugins/kibana_react/public/use_url_tracker/use_url_tracker.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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { History } from 'history'; -import { useLayoutEffect } from 'react'; -import { createUrlTracker } from '../../../kibana_utils/public/'; - -/** - * State management url_tracker in react hook form - * - * Replicates what src/legacy/ui/public/chrome/api/nav.ts did - * Persists the url in sessionStorage so it could be restored if navigated back to the app - * - * @param key - key to use in storage - * @param history - history instance to use - * @param shouldRestoreUrl - cb if url should be restored - * @param storage - storage to use. window.sessionStorage is default - */ -export function useUrlTracker( - key: string, - history: History, - shouldRestoreUrl: (urlToRestore: string) => boolean = () => true, - storage: Storage = sessionStorage -) { - useLayoutEffect(() => { - const urlTracker = createUrlTracker(key, storage); - const urlToRestore = urlTracker.getTrackedUrl(); - if (urlToRestore && shouldRestoreUrl(urlToRestore)) { - history.replace(urlToRestore); - } - const stopTrackingUrl = urlTracker.startTrackingUrl(history); - return () => { - stopTrackingUrl(); - }; - }, [key, history]); -} diff --git a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts index 1bc4c70e7706..9b83cdd69a54 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/core/core_usage_collector.ts @@ -154,6 +154,13 @@ export function getCoreUsageCollector( 'apiCalls.savedObjectsGet.namespace.custom.total': { type: 'long' }, 'apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.yes': { type: 'long' }, 'apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.no': { type: 'long' }, + 'apiCalls.savedObjectsResolve.total': { type: 'long' }, + 'apiCalls.savedObjectsResolve.namespace.default.total': { type: 'long' }, + 'apiCalls.savedObjectsResolve.namespace.default.kibanaRequest.yes': { type: 'long' }, + 'apiCalls.savedObjectsResolve.namespace.default.kibanaRequest.no': { type: 'long' }, + 'apiCalls.savedObjectsResolve.namespace.custom.total': { type: 'long' }, + 'apiCalls.savedObjectsResolve.namespace.custom.kibanaRequest.yes': { type: 'long' }, + 'apiCalls.savedObjectsResolve.namespace.custom.kibanaRequest.no': { type: 'long' }, 'apiCalls.savedObjectsUpdate.total': { type: 'long' }, 'apiCalls.savedObjectsUpdate.namespace.default.total': { type: 'long' }, 'apiCalls.savedObjectsUpdate.namespace.default.kibanaRequest.yes': { type: 'long' }, diff --git a/src/plugins/kibana_utils/common/url/encode_uri_query.test.ts b/src/plugins/kibana_utils/common/url/encode_uri_query.test.ts index ad5f620736ab..467143e4cf22 100644 --- a/src/plugins/kibana_utils/common/url/encode_uri_query.test.ts +++ b/src/plugins/kibana_utils/common/url/encode_uri_query.test.ts @@ -6,7 +6,7 @@ * Public License, v 1. */ -import { encodeUriQuery, encodeQuery } from './encode_uri_query'; +import { encodeUriQuery, encodeQuery, addQueryParam } from './encode_uri_query'; describe('encodeUriQuery', () => { test('should correctly encode uri query and not encode chars defined as pchar set in rfc3986', () => { @@ -57,3 +57,78 @@ describe('encodeQuery', () => { }); }); }); + +describe('addQueryParam', () => { + const sampleParams = '?myNumber=23&myString=test&myValue=&myBoolean=false'; + + describe('setting values', () => { + it('should perserve other values', () => { + expect(addQueryParam(sampleParams, 'myNewValue', 'test')).toEqual( + 'myBoolean=false&myNumber=23&myString=test&myValue=&myNewValue=test' + ); + }); + + it('should set boolean values', () => { + expect(addQueryParam('', 'myBoolean', 'false')).toEqual('myBoolean=false'); + expect(addQueryParam('', 'myBoolean', 'true')).toEqual('myBoolean=true'); + }); + + it('should set string values', () => { + expect(addQueryParam('', 'myString', 'test')).toEqual('myString=test'); + expect(addQueryParam('', 'myString', '')).toEqual('myString='); + }); + + it('should set number values', () => { + expect(addQueryParam('', 'myNumber', '23')).toEqual('myNumber=23'); + expect(addQueryParam('', 'myNumber', '0')).toEqual('myNumber=0'); + }); + }); + + describe('changing values', () => { + it('should perserve other values', () => { + expect(addQueryParam(sampleParams, 'myBoolean', 'true')).toEqual( + 'myBoolean=true&myNumber=23&myString=test&myValue=' + ); + }); + + it('should change boolean value', () => { + expect(addQueryParam('?myBoolean=true', 'myBoolean', 'false')).toEqual('myBoolean=false'); + expect(addQueryParam('?myBoolean=false', 'myBoolean', 'true')).toEqual('myBoolean=true'); + }); + + it('should change string values', () => { + expect(addQueryParam('?myString=initial', 'myString', 'test')).toEqual('myString=test'); + expect(addQueryParam('?myString=initial', 'myString', '')).toEqual('myString='); + }); + + it('should change number values', () => { + expect(addQueryParam('?myNumber=1', 'myNumber', '23')).toEqual('myNumber=23'); + expect(addQueryParam('?myNumber=1', 'myNumber', '0')).toEqual('myNumber=0'); + }); + }); + + describe('deleting values', () => { + it('should perserve other values', () => { + expect(addQueryParam(sampleParams, 'myNumber')).toEqual( + 'myBoolean=false&myString=test&myValue=' + ); + }); + + it('should delete empty values', () => { + expect(addQueryParam('?myValue=', 'myValue')).toEqual(''); + }); + + it('should delete boolean values', () => { + expect(addQueryParam('?myBoolean=false', 'myBoolean')).toEqual(''); + expect(addQueryParam('?myBoolean=true', 'myBoolean')).toEqual(''); + }); + + it('should delete string values', () => { + expect(addQueryParam('?myString=test', 'myString')).toEqual(''); + }); + + it('should delete number values', () => { + expect(addQueryParam('?myNumber=23', 'myNumber')).toEqual(''); + }); + }); +}); diff --git a/src/plugins/kibana_utils/common/url/encode_uri_query.ts b/src/plugins/kibana_utils/common/url/encode_uri_query.ts index ebc267c35222..c5fae36b1345 100644 --- a/src/plugins/kibana_utils/common/url/encode_uri_query.ts +++ b/src/plugins/kibana_utils/common/url/encode_uri_query.ts @@ -6,7 +6,7 @@ * Public License, v 1. */ -import { ParsedQuery } from 'query-string'; +import { ParsedQuery, parse, stringify } from 'query-string'; import { transform } from 'lodash'; /** @@ -32,15 +32,38 @@ export function encodeUriQuery(val: string, pctEncodeSpaces = false) { export const encodeQuery = ( query: ParsedQuery, - encodeFunction: (val: string, pctEncodeSpaces?: boolean) => string = encodeUriQuery -) => - transform(query, (result: any, value, key) => { + encodeFunction: (val: string, pctEncodeSpaces?: boolean) => string = encodeUriQuery, + pctEncodeSpaces = true +): ParsedQuery => + transform(query, (result, value, key) => { if (key) { const singleValue = Array.isArray(value) ? value.join(',') : value; result[key] = encodeFunction( singleValue === undefined || singleValue === null ? '' : singleValue, - true + pctEncodeSpaces ); } }); + +/** + * Method to help modify url query params. + * + * @param params + * @param key + * @param value + */ +export const addQueryParam = (params: string, key: string, value?: string) => { + const queryParams = parse(params); + + if (value !== undefined) { + queryParams[key] = value; + } else { + delete queryParams[key]; + } + + return stringify(encodeQuery(queryParams, undefined, false), { + sort: false, + encode: false, + }); +}; diff --git a/src/plugins/kibana_utils/common/url/index.ts b/src/plugins/kibana_utils/common/url/index.ts index b2705a45fc4d..fffef45dbe89 100644 --- a/src/plugins/kibana_utils/common/url/index.ts +++ b/src/plugins/kibana_utils/common/url/index.ts @@ -6,9 +6,10 @@ * Public License, v 1. */ -import { encodeUriQuery, encodeQuery } from './encode_uri_query'; +import { encodeUriQuery, encodeQuery, addQueryParam } from './encode_uri_query'; export const url = { encodeQuery, encodeUriQuery, + addQueryParam, }; diff --git a/src/plugins/saved_objects_management/public/lib/import_file.ts b/src/plugins/saved_objects_management/public/lib/import_file.ts index c5b3a9dc2995..a2f63571cbde 100644 --- a/src/plugins/saved_objects_management/public/lib/import_file.ts +++ b/src/plugins/saved_objects_management/public/lib/import_file.ts @@ -6,15 +6,9 @@ * Public License, v 1. */ -import { HttpStart, SavedObjectsImportFailure } from 'src/core/public'; +import { HttpStart, SavedObjectsImportResponse } from 'src/core/public'; import { ImportMode } from '../management_section/objects_table/components/import_mode_control'; -interface ImportResponse { - success: boolean; - successCount: number; - errors?: SavedObjectsImportFailure[]; -} - export async function importFile( http: HttpStart, file: File, @@ -23,7 +17,7 @@ export async function importFile( const formData = new FormData(); formData.append('file', file); const query = createNewCopies ? { createNewCopies } : { overwrite }; - return await http.post('/api/saved_objects/_import', { + return await http.post('/api/saved_objects/_import', { body: formData, headers: { // Important to be undefined, it forces proper headers to be set for FormData diff --git a/src/plugins/saved_objects_management/public/lib/process_import_response.test.ts b/src/plugins/saved_objects_management/public/lib/process_import_response.test.ts index 5d467883448b..a8f509ec4223 100644 --- a/src/plugins/saved_objects_management/public/lib/process_import_response.test.ts +++ b/src/plugins/saved_objects_management/public/lib/process_import_response.test.ts @@ -11,14 +11,16 @@ import { SavedObjectsImportAmbiguousConflictError, SavedObjectsImportUnknownError, SavedObjectsImportMissingReferencesError, + SavedObjectsImportResponse, } from 'src/core/public'; import { processImportResponse } from './process_import_response'; describe('processImportResponse()', () => { test('works when no errors exist in the response', () => { - const response = { + const response: SavedObjectsImportResponse = { success: true, successCount: 0, + warnings: [], }; const result = processImportResponse(response); expect(result.status).toBe('success'); @@ -26,7 +28,7 @@ describe('processImportResponse()', () => { }); test('conflict errors get added to failedImports and result in idle status', () => { - const response = { + const response: SavedObjectsImportResponse = { success: false, successCount: 0, errors: [ @@ -39,6 +41,7 @@ describe('processImportResponse()', () => { meta: {}, }, ], + warnings: [], }; const result = processImportResponse(response); expect(result.failedImports).toMatchInlineSnapshot(` @@ -59,7 +62,7 @@ describe('processImportResponse()', () => { }); test('ambiguous conflict errors get added to failedImports and result in idle status', () => { - const response = { + const response: SavedObjectsImportResponse = { success: false, successCount: 0, errors: [ @@ -72,6 +75,7 @@ describe('processImportResponse()', () => { meta: {}, }, ], + warnings: [], }; const result = processImportResponse(response); expect(result.failedImports).toMatchInlineSnapshot(` @@ -92,7 +96,7 @@ describe('processImportResponse()', () => { }); test('unknown errors get added to failedImports and result in success status', () => { - const response = { + const response: SavedObjectsImportResponse = { success: false, successCount: 0, errors: [ @@ -105,6 +109,7 @@ describe('processImportResponse()', () => { meta: {}, }, ], + warnings: [], }; const result = processImportResponse(response); expect(result.failedImports).toMatchInlineSnapshot(` @@ -125,7 +130,7 @@ describe('processImportResponse()', () => { }); test('missing references get added to failedImports and result in idle status', () => { - const response = { + const response: SavedObjectsImportResponse = { success: false, successCount: 0, errors: [ @@ -144,6 +149,7 @@ describe('processImportResponse()', () => { meta: {}, }, ], + warnings: [], }; const result = processImportResponse(response); expect(result.failedImports).toMatchInlineSnapshot(` @@ -170,7 +176,7 @@ describe('processImportResponse()', () => { }); test('missing references get added to unmatchedReferences, but are not duplicated', () => { - const response = { + const response: SavedObjectsImportResponse = { success: false, successCount: 0, errors: [ @@ -188,6 +194,7 @@ describe('processImportResponse()', () => { meta: {}, }, ], + warnings: [], }; const result = processImportResponse(response); expect(result.unmatchedReferences).toEqual([ @@ -197,10 +204,11 @@ describe('processImportResponse()', () => { }); test('success results get added to successfulImports and result in success status', () => { - const response = { + const response: SavedObjectsImportResponse = { success: true, successCount: 1, successResults: [{ type: 'a', id: '1', meta: {} }], + warnings: [], }; const result = processImportResponse(response); expect(result.successfulImports).toMatchInlineSnapshot(` @@ -214,4 +222,22 @@ describe('processImportResponse()', () => { `); expect(result.status).toBe('success'); }); + + test('warnings from the response get returned', () => { + const response: SavedObjectsImportResponse = { + success: true, + successCount: 1, + successResults: [{ type: 'a', id: '1', meta: {} }], + warnings: [ + { + type: 'action_required', + message: 'foo', + actionPath: '/somewhere', + }, + ], + }; + const result = processImportResponse(response); + expect(result.status).toBe('success'); + expect(result.importWarnings).toEqual(response.warnings); + }); }); diff --git a/src/plugins/saved_objects_management/public/lib/process_import_response.ts b/src/plugins/saved_objects_management/public/lib/process_import_response.ts index eaee81b89299..f6f216602ab9 100644 --- a/src/plugins/saved_objects_management/public/lib/process_import_response.ts +++ b/src/plugins/saved_objects_management/public/lib/process_import_response.ts @@ -15,6 +15,7 @@ import { SavedObjectsImportUnknownError, SavedObjectsImportFailure, SavedObjectsImportSuccess, + SavedObjectsImportWarning, } from 'src/core/public'; export interface FailedImport { @@ -41,6 +42,7 @@ export interface ProcessedImportResponse { importCount: number; conflictedSavedObjectsLinkedToSavedSearches: undefined; conflictedSearchDocs: undefined; + importWarnings: SavedObjectsImportWarning[]; } const isAnyConflict = ({ type }: FailedImport['error']) => @@ -87,5 +89,6 @@ export function processImportResponse( importCount: response.successCount, conflictedSavedObjectsLinkedToSavedSearches: undefined, conflictedSearchDocs: undefined, + importWarnings: response.warnings, }; } diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap index a48965cf7f41..a68e8891b5ad 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap @@ -226,6 +226,7 @@ exports[`Flyout conflicts should allow conflict resolution 2`] = ` "createNewCopies": false, "overwrite": true, }, + "importWarnings": undefined, "indexPatterns": Array [ Object { "id": "1", @@ -668,7 +669,18 @@ exports[`Flyout should render import step 1`] = ` exports[`Flyout summary should display summary when import is complete 1`] = ` `; diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx index a93502c2605c..0d8aa973bf5e 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx @@ -18,7 +18,7 @@ import { import React from 'react'; import { shallowWithI18nProvider } from '@kbn/test/jest'; -import { coreMock } from '../../../../../../core/public/mocks'; +import { coreMock, httpServiceMock } from '../../../../../../core/public/mocks'; import { serviceRegistryMock } from '../../../services/service_registry.mock'; import { Flyout, FlyoutProps, FlyoutState } from './flyout'; import { ShallowWrapper } from 'enzyme'; @@ -47,6 +47,7 @@ describe('Flyout', () => { beforeEach(() => { const { http, overlays } = coreMock.createStart(); const search = dataPluginMock.createStartContract().search; + const basePath = httpServiceMock.createBasePath(); defaultProps = { close: jest.fn(), @@ -63,6 +64,7 @@ describe('Flyout', () => { allowedTypes: ['search', 'index-pattern', 'visualization'], serviceRegistry: serviceRegistryMock.create(), search, + basePath, }; }); diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx index c8250863ab41..39a4529d1c23 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx @@ -31,7 +31,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { OverlayStart, HttpStart } from 'src/core/public'; +import { OverlayStart, HttpStart, IBasePath } from 'src/core/public'; import { IndexPatternsContract, IIndexPattern, @@ -69,6 +69,7 @@ export interface FlyoutProps { indexPatterns: IndexPatternsContract; overlays: OverlayStart; http: HttpStart; + basePath: IBasePath; search: DataPublicPluginStart['search']; } @@ -81,6 +82,7 @@ export interface FlyoutState { failedImports?: ProcessedImportResponse['failedImports']; successfulImports?: ProcessedImportResponse['successfulImports']; conflictingRecord?: ConflictingRecord; + importWarnings?: ProcessedImportResponse['importWarnings']; error?: string; file?: File; importCount: number; @@ -616,6 +618,7 @@ export class Flyout extends Component { successfulImports = [], isLegacyFile, importMode, + importWarnings, } = this.state; if (status === 'loading') { @@ -632,8 +635,15 @@ export class Flyout extends Component { ); } - if (isLegacyFile === false && status === 'success') { - return ; + if (!isLegacyFile && status === 'success') { + return ( + + ); } // Import summary for failed legacy import diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.test.tsx index 7a7f7b2daa1a..6cfd6f7fc57d 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.test.tsx @@ -7,8 +7,9 @@ */ import React from 'react'; -import { ShallowWrapper } from 'enzyme'; -import { shallowWithI18nProvider } from '@kbn/test/jest'; +import { ReactWrapper } from 'enzyme'; +import { mountWithI18nProvider } from '@kbn/test/jest'; +import { httpServiceMock } from '../../../../../../core/public/mocks'; import { ImportSummary, ImportSummaryProps } from './import_summary'; import { FailedImport } from '../../../lib'; @@ -16,6 +17,20 @@ import { FailedImport } from '../../../lib'; import { findTestSubject } from '@elastic/eui/lib/test'; describe('ImportSummary', () => { + let basePath: ReturnType; + + const getProps = (parts: Partial): ImportSummaryProps => ({ + basePath, + failedImports: [], + successfulImports: [], + importWarnings: [], + ...parts, + }); + + beforeEach(() => { + basePath = httpServiceMock.createBasePath(); + }); + const errorUnsupportedType: FailedImport = { obj: { type: 'error-obj-type', id: 'error-obj-id', meta: { title: 'Error object' } }, error: { type: 'unsupported_type' }, @@ -28,19 +43,20 @@ describe('ImportSummary', () => { overwrite: true, }; - const findHeader = (wrapper: ShallowWrapper) => wrapper.find('h3'); - const findCountCreated = (wrapper: ShallowWrapper) => + const findHeader = (wrapper: ReactWrapper) => wrapper.find('h3'); + const findCountCreated = (wrapper: ReactWrapper) => wrapper.find('h4.savedObjectsManagementImportSummary__createdCount'); - const findCountOverwritten = (wrapper: ShallowWrapper) => + const findCountOverwritten = (wrapper: ReactWrapper) => wrapper.find('h4.savedObjectsManagementImportSummary__overwrittenCount'); - const findCountError = (wrapper: ShallowWrapper) => + const findCountError = (wrapper: ReactWrapper) => wrapper.find('h4.savedObjectsManagementImportSummary__errorCount'); - const findObjectRow = (wrapper: ShallowWrapper) => - wrapper.find('.savedObjectsManagementImportSummary__row'); + const findObjectRow = (wrapper: ReactWrapper) => + wrapper.find('.savedObjectsManagementImportSummary__row').hostNodes(); + const findWarnings = (wrapper: ReactWrapper) => wrapper.find('ImportWarning'); it('should render as expected with no results', async () => { - const props: ImportSummaryProps = { failedImports: [], successfulImports: [] }; - const wrapper = shallowWithI18nProvider(); + const props = getProps({ failedImports: [], successfulImports: [] }); + const wrapper = mountWithI18nProvider(); expect(findHeader(wrapper).childAt(0).props()).toEqual( expect.objectContaining({ values: { importCount: 0 } }) @@ -52,14 +68,14 @@ describe('ImportSummary', () => { }); it('should render as expected with a newly created object', async () => { - const props: ImportSummaryProps = { + const props = getProps({ failedImports: [], successfulImports: [successNew], - }; - const wrapper = shallowWithI18nProvider(); + }); + const wrapper = mountWithI18nProvider(); expect(findHeader(wrapper).childAt(0).props()).toEqual( - expect.not.objectContaining({ values: expect.anything() }) // no importCount for singular + expect.objectContaining({ values: { importCount: 1 } }) ); const countCreated = findCountCreated(wrapper); expect(countCreated).toHaveLength(1); @@ -68,18 +84,19 @@ describe('ImportSummary', () => { ); expect(findCountOverwritten(wrapper)).toHaveLength(0); expect(findCountError(wrapper)).toHaveLength(0); + expect(findObjectRow(wrapper)).toHaveLength(1); }); it('should render as expected with an overwritten object', async () => { - const props: ImportSummaryProps = { + const props = getProps({ failedImports: [], successfulImports: [successOverwritten], - }; - const wrapper = shallowWithI18nProvider(); + }); + const wrapper = mountWithI18nProvider(); expect(findHeader(wrapper).childAt(0).props()).toEqual( - expect.not.objectContaining({ values: expect.anything() }) // no importCount for singular + expect.objectContaining({ values: { importCount: 1 } }) ); expect(findCountCreated(wrapper)).toHaveLength(0); const countOverwritten = findCountOverwritten(wrapper); @@ -92,14 +109,14 @@ describe('ImportSummary', () => { }); it('should render as expected with an error object', async () => { - const props: ImportSummaryProps = { + const props = getProps({ failedImports: [errorUnsupportedType], successfulImports: [], - }; - const wrapper = shallowWithI18nProvider(); + }); + const wrapper = mountWithI18nProvider(); expect(findHeader(wrapper).childAt(0).props()).toEqual( - expect.not.objectContaining({ values: expect.anything() }) // no importCount for singular + expect.objectContaining({ values: { importCount: 1 } }) ); expect(findCountCreated(wrapper)).toHaveLength(0); expect(findCountOverwritten(wrapper)).toHaveLength(0); @@ -112,11 +129,11 @@ describe('ImportSummary', () => { }); it('should render as expected with mixed objects', async () => { - const props: ImportSummaryProps = { + const props = getProps({ failedImports: [errorUnsupportedType], successfulImports: [successNew, successOverwritten], - }; - const wrapper = shallowWithI18nProvider(); + }); + const wrapper = mountWithI18nProvider(); expect(findHeader(wrapper).childAt(0).props()).toEqual( expect.objectContaining({ values: { importCount: 3 } }) @@ -138,4 +155,24 @@ describe('ImportSummary', () => { ); expect(findObjectRow(wrapper)).toHaveLength(3); }); + + it('should render warnings when present', async () => { + const props = getProps({ + successfulImports: [successNew], + importWarnings: [ + { + type: 'simple', + message: 'foo', + }, + { + type: 'action_required', + message: 'bar', + actionPath: '/app/lost', + }, + ], + }); + const wrapper = mountWithI18nProvider(); + + expect(findWarnings(wrapper)).toHaveLength(2); + }); }); diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.tsx index f562c3ca3f92..201bcc6f89cf 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_summary.tsx @@ -8,11 +8,13 @@ import './import_summary.scss'; import _ from 'lodash'; -import React, { Fragment } from 'react'; +import React, { Fragment, FC, useMemo } from 'react'; import { EuiText, EuiFlexGroup, EuiFlexItem, + EuiCallOut, + EuiButton, EuiToolTip, EuiIcon, EuiIconTip, @@ -22,15 +24,20 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SavedObjectsImportSuccess } from 'kibana/public'; -import { FailedImport } from '../../..'; -import { getDefaultTitle, getSavedObjectLabel } from '../../../lib'; +import type { + SavedObjectsImportSuccess, + SavedObjectsImportWarning, + IBasePath, +} from 'kibana/public'; +import { getDefaultTitle, getSavedObjectLabel, FailedImport } from '../../../lib'; const DEFAULT_ICON = 'apps'; export interface ImportSummaryProps { failedImports: FailedImport[]; successfulImports: SavedObjectsImportSuccess[]; + importWarnings: SavedObjectsImportWarning[]; + basePath: IBasePath; } interface ImportItem { @@ -72,7 +79,7 @@ const mapImportSuccess = (obj: SavedObjectsImportSuccess): ImportItem => { return { type, id, title, icon, outcome }; }; -const getCountIndicators = (importItems: ImportItem[]) => { +const CountIndicators: FC<{ importItems: ImportItem[] }> = ({ importItems }) => { if (!importItems.length) { return null; } @@ -130,7 +137,8 @@ const getCountIndicators = (importItems: ImportItem[]) => { ); }; -const getStatusIndicator = ({ outcome, errorMessage = 'Error' }: ImportItem) => { +const StatusIndicator: FC<{ item: ImportItem }> = ({ item }) => { + const { outcome, errorMessage = 'Error' } = item; switch (outcome) { case 'created': return ( @@ -165,13 +173,85 @@ const getStatusIndicator = ({ outcome, errorMessage = 'Error' }: ImportItem) => } }; -export const ImportSummary = ({ failedImports, successfulImports }: ImportSummaryProps) => { - const importItems: ImportItem[] = _.sortBy( - [ - ...failedImports.map((x) => mapFailedImport(x)), - ...successfulImports.map((x) => mapImportSuccess(x)), - ], - ['type', 'title'] +const ImportWarnings: FC<{ warnings: SavedObjectsImportWarning[]; basePath: IBasePath }> = ({ + warnings, + basePath, +}) => { + if (!warnings.length) { + return null; + } + + return ( + <> + + {warnings.map((warning, index) => ( + + + {index < warnings.length - 1 && } + + ))} + + ); +}; + +const ImportWarning: FC<{ warning: SavedObjectsImportWarning; basePath: IBasePath }> = ({ + warning, + basePath, +}) => { + const warningContent = useMemo(() => { + if (warning.type === 'action_required') { + return ( + + + + {warning.buttonLabel || ( + + )} + + + + ); + } + return null; + }, [warning, basePath]); + + return ( + + {warningContent} + + ); +}; + +export const ImportSummary: FC = ({ + failedImports, + successfulImports, + importWarnings, + basePath, +}) => { + const importItems: ImportItem[] = useMemo( + () => + _.sortBy( + [ + ...failedImports.map((x) => mapFailedImport(x)), + ...successfulImports.map((x) => mapImportSuccess(x)), + ], + ['type', 'title'] + ), + [successfulImports, failedImports] ); return ( @@ -183,22 +263,16 @@ export const ImportSummary = ({ failedImports, successfulImports }: ImportSummar } >

- {importItems.length === 1 ? ( - - ) : ( - - )} +

- - {getCountIndicators(importItems)} + + + {importItems.map((item, index) => { const { type, title, icon } = item; @@ -223,7 +297,9 @@ export const ImportSummary = ({ failedImports, successfulImports }: ImportSummar -
{getStatusIndicator(item)}
+
+ +
); diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx index 69ae11038122..bd70d34ae685 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx @@ -501,6 +501,7 @@ export class SavedObjectsTable extends Component ); diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index bed142f165d6..50a08d96de95 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -3864,6 +3864,27 @@ "apiCalls.savedObjectsGet.namespace.custom.kibanaRequest.no": { "type": "long" }, + "apiCalls.savedObjectsResolve.total": { + "type": "long" + }, + "apiCalls.savedObjectsResolve.namespace.default.total": { + "type": "long" + }, + "apiCalls.savedObjectsResolve.namespace.default.kibanaRequest.yes": { + "type": "long" + }, + "apiCalls.savedObjectsResolve.namespace.default.kibanaRequest.no": { + "type": "long" + }, + "apiCalls.savedObjectsResolve.namespace.custom.total": { + "type": "long" + }, + "apiCalls.savedObjectsResolve.namespace.custom.kibanaRequest.yes": { + "type": "long" + }, + "apiCalls.savedObjectsResolve.namespace.custom.kibanaRequest.no": { + "type": "long" + }, "apiCalls.savedObjectsUpdate.total": { "type": "long" }, diff --git a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.test.ts b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.test.ts index 457052decd65..ff73261d173d 100644 --- a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.test.ts +++ b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.test.ts @@ -36,7 +36,7 @@ describe('sendTelemetryOptInStatus', () => { expect(fetch).toBeCalledTimes(1); expect(fetch).toBeCalledWith(mockConfig.optInStatusUrl, { method: 'post', - body: mockOptInStatus, + body: '["mock_opt_in_hashed_value"]', headers: { 'X-Elastic-Stack-Version': mockConfig.currentKibanaVersion }, }); }); diff --git a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts index 33648769f170..1d23a49fc33e 100644 --- a/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts +++ b/src/plugins/telemetry/server/routes/telemetry_opt_in_stats.ts @@ -6,7 +6,6 @@ * Public License, v 1. */ -// @ts-ignore import fetch from 'node-fetch'; import { IRouter } from 'kibana/server'; @@ -35,7 +34,7 @@ export async function sendTelemetryOptInStatus( await fetch(optInStatusUrl, { method: 'post', - body: optInStatus, + body: JSON.stringify(optInStatus), headers: { 'X-Elastic-Stack-Version': currentKibanaVersion }, }); } diff --git a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx index 3b69544bd63d..ede209b772a4 100644 --- a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx +++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx @@ -38,7 +38,7 @@ interface Props { isSecurityExampleEnabled: () => boolean; showAppliesSettingMessage: boolean; enableSaving: boolean; - query?: any; + query?: { text: string }; toasts: ToastsStart; } @@ -51,34 +51,42 @@ interface State { } export class TelemetryManagementSection extends Component { - state: State = { - processing: false, - showExample: false, - showSecurityExample: false, - queryMatches: null, - enabled: this.props.telemetryService.getIsOptedIn() || false, - }; + constructor(props: Props) { + super(props); + + this.state = { + processing: false, + showExample: false, + showSecurityExample: false, + queryMatches: props.query ? this.checkQueryMatch(props.query) : null, + enabled: this.props.telemetryService.getIsOptedIn() || false, + }; + } UNSAFE_componentWillReceiveProps(nextProps: Props) { const { query } = nextProps; + const queryMatches = this.checkQueryMatch(query); - const searchTerm = (query.text || '').toLowerCase(); - const searchTermMatches = - this.props.telemetryService.getCanChangeOptInStatus() && - SEARCH_TERMS.some((term) => term.indexOf(searchTerm) >= 0); - - if (searchTermMatches !== this.state.queryMatches) { + if (queryMatches !== this.state.queryMatches) { this.setState( { - queryMatches: searchTermMatches, + queryMatches, }, () => { - this.props.onQueryMatchChange(searchTermMatches); + this.props.onQueryMatchChange(queryMatches); } ); } } + checkQueryMatch(query?: { text: string }): boolean { + const searchTerm = (query?.text ?? '').toLowerCase(); + return ( + this.props.telemetryService.getCanChangeOptInStatus() && + SEARCH_TERMS.some((term) => term.indexOf(searchTerm) >= 0) + ); + } + render() { const { telemetryService, isSecurityExampleEnabled } = this.props; const { showExample, showSecurityExample, queryMatches, enabled, processing } = this.state; diff --git a/src/plugins/vis_type_timelion/server/plugin.ts b/src/plugins/vis_type_timelion/server/plugin.ts index b41eecfc0c63..f999c1dfc773 100644 --- a/src/plugins/vis_type_timelion/server/plugin.ts +++ b/src/plugins/vis_type_timelion/server/plugin.ts @@ -11,8 +11,12 @@ import { first } from 'rxjs/operators'; import { TypeOf, schema } from '@kbn/config-schema'; import { RecursiveReadonly } from '@kbn/utility-types'; import { deepFreeze } from '@kbn/std'; +import type { RequestHandlerContext } from 'src/core/server'; -import { PluginStart } from '../../../../src/plugins/data/server'; +import type { + PluginStart, + DataApiRequestHandlerContext, +} from '../../../../src/plugins/data/server'; import { CoreSetup, PluginInitializerContext } from '../../../../src/core/server'; import { configSchema } from '../config'; import loadFunctions from './lib/load_functions'; @@ -67,7 +71,9 @@ export class Plugin { const logger = this.initializerContext.logger.get('timelion'); - const router = core.http.createRouter(); + const router = core.http.createRouter< + RequestHandlerContext & { search: DataApiRequestHandlerContext } + >(); const deps = { configManager, @@ -79,7 +85,7 @@ export class Plugin { functionsRoute(router, deps); runRoute(router, deps); - validateEsRoute(router, core); + validateEsRoute(router); core.uiSettings.register({ 'timelion:es.timefield': { diff --git a/src/plugins/vis_type_timelion/server/routes/validate_es.ts b/src/plugins/vis_type_timelion/server/routes/validate_es.ts index 242029a492e9..1637fcc464f4 100644 --- a/src/plugins/vis_type_timelion/server/routes/validate_es.ts +++ b/src/plugins/vis_type_timelion/server/routes/validate_es.ts @@ -7,9 +7,12 @@ */ import _ from 'lodash'; -import { IRouter, CoreSetup } from 'kibana/server'; +import { IRouter, RequestHandlerContext } from 'kibana/server'; +import type { DataApiRequestHandlerContext } from '../../../data/server'; -export function validateEsRoute(router: IRouter, core: CoreSetup) { +export function validateEsRoute( + router: IRouter +) { router.get( { path: '/api/timelion/validate/es', diff --git a/src/plugins/vis_type_timeseries/server/lib/get_fields.ts b/src/plugins/vis_type_timeseries/server/lib/get_fields.ts index 8bcafa685a27..dc075930cf25 100644 --- a/src/plugins/vis_type_timeseries/server/lib/get_fields.ts +++ b/src/plugins/vis_type_timeseries/server/lib/get_fields.ts @@ -8,14 +8,15 @@ import { uniqBy } from 'lodash'; import { first, map } from 'rxjs/operators'; -import { KibanaRequest, RequestHandlerContext } from 'kibana/server'; +import { KibanaRequest } from 'kibana/server'; import { Framework } from '../plugin'; import { IndexPatternsFetcher } from '../../../data/server'; import { ReqFacade } from './search_strategies/strategies/abstract_search_strategy'; +import { VisTypeTimeseriesRequestHandlerContext } from '../types'; export async function getFields( - requestContext: RequestHandlerContext, + requestContext: VisTypeTimeseriesRequestHandlerContext, request: KibanaRequest, framework: Framework, indexPatternString: string diff --git a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts index 81881d5bda4e..3b1e9d373f13 100644 --- a/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts +++ b/src/plugins/vis_type_timeseries/server/lib/get_vis_data.ts @@ -6,7 +6,7 @@ * Public License, v 1. */ -import { FakeRequest, RequestHandlerContext } from 'kibana/server'; +import { FakeRequest } from 'kibana/server'; import _ from 'lodash'; import { first, map } from 'rxjs/operators'; @@ -15,6 +15,7 @@ import { getPanelData } from './vis_data/get_panel_data'; import { Framework } from '../plugin'; import { ReqFacade } from './search_strategies/strategies/abstract_search_strategy'; import { TimeseriesVisData } from '../../common/types'; +import type { VisTypeTimeseriesRequestHandlerContext } from '../types'; export interface GetVisDataOptions { timerange: { @@ -30,13 +31,13 @@ export interface GetVisDataOptions { } export type GetVisData = ( - requestContext: RequestHandlerContext, + requestContext: VisTypeTimeseriesRequestHandlerContext, options: GetVisDataOptions, framework: Framework ) => Promise; export function getVisData( - requestContext: RequestHandlerContext, + requestContext: VisTypeTimeseriesRequestHandlerContext, request: FakeRequest & { body: GetVisDataOptions }, framework: Framework ): Promise { diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts index 871baf959645..966daca87a20 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts +++ b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts @@ -6,12 +6,7 @@ * Public License, v 1. */ -import type { - RequestHandlerContext, - FakeRequest, - IUiSettingsClient, - SavedObjectsClientContract, -} from 'kibana/server'; +import type { FakeRequest, IUiSettingsClient, SavedObjectsClientContract } from 'kibana/server'; import type { Framework } from '../../../plugin'; import type { IndexPatternsFetcher, IFieldType } from '../../../../../data/server'; @@ -19,6 +14,7 @@ import type { VisPayload } from '../../../../common/types'; import type { IndexPatternsService } from '../../../../../data/common'; import { indexPatterns } from '../../../../../data/server'; import { SanitizedFieldType } from '../../../../common/types'; +import type { VisTypeTimeseriesRequestHandlerContext } from '../../../types'; /** * ReqFacade is a regular KibanaRequest object extended with additional service @@ -27,7 +23,7 @@ import { SanitizedFieldType } from '../../../../common/types'; * This will be replaced by standard KibanaRequest and RequestContext objects in a later version. */ export interface ReqFacade extends FakeRequest { - requestContext: RequestHandlerContext; + requestContext: VisTypeTimeseriesRequestHandlerContext; framework: Framework; payload: T; pre: { @@ -58,8 +54,8 @@ export abstract class AbstractSearchStrategy { bodies.forEach((body) => { requests.push( - req.requestContext - .search!.search( + req.requestContext.search + .search( { indexType, params: { diff --git a/src/plugins/vis_type_timeseries/server/plugin.ts b/src/plugins/vis_type_timeseries/server/plugin.ts index bd483e3f0f72..adcd7e8bbf0d 100644 --- a/src/plugins/vis_type_timeseries/server/plugin.ts +++ b/src/plugins/vis_type_timeseries/server/plugin.ts @@ -11,9 +11,7 @@ import { CoreSetup, CoreStart, Plugin, - RequestHandlerContext, Logger, - IRouter, FakeRequest, } from 'src/core/server'; import { Observable } from 'rxjs'; @@ -27,6 +25,7 @@ import { visDataRoutes } from './routes/vis'; import { fieldsRoutes } from './routes/fields'; import { SearchStrategyRegistry } from './lib/search_strategies'; import { uiSettings } from './ui_settings'; +import type { VisTypeTimeseriesRequestHandlerContext, VisTypeTimeseriesRouter } from './types'; export interface LegacySetup { server: Server; @@ -42,7 +41,7 @@ interface VisTypeTimeseriesPluginStartDependencies { export interface VisTypeTimeseriesSetup { getVisData: ( - requestContext: RequestHandlerContext, + requestContext: VisTypeTimeseriesRequestHandlerContext, fakeRequest: FakeRequest, options: GetVisDataOptions ) => ReturnType; @@ -55,7 +54,7 @@ export interface Framework { config$: Observable; globalConfig$: PluginInitializerContext['config']['legacy']['globalConfig$']; logger: Logger; - router: IRouter; + router: VisTypeTimeseriesRouter; searchStrategyRegistry: SearchStrategyRegistry; } @@ -73,7 +72,7 @@ export class VisTypeTimeseriesPlugin implements Plugin { const config$ = this.initializerContext.config.create(); // Global config contains things like the ES shard timeout const globalConfig$ = this.initializerContext.config.legacy.globalConfig$; - const router = core.http.createRouter(); + const router = core.http.createRouter(); const searchStrategyRegistry = new SearchStrategyRegistry(); @@ -92,7 +91,7 @@ export class VisTypeTimeseriesPlugin implements Plugin { return { getVisData: async ( - requestContext: RequestHandlerContext, + requestContext: VisTypeTimeseriesRequestHandlerContext, fakeRequest: FakeRequest, options: GetVisDataOptions ) => { diff --git a/src/plugins/vis_type_timeseries/server/routes/vis.ts b/src/plugins/vis_type_timeseries/server/routes/vis.ts index fd9dfc18eadf..d1fcaa97b305 100644 --- a/src/plugins/vis_type_timeseries/server/routes/vis.ts +++ b/src/plugins/vis_type_timeseries/server/routes/vis.ts @@ -6,17 +6,18 @@ * Public License, v 1. */ -import { IRouter, KibanaRequest } from 'kibana/server'; +import { KibanaRequest } from 'kibana/server'; import { schema } from '@kbn/config-schema'; import { ensureNoUnsafeProperties } from '@kbn/std'; import { getVisData, GetVisDataOptions } from '../lib/get_vis_data'; import { visPayloadSchema } from '../../common/vis_schema'; import { ROUTES } from '../../common/constants'; import { Framework } from '../plugin'; +import type { VisTypeTimeseriesRouter } from '../types'; const escapeHatch = schema.object({}, { unknowns: 'allow' }); -export const visDataRoutes = (router: IRouter, framework: Framework) => { +export const visDataRoutes = (router: VisTypeTimeseriesRouter, framework: Framework) => { router.post( { path: ROUTES.VIS_DATA, diff --git a/src/plugins/vis_type_timeseries/server/types.ts b/src/plugins/vis_type_timeseries/server/types.ts new file mode 100644 index 000000000000..29cd33031c88 --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/types.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import type { IRouter, RequestHandlerContext } from 'src/core/server'; +import type { DataApiRequestHandlerContext } from '../../data/server'; + +export interface VisTypeTimeseriesRequestHandlerContext extends RequestHandlerContext { + search: DataApiRequestHandlerContext; +} + +export type VisTypeTimeseriesRouter = IRouter; diff --git a/src/plugins/vis_type_vega/public/data_model/search_api.ts b/src/plugins/vis_type_vega/public/data_model/search_api.ts index 4cb2a6119a2b..2d4710270513 100644 --- a/src/plugins/vis_type_vega/public/data_model/search_api.ts +++ b/src/plugins/vis_type_vega/public/data_model/search_api.ts @@ -45,7 +45,10 @@ export class SearchAPI { }); if (this.inspectorAdapters) { - requestResponders[requestId] = this.inspectorAdapters.requests.start(requestId, request); + requestResponders[requestId] = this.inspectorAdapters.requests.start(requestId, { + ...request, + searchSessionId: this.searchSessionId, + }); requestResponders[requestId].json(params.body); } diff --git a/test/api_integration/apis/saved_objects/bulk_create.ts b/test/api_integration/apis/saved_objects/bulk_create.ts index 903332a0a930..a548172365b0 100644 --- a/test/api_integration/apis/saved_objects/bulk_create.ts +++ b/test/api_integration/apis/saved_objects/bulk_create.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { getKibanaVersion } from './lib/saved_objects_test_utils'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -21,6 +22,7 @@ export default function ({ getService }: FtrProviderContext) { attributes: { title: 'An existing visualization', }, + coreMigrationVersion: '1.2.3', }, { type: 'dashboard', @@ -32,6 +34,12 @@ export default function ({ getService }: FtrProviderContext) { ]; describe('_bulk_create', () => { + let KIBANA_VERSION: string; + + before(async () => { + KIBANA_VERSION = await getKibanaVersion(getService); + }); + describe('with kibana index', () => { before(() => esArchiver.load('saved_objects/basic')); after(() => esArchiver.unload('saved_objects/basic')); @@ -65,6 +73,7 @@ export default function ({ getService }: FtrProviderContext) { migrationVersion: { dashboard: resp.body.saved_objects[1].migrationVersion.dashboard, }, + coreMigrationVersion: KIBANA_VERSION, references: [], namespaces: ['default'], }, @@ -112,6 +121,7 @@ export default function ({ getService }: FtrProviderContext) { migrationVersion: { visualization: resp.body.saved_objects[0].migrationVersion.visualization, }, + coreMigrationVersion: KIBANA_VERSION, // updated from 1.2.3 to the latest kibana version }, { type: 'dashboard', @@ -126,6 +136,7 @@ export default function ({ getService }: FtrProviderContext) { migrationVersion: { dashboard: resp.body.saved_objects[1].migrationVersion.dashboard, }, + coreMigrationVersion: KIBANA_VERSION, }, ], }); diff --git a/test/api_integration/apis/saved_objects/bulk_get.ts b/test/api_integration/apis/saved_objects/bulk_get.ts index e552c08a58cf..46631225f8e8 100644 --- a/test/api_integration/apis/saved_objects/bulk_get.ts +++ b/test/api_integration/apis/saved_objects/bulk_get.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { getKibanaVersion } from './lib/saved_objects_test_utils'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -30,6 +31,12 @@ export default function ({ getService }: FtrProviderContext) { ]; describe('_bulk_get', () => { + let KIBANA_VERSION: string; + + before(async () => { + KIBANA_VERSION = await getKibanaVersion(getService); + }); + describe('with kibana index', () => { before(() => esArchiver.load('saved_objects/basic')); after(() => esArchiver.unload('saved_objects/basic')); @@ -58,6 +65,7 @@ export default function ({ getService }: FtrProviderContext) { resp.body.saved_objects[0].attributes.kibanaSavedObjectMeta, }, migrationVersion: resp.body.saved_objects[0].migrationVersion, + coreMigrationVersion: KIBANA_VERSION, namespaces: ['default'], references: [ { @@ -87,6 +95,7 @@ export default function ({ getService }: FtrProviderContext) { }, namespaces: ['default'], migrationVersion: resp.body.saved_objects[2].migrationVersion, + coreMigrationVersion: KIBANA_VERSION, references: [], }, ], diff --git a/test/api_integration/apis/saved_objects/create.ts b/test/api_integration/apis/saved_objects/create.ts index b1cd5a8dfdae..551e082630e8 100644 --- a/test/api_integration/apis/saved_objects/create.ts +++ b/test/api_integration/apis/saved_objects/create.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { getKibanaVersion } from './lib/saved_objects_test_utils'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -15,6 +16,12 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); describe('create', () => { + let KIBANA_VERSION: string; + + before(async () => { + KIBANA_VERSION = await getKibanaVersion(getService); + }); + describe('with kibana index', () => { before(() => esArchiver.load('saved_objects/basic')); after(() => esArchiver.unload('saved_objects/basic')); @@ -42,6 +49,7 @@ export default function ({ getService }: FtrProviderContext) { id: resp.body.id, type: 'visualization', migrationVersion: resp.body.migrationVersion, + coreMigrationVersion: KIBANA_VERSION, updated_at: resp.body.updated_at, version: resp.body.version, attributes: { @@ -53,6 +61,21 @@ export default function ({ getService }: FtrProviderContext) { expect(resp.body.migrationVersion).to.be.ok(); }); }); + + it('result should be updated to the latest coreMigrationVersion', async () => { + await supertest + .post(`/api/saved_objects/visualization`) + .send({ + attributes: { + title: 'My favorite vis', + }, + coreMigrationVersion: '1.2.3', + }) + .expect(200) + .then((resp) => { + expect(resp.body.coreMigrationVersion).to.eql(KIBANA_VERSION); + }); + }); }); describe('without kibana index', () => { @@ -86,6 +109,7 @@ export default function ({ getService }: FtrProviderContext) { id: resp.body.id, type: 'visualization', migrationVersion: resp.body.migrationVersion, + coreMigrationVersion: KIBANA_VERSION, updated_at: resp.body.updated_at, version: resp.body.version, attributes: { @@ -99,6 +123,21 @@ export default function ({ getService }: FtrProviderContext) { expect((await es.indices.exists({ index: '.kibana' })).body).to.be(true); }); + + it('result should have the latest coreMigrationVersion', async () => { + await supertest + .post(`/api/saved_objects/visualization`) + .send({ + attributes: { + title: 'My favorite vis', + }, + coreMigrationVersion: '1.2.3', + }) + .expect(200) + .then((resp) => { + expect(resp.body.coreMigrationVersion).to.eql(KIBANA_VERSION); + }); + }); }); }); } diff --git a/test/api_integration/apis/saved_objects/export.ts b/test/api_integration/apis/saved_objects/export.ts index a84f3050fdd1..a45191f24d87 100644 --- a/test/api_integration/apis/saved_objects/export.ts +++ b/test/api_integration/apis/saved_objects/export.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import type { FtrProviderContext } from '../../ftr_provider_context'; +import { getKibanaVersion } from './lib/saved_objects_test_utils'; function ndjsonToObject(input: string) { return input.split('\n').map((str) => JSON.parse(str)); @@ -18,6 +19,12 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); describe('export', () => { + let KIBANA_VERSION: string; + + before(async () => { + KIBANA_VERSION = await getKibanaVersion(getService); + }); + describe('with kibana index', () => { describe('basic amount of saved objects', () => { before(() => esArchiver.load('saved_objects/basic')); @@ -312,6 +319,7 @@ export default function ({ getService }: FtrProviderContext) { }, id: 'be3733a0-9efe-11e7-acb3-3dab96693fab', migrationVersion: objects[0].migrationVersion, + coreMigrationVersion: KIBANA_VERSION, references: [ { id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab', @@ -371,6 +379,7 @@ export default function ({ getService }: FtrProviderContext) { }, id: 'be3733a0-9efe-11e7-acb3-3dab96693fab', migrationVersion: objects[0].migrationVersion, + coreMigrationVersion: KIBANA_VERSION, references: [ { id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab', @@ -435,6 +444,7 @@ export default function ({ getService }: FtrProviderContext) { }, id: 'be3733a0-9efe-11e7-acb3-3dab96693fab', migrationVersion: objects[0].migrationVersion, + coreMigrationVersion: KIBANA_VERSION, references: [ { id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab', diff --git a/test/api_integration/apis/saved_objects/find.ts b/test/api_integration/apis/saved_objects/find.ts index a3ce70888049..7aa4de86baa6 100644 --- a/test/api_integration/apis/saved_objects/find.ts +++ b/test/api_integration/apis/saved_objects/find.ts @@ -9,6 +9,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; import { SavedObject } from '../../../../src/core/server'; +import { getKibanaVersion } from './lib/saved_objects_test_utils'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -16,6 +17,12 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); describe('find', () => { + let KIBANA_VERSION: string; + + before(async () => { + KIBANA_VERSION = await getKibanaVersion(getService); + }); + describe('with kibana index', () => { before(() => esArchiver.load('saved_objects/basic')); after(() => esArchiver.unload('saved_objects/basic')); @@ -39,6 +46,7 @@ export default function ({ getService }: FtrProviderContext) { }, score: 0, migrationVersion: resp.body.saved_objects[0].migrationVersion, + coreMigrationVersion: KIBANA_VERSION, namespaces: ['default'], references: [ { @@ -134,6 +142,7 @@ export default function ({ getService }: FtrProviderContext) { title: 'Count of requests', }, migrationVersion: resp.body.saved_objects[0].migrationVersion, + coreMigrationVersion: KIBANA_VERSION, namespaces: ['default'], score: 0, references: [ @@ -170,6 +179,7 @@ export default function ({ getService }: FtrProviderContext) { title: 'Count of requests', }, migrationVersion: resp.body.saved_objects[0].migrationVersion, + coreMigrationVersion: KIBANA_VERSION, namespaces: ['default'], score: 0, references: [ @@ -187,6 +197,7 @@ export default function ({ getService }: FtrProviderContext) { }, id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab', migrationVersion: resp.body.saved_objects[0].migrationVersion, + coreMigrationVersion: KIBANA_VERSION, namespaces: ['foo-ns'], references: [ { @@ -202,7 +213,6 @@ export default function ({ getService }: FtrProviderContext) { }, ], }); - expect(resp.body.saved_objects[0].migrationVersion).to.be.ok(); })); }); @@ -244,6 +254,7 @@ export default function ({ getService }: FtrProviderContext) { }, ], migrationVersion: resp.body.saved_objects[0].migrationVersion, + coreMigrationVersion: KIBANA_VERSION, updated_at: '2017-09-21T18:51:23.794Z', version: 'WzIsMV0=', }, diff --git a/test/api_integration/apis/saved_objects/get.ts b/test/api_integration/apis/saved_objects/get.ts index 713491712217..ff47b9df218d 100644 --- a/test/api_integration/apis/saved_objects/get.ts +++ b/test/api_integration/apis/saved_objects/get.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { getKibanaVersion } from './lib/saved_objects_test_utils'; export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -15,6 +16,12 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); describe('get', () => { + let KIBANA_VERSION: string; + + before(async () => { + KIBANA_VERSION = await getKibanaVersion(getService); + }); + describe('with kibana index', () => { before(() => esArchiver.load('saved_objects/basic')); after(() => esArchiver.unload('saved_objects/basic')); @@ -30,6 +37,7 @@ export default function ({ getService }: FtrProviderContext) { updated_at: '2017-09-21T18:51:23.794Z', version: resp.body.version, migrationVersion: resp.body.migrationVersion, + coreMigrationVersion: KIBANA_VERSION, attributes: { title: 'Count of requests', description: '', diff --git a/test/api_integration/apis/saved_objects/import.ts b/test/api_integration/apis/saved_objects/import.ts index 4dcaa4bd9993..573ad60482e2 100644 --- a/test/api_integration/apis/saved_objects/import.ts +++ b/test/api_integration/apis/saved_objects/import.ts @@ -74,6 +74,7 @@ export default function ({ getService }: FtrProviderContext) { createConflictError(visualization), createConflictError(dashboard), ], + warnings: [], }); }); }); @@ -93,6 +94,7 @@ export default function ({ getService }: FtrProviderContext) { { ...visualization, overwrite: true }, { ...dashboard, overwrite: true }, ], + warnings: [], }); }); }); @@ -119,6 +121,7 @@ export default function ({ getService }: FtrProviderContext) { error: { type: 'unsupported_type' }, }, ], + warnings: [], }); }); }); @@ -157,6 +160,7 @@ export default function ({ getService }: FtrProviderContext) { type: 'dashboard', }, ], + warnings: [], }); }); @@ -227,6 +231,7 @@ export default function ({ getService }: FtrProviderContext) { }, }, ], + warnings: [], }); }); }); diff --git a/test/api_integration/apis/saved_objects/index.ts b/test/api_integration/apis/saved_objects/index.ts index 0e07b3c1ed06..2f63a4a7cce0 100644 --- a/test/api_integration/apis/saved_objects/index.ts +++ b/test/api_integration/apis/saved_objects/index.ts @@ -12,15 +12,16 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('saved_objects', () => { loadTestFile(require.resolve('./bulk_create')); loadTestFile(require.resolve('./bulk_get')); + loadTestFile(require.resolve('./bulk_update')); loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./export')); loadTestFile(require.resolve('./find')); loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./import')); + loadTestFile(require.resolve('./migrations')); + loadTestFile(require.resolve('./resolve')); loadTestFile(require.resolve('./resolve_import_errors')); loadTestFile(require.resolve('./update')); - loadTestFile(require.resolve('./bulk_update')); - loadTestFile(require.resolve('./migrations')); }); } diff --git a/test/api_integration/apis/saved_objects/lib/saved_objects_test_utils.ts b/test/api_integration/apis/saved_objects/lib/saved_objects_test_utils.ts new file mode 100644 index 000000000000..e278bd3d5003 --- /dev/null +++ b/test/api_integration/apis/saved_objects/lib/saved_objects_test_utils.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export async function getKibanaVersion(getService: FtrProviderContext['getService']) { + const kibanaServer = getService('kibanaServer'); + const kibanaVersion = await kibanaServer.version.get(); + expect(typeof kibanaVersion).to.eql('string'); + expect(kibanaVersion.length).to.be.greaterThan(0); + return kibanaVersion; +} diff --git a/test/api_integration/apis/saved_objects/migrations.ts b/test/api_integration/apis/saved_objects/migrations.ts index 9bb820b2f841..0b06b675f60c 100644 --- a/test/api_integration/apis/saved_objects/migrations.ts +++ b/test/api_integration/apis/saved_objects/migrations.ts @@ -10,10 +10,11 @@ * Smokescreen tests for core migration logic */ +import uuidv5 from 'uuid/v5'; import { set } from '@elastic/safer-lodash-set'; import _ from 'lodash'; import expect from '@kbn/expect'; -import { ElasticsearchClient, SavedObjectMigrationMap, SavedObjectsType } from 'src/core/server'; +import { ElasticsearchClient, SavedObjectsType } from 'src/core/server'; import { SearchResponse } from '../../../../src/core/server/elasticsearch/client'; import { DocumentMigrator, @@ -28,6 +29,26 @@ import { } from '../../../../src/core/server/saved_objects'; import { FtrProviderContext } from '../../ftr_provider_context'; +const KIBANA_VERSION = '99.9.9'; +const FOO_TYPE: SavedObjectsType = { + name: 'foo', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, +}; +const BAR_TYPE: SavedObjectsType = { + name: 'bar', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, +}; +const BAZ_TYPE: SavedObjectsType = { + name: 'baz', + hidden: false, + namespaceType: 'single', + mappings: { properties: {} }, +}; + function getLogMock() { return { debug() {}, @@ -61,16 +82,22 @@ export default ({ getService }: FtrProviderContext) => { bar: { properties: { mynum: { type: 'integer' } } }, }; - const migrations: Record = { - foo: { - '1.0.0': (doc) => set(doc, 'attributes.name', doc.attributes.name.toUpperCase()), + const savedObjectTypes: SavedObjectsType[] = [ + { + ...FOO_TYPE, + migrations: { + '1.0.0': (doc) => set(doc, 'attributes.name', doc.attributes.name.toUpperCase()), + }, }, - bar: { - '1.0.0': (doc) => set(doc, 'attributes.nomnom', doc.attributes.nomnom + 1), - '1.3.0': (doc) => set(doc, 'attributes', { mynum: doc.attributes.nomnom }), - '1.9.0': (doc) => set(doc, 'attributes.mynum', doc.attributes.mynum * 2), + { + ...BAR_TYPE, + migrations: { + '1.0.0': (doc) => set(doc, 'attributes.nomnom', doc.attributes.nomnom + 1), + '1.3.0': (doc) => set(doc, 'attributes', { mynum: doc.attributes.nomnom }), + '1.9.0': (doc) => set(doc, 'attributes.mynum', doc.attributes.mynum * 2), + }, }, - }; + ]; await createIndex({ esClient, index }); await createDocs({ esClient, index, docs: originalDocs }); @@ -107,7 +134,7 @@ export default ({ getService }: FtrProviderContext) => { const result = await migrateIndex({ esClient, index, - migrations, + savedObjectTypes, mappingProperties, obsoleteIndexTemplatePattern: 'migration_a*', }); @@ -129,13 +156,7 @@ export default ({ getService }: FtrProviderContext) => { }); // The docs in the original index are unchanged - expect(await fetchDocs(esClient, `${index}_1`)).to.eql([ - { id: 'bar:i', type: 'bar', bar: { nomnom: 33 } }, - { id: 'bar:o', type: 'bar', bar: { nomnom: 2 } }, - { id: 'baz:u', type: 'baz', baz: { title: 'Terrific!' } }, - { id: 'foo:a', type: 'foo', foo: { name: 'Foo A' } }, - { id: 'foo:e', type: 'foo', foo: { name: 'Fooey' } }, - ]); + expect(await fetchDocs(esClient, `${index}_1`)).to.eql(originalDocs.sort(sortByTypeAndId)); // The docs in the alias have been migrated expect(await fetchDocs(esClient, index)).to.eql([ @@ -145,6 +166,7 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { bar: '1.9.0' }, bar: { mynum: 68 }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, { id: 'bar:o', @@ -152,14 +174,22 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { bar: '1.9.0' }, bar: { mynum: 6 }, references: [], + coreMigrationVersion: KIBANA_VERSION, + }, + { + id: 'baz:u', + type: 'baz', + baz: { title: 'Terrific!' }, + references: [], + coreMigrationVersion: KIBANA_VERSION, }, - { id: 'baz:u', type: 'baz', baz: { title: 'Terrific!' }, references: [] }, { id: 'foo:a', type: 'foo', migrationVersion: { foo: '1.0.0' }, foo: { name: 'FOO A' }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, { id: 'foo:e', @@ -167,6 +197,7 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { foo: '1.0.0' }, foo: { name: 'FOOEY' }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, ]); }); @@ -185,28 +216,46 @@ export default ({ getService }: FtrProviderContext) => { bar: { properties: { mynum: { type: 'integer' } } }, }; - const migrations: Record = { - foo: { - '1.0.0': (doc) => set(doc, 'attributes.name', doc.attributes.name.toUpperCase()), + let savedObjectTypes: SavedObjectsType[] = [ + { + ...FOO_TYPE, + migrations: { + '1.0.0': (doc) => set(doc, 'attributes.name', doc.attributes.name.toUpperCase()), + }, }, - bar: { - '1.0.0': (doc) => set(doc, 'attributes.nomnom', doc.attributes.nomnom + 1), - '1.3.0': (doc) => set(doc, 'attributes', { mynum: doc.attributes.nomnom }), - '1.9.0': (doc) => set(doc, 'attributes.mynum', doc.attributes.mynum * 2), + { + ...BAR_TYPE, + migrations: { + '1.0.0': (doc) => set(doc, 'attributes.nomnom', doc.attributes.nomnom + 1), + '1.3.0': (doc) => set(doc, 'attributes', { mynum: doc.attributes.nomnom }), + '1.9.0': (doc) => set(doc, 'attributes.mynum', doc.attributes.mynum * 2), + }, }, - }; + ]; await createIndex({ esClient, index }); await createDocs({ esClient, index, docs: originalDocs }); - await migrateIndex({ esClient, index, migrations, mappingProperties }); + await migrateIndex({ esClient, index, savedObjectTypes, mappingProperties }); // @ts-expect-error name doesn't exist on mynum type mappingProperties.bar.properties.name = { type: 'keyword' }; - migrations.foo['2.0.1'] = (doc) => set(doc, 'attributes.name', `${doc.attributes.name}v2`); - migrations.bar['2.3.4'] = (doc) => set(doc, 'attributes.name', `NAME ${doc.id}`); + savedObjectTypes = [ + { + ...FOO_TYPE, + migrations: { + '2.0.1': (doc) => set(doc, 'attributes.name', `${doc.attributes.name}v2`), + }, + }, + { + ...BAR_TYPE, + migrations: { + '2.3.4': (doc) => set(doc, 'attributes.name', `NAME ${doc.id}`), + }, + }, + ]; - await migrateIndex({ esClient, index, migrations, mappingProperties }); + await migrateIndex({ esClient, index, savedObjectTypes, mappingProperties }); // The index for the initial migration has not been destroyed... expect(await fetchDocs(esClient, `${index}_2`)).to.eql([ @@ -216,6 +265,7 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { bar: '1.9.0' }, bar: { mynum: 68 }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, { id: 'bar:o', @@ -223,6 +273,7 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { bar: '1.9.0' }, bar: { mynum: 6 }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, { id: 'foo:a', @@ -230,6 +281,7 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { foo: '1.0.0' }, foo: { name: 'FOO A' }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, { id: 'foo:e', @@ -237,6 +289,7 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { foo: '1.0.0' }, foo: { name: 'FOOEY' }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, ]); @@ -248,6 +301,7 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { bar: '2.3.4' }, bar: { mynum: 68, name: 'NAME i' }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, { id: 'bar:o', @@ -255,6 +309,7 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { bar: '2.3.4' }, bar: { mynum: 6, name: 'NAME o' }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, { id: 'foo:a', @@ -262,6 +317,7 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { foo: '2.0.1' }, foo: { name: 'FOO Av2' }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, { id: 'foo:e', @@ -269,6 +325,7 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { foo: '2.0.1' }, foo: { name: 'FOOEYv2' }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, ]); }); @@ -281,18 +338,21 @@ export default ({ getService }: FtrProviderContext) => { foo: { properties: { name: { type: 'text' } } }, }; - const migrations: Record = { - foo: { - '1.0.0': (doc) => set(doc, 'attributes.name', 'LOTR'), + const savedObjectTypes: SavedObjectsType[] = [ + { + ...FOO_TYPE, + migrations: { + '1.0.0': (doc) => set(doc, 'attributes.name', 'LOTR'), + }, }, - }; + ]; await createIndex({ esClient, index }); await createDocs({ esClient, index, docs: originalDocs }); const result = await Promise.all([ - migrateIndex({ esClient, index, migrations, mappingProperties }), - migrateIndex({ esClient, index, migrations, mappingProperties }), + migrateIndex({ esClient, index, savedObjectTypes, mappingProperties }), + migrateIndex({ esClient, index, savedObjectTypes, mappingProperties }), ]); // The polling instance and the migrating instance should both @@ -327,9 +387,170 @@ export default ({ getService }: FtrProviderContext) => { migrationVersion: { foo: '1.0.0' }, foo: { name: 'LOTR' }, references: [], + coreMigrationVersion: KIBANA_VERSION, }, ]); }); + + it('Correctly applies reference transforms and conversion transforms', async () => { + const index = '.migration-d'; + const originalDocs = [ + { id: 'foo:1', type: 'foo', foo: { name: 'Foo 1 default' } }, + { id: 'spacex:foo:1', type: 'foo', foo: { name: 'Foo 1 spacex' }, namespace: 'spacex' }, + { + id: 'bar:1', + type: 'bar', + bar: { nomnom: 1 }, + references: [{ type: 'foo', id: '1', name: 'Foo 1 default' }], + }, + { + id: 'spacex:bar:1', + type: 'bar', + bar: { nomnom: 2 }, + references: [{ type: 'foo', id: '1', name: 'Foo 1 spacex' }], + namespace: 'spacex', + }, + { + id: 'baz:1', + type: 'baz', + baz: { title: 'Baz 1 default' }, + references: [{ type: 'bar', id: '1', name: 'Bar 1 default' }], + }, + { + id: 'spacex:baz:1', + type: 'baz', + baz: { title: 'Baz 1 spacex' }, + references: [{ type: 'bar', id: '1', name: 'Bar 1 spacex' }], + namespace: 'spacex', + }, + ]; + + const mappingProperties = { + foo: { properties: { name: { type: 'text' } } }, + bar: { properties: { nomnom: { type: 'integer' } } }, + baz: { properties: { title: { type: 'keyword' } } }, + }; + + const savedObjectTypes: SavedObjectsType[] = [ + { + ...FOO_TYPE, + namespaceType: 'multiple', + convertToMultiNamespaceTypeVersion: '1.0.0', + }, + { + ...BAR_TYPE, + namespaceType: 'multiple', + convertToMultiNamespaceTypeVersion: '2.0.0', + }, + BAZ_TYPE, // must be registered for reference transforms to be applied to objects of this type + ]; + + await createIndex({ esClient, index }); + await createDocs({ esClient, index, docs: originalDocs }); + + await migrateIndex({ + esClient, + index, + savedObjectTypes, + mappingProperties, + obsoleteIndexTemplatePattern: 'migration_a*', + }); + + // The docs in the original index are unchanged + expect(await fetchDocs(esClient, `${index}_1`)).to.eql(originalDocs.sort(sortByTypeAndId)); + + // The docs in the alias have been migrated + const migratedDocs = await fetchDocs(esClient, index); + + // each newly converted multi-namespace object in a non-default space has its ID deterministically regenerated, and a legacy-url-alias + // object is created which links the old ID to the new ID + const newFooId = uuidv5('spacex:foo:1', uuidv5.DNS); + const newBarId = uuidv5('spacex:bar:1', uuidv5.DNS); + + expect(migratedDocs).to.eql( + [ + { + id: 'foo:1', + type: 'foo', + foo: { name: 'Foo 1 default' }, + references: [], + namespaces: ['default'], + migrationVersion: { foo: '1.0.0' }, + coreMigrationVersion: KIBANA_VERSION, + }, + { + id: `foo:${newFooId}`, + type: 'foo', + foo: { name: 'Foo 1 spacex' }, + references: [], + namespaces: ['spacex'], + originId: '1', + migrationVersion: { foo: '1.0.0' }, + coreMigrationVersion: KIBANA_VERSION, + }, + { + // new object + id: 'legacy-url-alias:spacex:foo:1', + type: 'legacy-url-alias', + 'legacy-url-alias': { + targetId: newFooId, + targetNamespace: 'spacex', + targetType: 'foo', + }, + migrationVersion: {}, + references: [], + coreMigrationVersion: KIBANA_VERSION, + }, + { + id: 'bar:1', + type: 'bar', + bar: { nomnom: 1 }, + references: [{ type: 'foo', id: '1', name: 'Foo 1 default' }], + namespaces: ['default'], + migrationVersion: { bar: '2.0.0' }, + coreMigrationVersion: KIBANA_VERSION, + }, + { + id: `bar:${newBarId}`, + type: 'bar', + bar: { nomnom: 2 }, + references: [{ type: 'foo', id: newFooId, name: 'Foo 1 spacex' }], + namespaces: ['spacex'], + originId: '1', + migrationVersion: { bar: '2.0.0' }, + coreMigrationVersion: KIBANA_VERSION, + }, + { + // new object + id: 'legacy-url-alias:spacex:bar:1', + type: 'legacy-url-alias', + 'legacy-url-alias': { + targetId: newBarId, + targetNamespace: 'spacex', + targetType: 'bar', + }, + migrationVersion: {}, + references: [], + coreMigrationVersion: KIBANA_VERSION, + }, + { + id: 'baz:1', + type: 'baz', + baz: { title: 'Baz 1 default' }, + references: [{ type: 'bar', id: '1', name: 'Bar 1 default' }], + coreMigrationVersion: KIBANA_VERSION, + }, + { + id: 'spacex:baz:1', + type: 'baz', + baz: { title: 'Baz 1 spacex' }, + references: [{ type: 'bar', id: newBarId, name: 'Bar 1 spacex' }], + namespace: 'spacex', + coreMigrationVersion: KIBANA_VERSION, + }, + ].sort(sortByTypeAndId) + ); + }); }); }; @@ -340,6 +561,30 @@ async function createIndex({ esClient, index }: { esClient: ElasticsearchClient; foo: { properties: { name: { type: 'keyword' } } }, bar: { properties: { nomnom: { type: 'integer' } } }, baz: { properties: { title: { type: 'keyword' } } }, + 'legacy-url-alias': { + properties: { + targetNamespace: { type: 'text' }, + targetType: { type: 'text' }, + targetId: { type: 'text' }, + lastResolved: { type: 'date' }, + resolveCounter: { type: 'integer' }, + disabled: { type: 'boolean' }, + }, + }, + namespace: { type: 'keyword' }, + namespaces: { type: 'keyword' }, + originId: { type: 'keyword' }, + references: { + type: 'nested', + properties: { + name: { type: 'keyword' }, + type: { type: 'keyword' }, + id: { type: 'keyword' }, + }, + }, + coreMigrationVersion: { + type: 'keyword', + }, }; await esClient.indices.create({ index, @@ -369,23 +614,23 @@ async function createDocs({ async function migrateIndex({ esClient, index, - migrations, + savedObjectTypes, mappingProperties, obsoleteIndexTemplatePattern, }: { esClient: ElasticsearchClient; index: string; - migrations: Record; + savedObjectTypes: SavedObjectsType[]; mappingProperties: SavedObjectsTypeMappingDefinitions; obsoleteIndexTemplatePattern?: string; }) { const typeRegistry = new SavedObjectTypeRegistry(); - const types = migrationsToTypes(migrations); - types.forEach((type) => typeRegistry.registerType(type)); + savedObjectTypes.forEach((type) => typeRegistry.registerType(type)); const documentMigrator = new DocumentMigrator({ - kibanaVersion: '99.9.9', + kibanaVersion: KIBANA_VERSION, typeRegistry, + minimumConvertVersion: '0.0.0', // bypass the restriction of a minimum version of 8.0.0 for these integration tests log: getLogMock(), }); @@ -395,6 +640,7 @@ async function migrateIndex({ client: createMigrationEsClient(esClient, getLogMock()), documentMigrator, index, + kibanaVersion: KIBANA_VERSION, obsoleteIndexTemplatePattern, mappingProperties, batchSize: 10, @@ -407,18 +653,6 @@ async function migrateIndex({ return await migrator.migrate(); } -function migrationsToTypes( - migrations: Record -): SavedObjectsType[] { - return Object.entries(migrations).map(([type, migrationsMap]) => ({ - name: type, - hidden: false, - namespaceType: 'single', - mappings: { properties: {} }, - migrations: { ...migrationsMap }, - })); -} - async function fetchDocs(esClient: ElasticsearchClient, index: string) { const { body } = await esClient.search>({ index }); @@ -427,5 +661,9 @@ async function fetchDocs(esClient: ElasticsearchClient, index: string) { ...h._source, id: h._id, })) - .sort((a, b) => a.id.localeCompare(b.id)); + .sort(sortByTypeAndId); +} + +function sortByTypeAndId(a: { type: string; id: string }, b: { type: string; id: string }) { + return a.type.localeCompare(b.type) || a.id.localeCompare(b.id); } diff --git a/test/api_integration/apis/saved_objects/resolve.ts b/test/api_integration/apis/saved_objects/resolve.ts new file mode 100644 index 000000000000..fb8baef465f3 --- /dev/null +++ b/test/api_integration/apis/saved_objects/resolve.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import expect from '@kbn/expect'; +import type { FtrProviderContext } from '../../ftr_provider_context'; +import { getKibanaVersion } from './lib/saved_objects_test_utils'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('es'); + const esArchiver = getService('esArchiver'); + + describe('resolve', () => { + let KIBANA_VERSION: string; + + before(async () => { + KIBANA_VERSION = await getKibanaVersion(getService); + }); + + describe('with kibana index', () => { + before(() => esArchiver.load('saved_objects/basic')); + after(() => esArchiver.unload('saved_objects/basic')); + + it('should return 200', async () => + await supertest + .get(`/api/saved_objects/resolve/visualization/dd7caf20-9efd-11e7-acb3-3dab96693fab`) + .expect(200) + .then((resp) => { + expect(resp.body).to.eql({ + saved_object: { + id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab', + type: 'visualization', + updated_at: '2017-09-21T18:51:23.794Z', + version: resp.body.saved_object.version, + migrationVersion: resp.body.saved_object.migrationVersion, + coreMigrationVersion: KIBANA_VERSION, + attributes: { + title: 'Count of requests', + description: '', + version: 1, + // cheat for some of the more complex attributes + visState: resp.body.saved_object.attributes.visState, + uiStateJSON: resp.body.saved_object.attributes.uiStateJSON, + kibanaSavedObjectMeta: resp.body.saved_object.attributes.kibanaSavedObjectMeta, + }, + references: [ + { + type: 'index-pattern', + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + id: '91200a00-9efd-11e7-acb3-3dab96693fab', + }, + ], + namespaces: ['default'], + }, + outcome: 'exactMatch', + }); + expect(resp.body.saved_object.migrationVersion).to.be.ok(); + })); + + describe('doc does not exist', () => { + it('should return same generic error as when index does not exist', async () => + await supertest + .get(`/api/saved_objects/resolve/visualization/foobar`) + .expect(404) + .then((resp) => { + expect(resp.body).to.eql({ + error: 'Not Found', + message: 'Saved object [visualization/foobar] not found', + statusCode: 404, + }); + })); + }); + }); + + describe('without kibana index', () => { + before( + async () => + // just in case the kibana server has recreated it + await es.indices.delete({ index: '.kibana' }, { ignore: [404] }) + ); + + it('should return basic 404 without mentioning index', async () => + await supertest + .get('/api/saved_objects/resolve/visualization/dd7caf20-9efd-11e7-acb3-3dab96693fab') + .expect(404) + .then((resp) => { + expect(resp.body).to.eql({ + error: 'Not Found', + message: + 'Saved object [visualization/dd7caf20-9efd-11e7-acb3-3dab96693fab] not found', + statusCode: 404, + }); + })); + }); + }); +} diff --git a/test/api_integration/apis/saved_objects/resolve_import_errors.ts b/test/api_integration/apis/saved_objects/resolve_import_errors.ts index 5f3929f26aba..3686c46b229b 100644 --- a/test/api_integration/apis/saved_objects/resolve_import_errors.ts +++ b/test/api_integration/apis/saved_objects/resolve_import_errors.ts @@ -46,6 +46,7 @@ export default function ({ getService }: FtrProviderContext) { expect(resp.body).to.eql({ success: true, successCount: 0, + warnings: [], }); }); }); @@ -84,6 +85,7 @@ export default function ({ getService }: FtrProviderContext) { { ...visualization, overwrite: true }, { ...dashboard, overwrite: true }, ], + warnings: [], }); }); }); @@ -125,6 +127,7 @@ export default function ({ getService }: FtrProviderContext) { error: { type: 'unsupported_type' }, }, ], + warnings: [], }); }); }); @@ -198,6 +201,7 @@ export default function ({ getService }: FtrProviderContext) { }, }, ], + warnings: [], }); }); }); @@ -215,7 +219,7 @@ export default function ({ getService }: FtrProviderContext) { .attach('file', join(__dirname, '../../fixtures/import.ndjson')) .expect(200) .then((resp) => { - expect(resp.body).to.eql({ success: true, successCount: 0 }); + expect(resp.body).to.eql({ success: true, successCount: 0, warnings: [] }); }); }); @@ -253,6 +257,7 @@ export default function ({ getService }: FtrProviderContext) { { ...visualization, overwrite: true }, { ...dashboard, overwrite: true }, ], + warnings: [], }); }); }); @@ -277,6 +282,7 @@ export default function ({ getService }: FtrProviderContext) { success: true, successCount: 1, successResults: [{ ...visualization, overwrite: true }], + warnings: [], }); }); }); @@ -328,6 +334,7 @@ export default function ({ getService }: FtrProviderContext) { meta: { title: 'My favorite vis', icon: 'visualizeApp' }, }, ], + warnings: [], }); }); await supertest diff --git a/test/api_integration/apis/saved_objects_management/find.ts b/test/api_integration/apis/saved_objects_management/find.ts index d7b486e8ab5c..acc01c73de67 100644 --- a/test/api_integration/apis/saved_objects_management/find.ts +++ b/test/api_integration/apis/saved_objects_management/find.ts @@ -14,8 +14,17 @@ export default function ({ getService }: FtrProviderContext) { const es = getService('es'); const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); describe('find', () => { + let KIBANA_VERSION: string; + + before(async () => { + KIBANA_VERSION = await kibanaServer.version.get(); + expect(typeof KIBANA_VERSION).to.eql('string'); + expect(KIBANA_VERSION.length).to.be.greaterThan(0); + }); + describe('with kibana index', () => { before(() => esArchiver.load('saved_objects/basic')); after(() => esArchiver.unload('saved_objects/basic')); @@ -38,6 +47,7 @@ export default function ({ getService }: FtrProviderContext) { title: 'Count of requests', }, migrationVersion: resp.body.saved_objects[0].migrationVersion, + coreMigrationVersion: KIBANA_VERSION, namespaces: ['default'], references: [ { diff --git a/test/examples/config.js b/test/examples/config.js index a720899a637d..aab71cb30501 100644 --- a/test/examples/config.js +++ b/test/examples/config.js @@ -19,6 +19,7 @@ export default async function ({ readConfigFile }) { require.resolve('./ui_actions'), require.resolve('./state_sync'), require.resolve('./routing'), + require.resolve('./expressions_explorer'), ], services: { ...functionalConfig.get('services'), diff --git a/test/examples/expressions_explorer/expressions.ts b/test/examples/expressions_explorer/expressions.ts new file mode 100644 index 000000000000..7261564e6db3 --- /dev/null +++ b/test/examples/expressions_explorer/expressions.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import expect from '@kbn/expect'; + +import { PluginFunctionalProviderContext } from 'test/plugin_functional/services'; + +// eslint-disable-next-line import/no-default-export +export default function ({ getService }: PluginFunctionalProviderContext) { + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const browser = getService('browser'); + + describe('', () => { + it('runs expression', async () => { + await retry.try(async () => { + const text = await testSubjects.getVisibleText('expressionResult'); + expect(text).to.be( + '{\n "type": "error",\n "error": {\n "message": "Function markdown could not be found.",\n "name": "fn not found"\n }\n}' + ); + }); + }); + + it('renders expression', async () => { + await retry.try(async () => { + const text = await testSubjects.getVisibleText('expressionRender'); + expect(text).to.be('Function markdown could not be found.'); + }); + }); + + it('emits an action and navigates', async () => { + await testSubjects.click('testExpressionButton'); + await retry.try(async () => { + const text = await browser.getCurrentUrl(); + expect(text).to.be('https://www.google.com/?gws_rd=ssl'); + }); + }); + }); +} diff --git a/test/examples/expressions_explorer/index.ts b/test/examples/expressions_explorer/index.ts new file mode 100644 index 000000000000..77d2a594c0f2 --- /dev/null +++ b/test/examples/expressions_explorer/index.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { PluginFunctionalProviderContext } from 'test/plugin_functional/services'; + +// eslint-disable-next-line import/no-default-export +export default function ({ + getService, + getPageObjects, + loadTestFile, +}: PluginFunctionalProviderContext) { + const browser = getService('browser'); + const PageObjects = getPageObjects(['common', 'header']); + + describe('expressions explorer', function () { + before(async () => { + await browser.setWindowSize(1300, 900); + await PageObjects.common.navigateToApp('expressionsExplorer'); + }); + + loadTestFile(require.resolve('./expressions')); + }); +} diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/data.json b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/data.json new file mode 100644 index 000000000000..7f4043958bc8 --- /dev/null +++ b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/data.json @@ -0,0 +1,149 @@ +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "test-export-transform:type_1-obj_1", + "source": { + "test-export-transform": { + "title": "test_1-obj_1", + "enabled": true + }, + "type": "test-export-transform", + "migrationVersion": {}, + "updated_at": "2018-12-21T00:43:07.096Z" + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "test-export-transform:type_1-obj_2", + "source": { + "test-export-transform": { + "title": "test_1-obj_2", + "enabled": true + }, + "type": "test-export-transform", + "migrationVersion": {}, + "updated_at": "2018-12-21T00:43:07.096Z" + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "test-export-add:type_2-obj_1", + "source": { + "test-export-add": { + "title": "test_2-obj_1" + }, + "type": "test-export-add", + "migrationVersion": {}, + "updated_at": "2018-12-21T00:43:07.096Z" + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "test-export-add:type_2-obj_2", + "source": { + "test-export-add": { + "title": "test_2-obj_2" + }, + "type": "test-export-add", + "migrationVersion": {}, + "updated_at": "2018-12-21T00:43:07.096Z" + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "test-export-add-dep:type_dep-obj_1", + "source": { + "test-export-add-dep": { + "title": "type_dep-obj_1" + }, + "type": "test-export-add-dep", + "migrationVersion": {}, + "updated_at": "2018-12-21T00:43:07.096Z", + "references": [ + { + "type": "test-export-add", + "id": "type_2-obj_1" + } + ] + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "test-export-add-dep:type_dep-obj_2", + "source": { + "test-export-add-dep": { + "title": "type_dep-obj_2" + }, + "type": "test-export-add-dep", + "migrationVersion": {}, + "updated_at": "2018-12-21T00:43:07.096Z", + "references": [ + { + "type": "test-export-add", + "id": "type_2-obj_2" + } + ] + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "test-export-invalid-transform:type_3-obj_1", + "source": { + "test-export-invalid-transform": { + "title": "test_2-obj_1" + }, + "type": "test-export-invalid-transform", + "migrationVersion": {}, + "updated_at": "2018-12-21T00:43:07.096Z" + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "type": "doc", + "id": "test-export-transform-error:type_4-obj_1", + "source": { + "test-export-transform-error": { + "title": "test_2-obj_1" + }, + "type": "test-export-transform-error", + "migrationVersion": {}, + "updated_at": "2018-12-21T00:43:07.096Z" + } + } +} diff --git a/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json new file mode 100644 index 000000000000..d85125efd672 --- /dev/null +++ b/test/functional/fixtures/es_archiver/saved_objects_management/export_transform/mappings.json @@ -0,0 +1,499 @@ +{ + "type": "index", + "value": { + "index": ".kibana", + "settings": { + "index": { + "number_of_shards": "1", + "auto_expand_replicas": "0-1", + "number_of_replicas": "0" + } + }, + "mappings": { + "dynamic": "strict", + "properties": { + "test-export-transform": { + "properties": { + "title": { "type": "text" }, + "enabled": { "type": "boolean" } + } + }, + "test-export-add": { + "properties": { + "title": { "type": "text" } + } + }, + "test-export-add-dep": { + "properties": { + "title": { "type": "text" } + } + }, + "test-export-transform-error": { + "properties": { + "title": { "type": "text" } + } + }, + "test-export-invalid-transform": { + "properties": { + "title": { "type": "text" } + } + }, + "apm-telemetry": { + "properties": { + "has_any_services": { + "type": "boolean" + }, + "services_per_agent": { + "properties": { + "go": { + "type": "long", + "null_value": 0 + }, + "java": { + "type": "long", + "null_value": 0 + }, + "js-base": { + "type": "long", + "null_value": 0 + }, + "nodejs": { + "type": "long", + "null_value": 0 + }, + "python": { + "type": "long", + "null_value": 0 + }, + "ruby": { + "type": "long", + "null_value": 0 + } + } + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "id": { + "type": "text", + "index": false + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + } + } + }, + "config": { + "dynamic": "true", + "properties": { + "accessibility:disableAnimations": { + "type": "boolean" + }, + "buildNum": { + "type": "keyword" + }, + "dateFormat:tz": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "defaultIndex": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "telemetry:optIn": { + "type": "boolean" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "optionsJSON": { + "type": "text" + }, + "panelsJSON": { + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "type": "keyword" + }, + "pause": { + "type": "boolean" + }, + "section": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, + "timeFrom": { + "type": "keyword" + }, + "timeRestore": { + "type": "boolean" + }, + "timeTo": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "map": { + "properties": { + "bounds": { + "type": "geo_shape", + "tree": "quadtree" + }, + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "index-pattern": { + "properties": { + "fieldFormatMap": { + "type": "text" + }, + "fields": { + "type": "text" + }, + "intervalName": { + "type": "keyword" + }, + "notExpandable": { + "type": "boolean" + }, + "sourceFilters": { + "type": "text" + }, + "timeFieldName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "typeMeta": { + "type": "keyword" + } + } + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "index-pattern": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "space": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + } + } + }, + "namespace": { + "type": "keyword" + }, + "search": { + "properties": { + "columns": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "sort": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "server": { + "properties": { + "uuid": { + "type": "keyword" + } + } + }, + "space": { + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "initials": { + "type": "keyword" + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 2048 + } + } + } + } + }, + "spaceId": { + "type": "keyword" + }, + "telemetry": { + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 2048 + } + } + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "savedSearchId": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + } + } + } + } +} diff --git a/test/functional/page_objects/management/saved_objects_page.ts b/test/functional/page_objects/management/saved_objects_page.ts index e29a9abadd88..1cdf76ad58ef 100644 --- a/test/functional/page_objects/management/saved_objects_page.ts +++ b/test/functional/page_objects/management/saved_objects_page.ts @@ -283,6 +283,22 @@ export function SavedObjectsPageProvider({ getService, getPageObjects }: FtrProv await testSubjects.click('confirmModalConfirmButton'); await this.waitTableIsLoaded(); } + + async getImportWarnings() { + const elements = await testSubjects.findAll('importSavedObjectsWarning'); + return Promise.all( + elements.map(async (element) => { + const message = await element + .findByClassName('euiCallOutHeader__title') + .then((titleEl) => titleEl.getVisibleText()); + const buttons = await element.findAllByClassName('euiButton'); + return { + message, + type: buttons.length ? 'action_required' : 'simple', + }; + }) + ); + } } return new SavedObjectsPage(); diff --git a/test/functional/services/common/browser.ts b/test/functional/services/common/browser.ts index b9f7ae8da465..635fde6dad72 100644 --- a/test/functional/services/common/browser.ts +++ b/test/functional/services/common/browser.ts @@ -6,6 +6,7 @@ * Public License, v 1. */ +import { delay } from 'bluebird'; import { cloneDeepWith } from 'lodash'; import { Key, Origin } from 'selenium-webdriver'; // @ts-ignore internal modules are not typed @@ -298,11 +299,13 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { dispatchEvent(target, dropEvent, dragStartEvent.dataTransfer); const dragEndEvent = createEvent('dragend'); dispatchEvent(origin, dragEndEvent, dropEvent.dataTransfer); - }, 50); + }, 100); `, from, to ); + // wait for 150ms to make sure the script has run + await delay(150); } /** diff --git a/test/plugin_functional/config.ts b/test/plugin_functional/config.ts index 9822ba3bee8d..2842a18c9445 100644 --- a/test/plugin_functional/config.ts +++ b/test/plugin_functional/config.ts @@ -29,6 +29,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { require.resolve('./test_suites/doc_views'), require.resolve('./test_suites/application_links'), require.resolve('./test_suites/data_plugin'), + require.resolve('./test_suites/saved_objects_management'), ], services: { ...functionalConfig.get('services'), diff --git a/test/plugin_functional/plugins/core_plugin_a/server/index.ts b/test/plugin_functional/plugins/core_plugin_a/server/index.ts index 2134b88eae84..f98b5772c7e5 100644 --- a/test/plugin_functional/plugins/core_plugin_a/server/index.ts +++ b/test/plugin_functional/plugins/core_plugin_a/server/index.ts @@ -7,6 +7,6 @@ */ import { CorePluginAPlugin } from './plugin'; -export { PluginARequestContext } from './plugin'; +export { PluginAApiRequestContext } from './plugin'; export const plugin = () => new CorePluginAPlugin(); diff --git a/test/plugin_functional/plugins/core_plugin_a/server/plugin.ts b/test/plugin_functional/plugins/core_plugin_a/server/plugin.ts index b49336f2d3e1..367168afdc1d 100644 --- a/test/plugin_functional/plugins/core_plugin_a/server/plugin.ts +++ b/test/plugin_functional/plugins/core_plugin_a/server/plugin.ts @@ -6,26 +6,27 @@ * Public License, v 1. */ -import { Plugin, CoreSetup } from 'kibana/server'; +import type { Plugin, CoreSetup, RequestHandlerContext } from 'kibana/server'; -export interface PluginARequestContext { +export interface PluginAApiRequestContext { ping: () => Promise; } -declare module 'kibana/server' { - interface RequestHandlerContext { - pluginA?: PluginARequestContext; - } +interface PluginARequstHandlerContext extends RequestHandlerContext { + pluginA: PluginAApiRequestContext; } export class CorePluginAPlugin implements Plugin { public setup(core: CoreSetup, deps: {}) { - core.http.registerRouteHandlerContext('pluginA', (context) => { - return { - ping: () => - context.core.elasticsearch.legacy.client.callAsInternalUser('ping') as Promise, - }; - }); + core.http.registerRouteHandlerContext( + 'pluginA', + (context) => { + return { + ping: () => + context.core.elasticsearch.legacy.client.callAsInternalUser('ping') as Promise, + }; + } + ); } public start() {} diff --git a/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts b/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts index 528f99691588..fba19a46fc7e 100644 --- a/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts +++ b/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts @@ -6,19 +6,17 @@ * Public License, v 1. */ -import { Plugin, CoreSetup } from 'kibana/server'; +import { Plugin, CoreSetup, RequestHandlerContext } from 'kibana/server'; import { schema } from '@kbn/config-schema'; -import { PluginARequestContext } from '../../core_plugin_a/server'; +import { PluginAApiRequestContext } from '../../core_plugin_a/server'; -declare module 'kibana/server' { - interface RequestHandlerContext { - pluginA?: PluginARequestContext; - } +interface PluginBContext extends RequestHandlerContext { + pluginA: PluginAApiRequestContext; } export class CorePluginBPlugin implements Plugin { public setup(core: CoreSetup, deps: {}) { - const router = core.http.createRouter(); + const router = core.http.createRouter(); router.get({ path: '/core_plugin_b', validate: false }, async (context, req, res) => { if (!context.pluginA) return res.internalError({ body: 'pluginA is disabled' }); const response = await context.pluginA.ping(); diff --git a/test/plugin_functional/plugins/core_plugin_route_timeouts/server/index.ts b/test/plugin_functional/plugins/core_plugin_route_timeouts/server/index.ts index 6569d89f9d0b..5b58c308d509 100644 --- a/test/plugin_functional/plugins/core_plugin_route_timeouts/server/index.ts +++ b/test/plugin_functional/plugins/core_plugin_route_timeouts/server/index.ts @@ -7,6 +7,5 @@ */ import { CorePluginRouteTimeoutsPlugin } from './plugin'; -export { PluginARequestContext } from './plugin'; export const plugin = () => new CorePluginRouteTimeoutsPlugin(); diff --git a/test/plugin_functional/plugins/core_plugin_route_timeouts/server/plugin.ts b/test/plugin_functional/plugins/core_plugin_route_timeouts/server/plugin.ts index e9e97288a115..a6d964fdeb7d 100644 --- a/test/plugin_functional/plugins/core_plugin_route_timeouts/server/plugin.ts +++ b/test/plugin_functional/plugins/core_plugin_route_timeouts/server/plugin.ts @@ -9,16 +9,6 @@ import { Plugin, CoreSetup } from 'kibana/server'; import { schema } from '@kbn/config-schema'; -export interface PluginARequestContext { - ping: () => Promise; -} - -declare module 'kibana/server' { - interface RequestHandlerContext { - pluginA?: PluginARequestContext; - } -} - export class CorePluginRouteTimeoutsPlugin implements Plugin { public setup(core: CoreSetup, deps: {}) { const { http } = core; diff --git a/test/plugin_functional/plugins/saved_object_export_transforms/kibana.json b/test/plugin_functional/plugins/saved_object_export_transforms/kibana.json new file mode 100644 index 000000000000..40b4c12f58e6 --- /dev/null +++ b/test/plugin_functional/plugins/saved_object_export_transforms/kibana.json @@ -0,0 +1,8 @@ +{ + "id": "savedObjectExportTransforms", + "version": "0.0.1", + "kibanaVersion": "kibana", + "configPath": ["saved_object_export_transforms"], + "server": true, + "ui": false +} diff --git a/test/plugin_functional/plugins/saved_object_export_transforms/package.json b/test/plugin_functional/plugins/saved_object_export_transforms/package.json new file mode 100644 index 000000000000..0ced0a3b2128 --- /dev/null +++ b/test/plugin_functional/plugins/saved_object_export_transforms/package.json @@ -0,0 +1,14 @@ +{ + "name": "saved_object_export_transforms", + "version": "1.0.0", + "main": "target/test/plugin_functional/plugins/saved_object_export_transforms", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "kbn": "node ../../../../scripts/kbn.js", + "build": "rm -rf './target' && ../../../../node_modules/.bin/tsc" + } +} \ No newline at end of file diff --git a/test/plugin_functional/plugins/saved_object_export_transforms/server/index.ts b/test/plugin_functional/plugins/saved_object_export_transforms/server/index.ts new file mode 100644 index 000000000000..f87a7d7d2e6a --- /dev/null +++ b/test/plugin_functional/plugins/saved_object_export_transforms/server/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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { SavedObjectExportTransformsPlugin } from './plugin'; + +export const plugin = () => new SavedObjectExportTransformsPlugin(); diff --git a/test/plugin_functional/plugins/saved_object_export_transforms/server/plugin.ts b/test/plugin_functional/plugins/saved_object_export_transforms/server/plugin.ts new file mode 100644 index 000000000000..acbf454a9309 --- /dev/null +++ b/test/plugin_functional/plugins/saved_object_export_transforms/server/plugin.ts @@ -0,0 +1,141 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { Plugin, CoreSetup } from 'kibana/server'; + +export class SavedObjectExportTransformsPlugin implements Plugin { + public setup({ savedObjects, getStartServices }: CoreSetup, deps: {}) { + const savedObjectStartContractPromise = getStartServices().then( + ([{ savedObjects: savedObjectsStart }]) => savedObjectsStart + ); + + // example of a SO type that will mutates its properties + // during the export transform + savedObjects.registerType({ + name: 'test-export-transform', + hidden: false, + namespaceType: 'single', + mappings: { + properties: { + title: { type: 'text' }, + enabled: { + type: 'boolean', + }, + }, + }, + management: { + defaultSearchField: 'title', + importableAndExportable: true, + getTitle: (obj) => obj.attributes.title, + onExport: (ctx, objs) => { + return objs.map((obj) => ({ + ...obj, + attributes: { + ...obj.attributes, + enabled: false, + }, + })); + }, + }, + }); + + // example of a SO type that will add additional objects + // to the export during the export transform + savedObjects.registerType({ + name: 'test-export-add', + hidden: false, + namespaceType: 'single', + mappings: { + properties: { + title: { type: 'text' }, + }, + }, + management: { + defaultSearchField: 'title', + importableAndExportable: true, + getTitle: (obj) => obj.attributes.title, + onExport: async (ctx, objs) => { + const { getScopedClient } = await savedObjectStartContractPromise; + const client = getScopedClient(ctx.request); + const objRefs = objs.map(({ id, type }) => ({ id, type })); + const depResponse = await client.find({ + type: 'test-export-add-dep', + hasReference: objRefs, + }); + return [...objs, ...depResponse.saved_objects]; + }, + }, + }); + + // dependency of `test_export_transform_2` that will be included + // when exporting them + savedObjects.registerType({ + name: 'test-export-add-dep', + hidden: false, + namespaceType: 'single', + mappings: { + properties: { + title: { type: 'text' }, + }, + }, + management: { + defaultSearchField: 'title', + importableAndExportable: true, + getTitle: (obj) => obj.attributes.title, + }, + }); + + ///////////// + ///////////// + // example of a SO type that will throw an object-transform-error + savedObjects.registerType({ + name: 'test-export-transform-error', + hidden: false, + namespaceType: 'single', + mappings: { + properties: { + title: { type: 'text' }, + }, + }, + management: { + defaultSearchField: 'title', + importableAndExportable: true, + getTitle: (obj) => obj.attributes.title, + onExport: (ctx, objs) => { + throw new Error('Error during transform'); + }, + }, + }); + + // example of a SO type that will throw an invalid-transform-error + savedObjects.registerType({ + name: 'test-export-invalid-transform', + hidden: false, + namespaceType: 'single', + mappings: { + properties: { + title: { type: 'text' }, + }, + }, + management: { + defaultSearchField: 'title', + importableAndExportable: true, + getTitle: (obj) => obj.attributes.title, + onExport: (ctx, objs) => { + return objs.map((obj) => ({ + ...obj, + id: `${obj.id}-mutated`, + })); + }, + }, + }); + } + + public start() {} + public stop() {} +} diff --git a/test/plugin_functional/plugins/saved_object_export_transforms/tsconfig.json b/test/plugin_functional/plugins/saved_object_export_transforms/tsconfig.json new file mode 100644 index 000000000000..da457c9ba32f --- /dev/null +++ b/test/plugin_functional/plugins/saved_object_export_transforms/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "server/**/*.ts", + "../../../../typings/**/*", + ], + "exclude": [], + "references": [ + { "path": "../../../../src/core/tsconfig.json" } + ] +} diff --git a/test/plugin_functional/plugins/saved_object_import_warnings/kibana.json b/test/plugin_functional/plugins/saved_object_import_warnings/kibana.json new file mode 100644 index 000000000000..947f840560eb --- /dev/null +++ b/test/plugin_functional/plugins/saved_object_import_warnings/kibana.json @@ -0,0 +1,8 @@ +{ + "id": "savedObjectImportWarnings", + "version": "0.0.1", + "kibanaVersion": "kibana", + "configPath": ["saved_object_import_warnings"], + "server": true, + "ui": false +} diff --git a/test/plugin_functional/plugins/saved_object_import_warnings/package.json b/test/plugin_functional/plugins/saved_object_import_warnings/package.json new file mode 100644 index 000000000000..0c3cb50bd0b1 --- /dev/null +++ b/test/plugin_functional/plugins/saved_object_import_warnings/package.json @@ -0,0 +1,14 @@ +{ + "name": "saved_object_import_warnings", + "version": "1.0.0", + "main": "target/test/plugin_functional/plugins/saved_object_import_warnings", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "kbn": "node ../../../../scripts/kbn.js", + "build": "rm -rf './target' && ../../../../node_modules/.bin/tsc" + } +} \ No newline at end of file diff --git a/test/plugin_functional/plugins/saved_object_import_warnings/server/index.ts b/test/plugin_functional/plugins/saved_object_import_warnings/server/index.ts new file mode 100644 index 000000000000..9a7209480cc1 --- /dev/null +++ b/test/plugin_functional/plugins/saved_object_import_warnings/server/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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { SavedObjectImportWarningsPlugin } from './plugin'; + +export const plugin = () => new SavedObjectImportWarningsPlugin(); diff --git a/test/plugin_functional/plugins/saved_object_import_warnings/server/plugin.ts b/test/plugin_functional/plugins/saved_object_import_warnings/server/plugin.ts new file mode 100644 index 000000000000..5fc4e4aed9b9 --- /dev/null +++ b/test/plugin_functional/plugins/saved_object_import_warnings/server/plugin.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { Plugin, CoreSetup } from 'kibana/server'; + +export class SavedObjectImportWarningsPlugin implements Plugin { + public setup({ savedObjects }: CoreSetup, deps: {}) { + savedObjects.registerType({ + name: 'test_import_warning_1', + hidden: false, + namespaceType: 'single', + mappings: { + properties: { + title: { type: 'text' }, + }, + }, + management: { + defaultSearchField: 'title', + importableAndExportable: true, + getTitle: (obj) => obj.attributes.title, + onImport: (objects) => { + return { + warnings: [{ type: 'simple', message: 'warning for test_import_warning_1' }], + }; + }, + }, + }); + + savedObjects.registerType({ + name: 'test_import_warning_2', + hidden: false, + namespaceType: 'single', + mappings: { + properties: { + title: { type: 'text' }, + }, + }, + management: { + defaultSearchField: 'title', + importableAndExportable: true, + getTitle: (obj) => obj.attributes.title, + onImport: (objects) => { + return { + warnings: [ + { + type: 'action_required', + message: 'warning for test_import_warning_2', + actionPath: '/some/url', + }, + ], + }; + }, + }, + }); + } + + public start() {} + public stop() {} +} diff --git a/test/plugin_functional/plugins/saved_object_import_warnings/tsconfig.json b/test/plugin_functional/plugins/saved_object_import_warnings/tsconfig.json new file mode 100644 index 000000000000..3d9d8ca9451d --- /dev/null +++ b/test/plugin_functional/plugins/saved_object_import_warnings/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../../typings/**/*", + ], + "exclude": [], + "references": [ + { "path": "../../../../src/core/tsconfig.json" } + ] +} diff --git a/test/plugin_functional/test_suites/saved_objects_management/export_transform.ts b/test/plugin_functional/test_suites/saved_objects_management/export_transform.ts new file mode 100644 index 000000000000..33c4ddc38be0 --- /dev/null +++ b/test/plugin_functional/test_suites/saved_objects_management/export_transform.ts @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import expect from '@kbn/expect'; +import type { SavedObject } from '../../../../src/core/types'; +import { PluginFunctionalProviderContext } from '../../services'; + +function parseNdJson(input: string): Array> { + return input.split('\n').map((str) => JSON.parse(str)); +} + +export default function ({ getService }: PluginFunctionalProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + describe('export transforms', () => { + before(async () => { + await esArchiver.load( + '../functional/fixtures/es_archiver/saved_objects_management/export_transform' + ); + }); + + after(async () => { + await esArchiver.unload( + '../functional/fixtures/es_archiver/saved_objects_management/export_transform' + ); + }); + + it('allows to mutate the objects during an export', async () => { + await supertest + .post('/api/saved_objects/_export') + .set('kbn-xsrf', 'true') + .send({ + type: ['test-export-transform'], + excludeExportDetails: true, + }) + .expect(200) + .then((resp) => { + const objects = parseNdJson(resp.text); + expect(objects.map((obj) => ({ id: obj.id, enabled: obj.attributes.enabled }))).to.eql([ + { + id: 'type_1-obj_1', + enabled: false, + }, + { + id: 'type_1-obj_2', + enabled: false, + }, + ]); + }); + }); + + it('allows to add additional objects to an export', async () => { + await supertest + .post('/api/saved_objects/_export') + .set('kbn-xsrf', 'true') + .send({ + objects: [ + { + type: 'test-export-add', + id: 'type_2-obj_1', + }, + ], + excludeExportDetails: true, + }) + .expect(200) + .then((resp) => { + const objects = parseNdJson(resp.text); + expect(objects.map((obj) => obj.id)).to.eql(['type_2-obj_1', 'type_dep-obj_1']); + }); + }); + + it('allows to add additional objects to an export when exporting by type', async () => { + await supertest + .post('/api/saved_objects/_export') + .set('kbn-xsrf', 'true') + .send({ + type: ['test-export-add'], + excludeExportDetails: true, + }) + .expect(200) + .then((resp) => { + const objects = parseNdJson(resp.text); + expect(objects.map((obj) => obj.id)).to.eql([ + 'type_2-obj_1', + 'type_2-obj_2', + 'type_dep-obj_1', + 'type_dep-obj_2', + ]); + }); + }); + + it('returns a 400 when the type causes a transform error', async () => { + await supertest + .post('/api/saved_objects/_export') + .set('kbn-xsrf', 'true') + .send({ + type: ['test-export-transform-error'], + excludeExportDetails: true, + }) + .expect(400) + .then((resp) => { + const { attributes, ...error } = resp.body; + expect(error).to.eql({ + error: 'Bad Request', + message: 'Error transforming objects to export', + statusCode: 400, + }); + expect(attributes.cause).to.eql('Error during transform'); + expect(attributes.objects.map((obj: any) => obj.id)).to.eql(['type_4-obj_1']); + }); + }); + + it('returns a 400 when the type causes an invalid transform', async () => { + await supertest + .post('/api/saved_objects/_export') + .set('kbn-xsrf', 'true') + .send({ + type: ['test-export-invalid-transform'], + excludeExportDetails: true, + }) + .expect(400) + .then((resp) => { + expect(resp.body).to.eql({ + error: 'Bad Request', + message: 'Invalid transform performed on objects to export', + statusCode: 400, + attributes: { + objectKeys: ['test-export-invalid-transform|type_3-obj_1'], + }, + }); + }); + }); + }); +} diff --git a/test/plugin_functional/test_suites/saved_objects_management/exports/_import_both_types.ndjson b/test/plugin_functional/test_suites/saved_objects_management/exports/_import_both_types.ndjson new file mode 100644 index 000000000000..d72511238e38 --- /dev/null +++ b/test/plugin_functional/test_suites/saved_objects_management/exports/_import_both_types.ndjson @@ -0,0 +1,2 @@ +{"attributes":{"title": "Test Import warnings 1"},"id":"08ff1d6a-a2e7-11e7-bb30-2e3be9be6a73","migrationVersion":{"visualization":"7.0.0"},"references":[],"type":"test_import_warning_1","version":1} +{"attributes":{"title": "Test Import warnings 2"},"id":"77bb1e6a-a2e7-11e7-bb30-2e3be9be6a73","migrationVersion":{"visualization":"7.0.0"},"references":[],"type":"test_import_warning_2","version":1} diff --git a/test/plugin_functional/test_suites/saved_objects_management/exports/_import_type_1.ndjson b/test/plugin_functional/test_suites/saved_objects_management/exports/_import_type_1.ndjson new file mode 100644 index 000000000000..f24f73880190 --- /dev/null +++ b/test/plugin_functional/test_suites/saved_objects_management/exports/_import_type_1.ndjson @@ -0,0 +1 @@ +{"attributes":{"title": "Test Import warnings 1"},"id":"08ff1d6a-a2e7-11e7-bb30-2e3be9be6a73","migrationVersion":{"visualization":"7.0.0"},"references":[],"type":"test_import_warning_1","version":1} diff --git a/test/plugin_functional/test_suites/saved_objects_management/exports/_import_type_2.ndjson b/test/plugin_functional/test_suites/saved_objects_management/exports/_import_type_2.ndjson new file mode 100644 index 000000000000..15efd8a6ce03 --- /dev/null +++ b/test/plugin_functional/test_suites/saved_objects_management/exports/_import_type_2.ndjson @@ -0,0 +1 @@ +{"attributes":{"title": "Test Import warnings 2"},"id":"77bb1e6a-a2e7-11e7-bb30-2e3be9be6a73","migrationVersion":{"visualization":"7.0.0"},"references":[],"type":"test_import_warning_2","version":1} diff --git a/test/plugin_functional/test_suites/saved_objects_management/import_warnings.ts b/test/plugin_functional/test_suites/saved_objects_management/import_warnings.ts new file mode 100644 index 000000000000..71663b19b35c --- /dev/null +++ b/test/plugin_functional/test_suites/saved_objects_management/import_warnings.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import path from 'path'; +import expect from '@kbn/expect'; +import { PluginFunctionalProviderContext } from '../../services'; + +export default function ({ getPageObjects }: PluginFunctionalProviderContext) { + const PageObjects = getPageObjects(['common', 'settings', 'header', 'savedObjects']); + + describe('import warnings', () => { + beforeEach(async () => { + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaSavedObjects(); + }); + + it('should display simple warnings', async () => { + await PageObjects.savedObjects.importFile( + path.join(__dirname, 'exports', '_import_type_1.ndjson') + ); + + await PageObjects.savedObjects.checkImportSucceeded(); + const warnings = await PageObjects.savedObjects.getImportWarnings(); + + expect(warnings).to.eql([ + { + message: 'warning for test_import_warning_1', + type: 'simple', + }, + ]); + }); + + it('should display action warnings', async () => { + await PageObjects.savedObjects.importFile( + path.join(__dirname, 'exports', '_import_type_2.ndjson') + ); + + await PageObjects.savedObjects.checkImportSucceeded(); + const warnings = await PageObjects.savedObjects.getImportWarnings(); + + expect(warnings).to.eql([ + { + type: 'action_required', + message: 'warning for test_import_warning_2', + }, + ]); + }); + + it('should display warnings coming from multiple types', async () => { + await PageObjects.savedObjects.importFile( + path.join(__dirname, 'exports', '_import_both_types.ndjson') + ); + + await PageObjects.savedObjects.checkImportSucceeded(); + const warnings = await PageObjects.savedObjects.getImportWarnings(); + + expect(warnings).to.eql([ + { + message: 'warning for test_import_warning_1', + type: 'simple', + }, + { + type: 'action_required', + message: 'warning for test_import_warning_2', + }, + ]); + }); + }); +} diff --git a/test/plugin_functional/test_suites/saved_objects_management/index.ts b/test/plugin_functional/test_suites/saved_objects_management/index.ts new file mode 100644 index 000000000000..38c966901d8a --- /dev/null +++ b/test/plugin_functional/test_suites/saved_objects_management/index.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 + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { PluginFunctionalProviderContext } from '../../services'; + +export default function ({ loadTestFile }: PluginFunctionalProviderContext) { + describe('Saved Objects Management', function () { + loadTestFile(require.resolve('./export_transform')); + loadTestFile(require.resolve('./import_warnings')); + }); +} diff --git a/test/scripts/run_multiple_kibana_nodes.sh b/test/scripts/run_multiple_kibana_nodes.sh new file mode 100755 index 000000000000..f5661c19bed1 --- /dev/null +++ b/test/scripts/run_multiple_kibana_nodes.sh @@ -0,0 +1,86 @@ +# 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. + +#!/bin/bash + +# +# Script to run multiple kibana nodes in parallel on the same machine. +# Make sure to run the script from kibana root directory. Some functions depend on the jq command-line utility +# being installed. +# +# bash test/scripts/run_multiple_kibana_nodes.sh [options] +# functions: +# start [instances] [args] - start multiple kibanas (3 default) +# es [args] - run elasticsearch +# tail - show logs of all kibanas +# kill - kills all started kibana processes +# clean - clean up nohup files +# kibana_index - search .kibana index against es +# + +FN="$1" + +if [ "${FN}" == "kill" ]; then + echo "killing main processes" + for pid in $(cat processes.out); do kill -9 $pid; done + echo "killing trailing processes" + for pid in $(pgrep -f scripts/kibana); do kill -9 $pid; done + exit 0; +fi + +if [ "${FN}" == "tail" ]; then + tail -f nohup_* + exit 0; +fi + +if [ "${FN}" == "clean" ]; then + rm -r nohup_*.out + rm processes.out + exit 0; +fi + +if [ "${FN}" == "es" ]; then + ARGS="$2" + yarn es snapshot $ARGS + exit 0; +fi + +if [ "${FN}" == "kibana_index" ]; then + # search the kibana index + curl -XPOST http://elastic:changeme@localhost:9200/.kibana/_search -u elastic:changeme -d '' | jq + exit 0; +fi + +if [ "${FN}" == "start" ]; then + NUM="$2" + ARGS="$3" + if test ! "${NUM-}"; then + NUM=3 + fi + node scripts/build_kibana_platform_plugins --no-examples + rm processes.out + for i in $(seq 0 $(expr $NUM - 1)) + do + PORT="56${i}1" + PROXY="56${i}3" + echo "starting kibana on port $PORT" + nohup node scripts/kibana.js --dev.basePathProxyTarget=$PROXY --server.port=$PORT --dev --no-watch --no-optimizer --no-base-path $ARGS > nohup_$i.out & + PROCESS_ID=$! + echo "${PROCESS_ID}" >> processes.out + done + exit 0; +fi diff --git a/vars/kibanaPipeline.groovy b/vars/kibanaPipeline.groovy index 7991dd325215..93cb7a719bbe 100644 --- a/vars/kibanaPipeline.groovy +++ b/vars/kibanaPipeline.groovy @@ -89,6 +89,7 @@ def withFunctionalTestEnv(List additionalEnvs = [], Closure closure) { def esTransportPort = "61${parallelId}3" def fleetPackageRegistryPort = "61${parallelId}4" def alertingProxyPort = "61${parallelId}5" + def corsTestServerPort = "61${parallelId}6" def apmActive = githubPr.isPr() ? "false" : "true" withEnv([ @@ -100,6 +101,7 @@ def withFunctionalTestEnv(List additionalEnvs = [], Closure closure) { "TEST_KIBANA_URL=http://elastic:changeme@localhost:${kibanaPort}", "TEST_ES_URL=http://elastic:changeme@localhost:${esPort}", "TEST_ES_TRANSPORT_PORT=${esTransportPort}", + "TEST_CORS_SERVER_PORT=${corsTestServerPort}", "KBN_NP_PLUGINS_BUILT=true", "FLEET_PACKAGE_REGISTRY_PORT=${fleetPackageRegistryPort}", "ALERTING_PROXY_PORT=${alertingProxyPort}", diff --git a/x-pack/examples/alerting_example/tsconfig.json b/x-pack/examples/alerting_example/tsconfig.json index 95d42d40aceb..99e0f1f0e7c9 100644 --- a/x-pack/examples/alerting_example/tsconfig.json +++ b/x-pack/examples/alerting_example/tsconfig.json @@ -9,7 +9,7 @@ "public/**/*.tsx", "server/**/*.ts", "common/**/*.ts", - "../../../typings/**/*", + "../../typings/**/*", ], "exclude": [], "references": [ diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index c43cc20bd477..6c4857bff4e8 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -14,12 +14,13 @@ import { ActionsConfigType } from './types'; export type ActionsClient = PublicMethodsOf; export type ActionsAuthorization = PublicMethodsOf; -export { +export type { ActionsPlugin, ActionResult, ActionTypeExecutorOptions, ActionType, PreConfiguredAction, + ActionsApiRequestHandlerContext, } from './types'; export type { @@ -45,7 +46,7 @@ export type { TeamsActionParams, } from './builtin_action_types'; -export { PluginSetupContract, PluginStartContract } from './plugin'; +export type { PluginSetupContract, PluginStartContract } from './plugin'; export { asSavedObjectExecutionSource, asHttpRequestExecutionSource } from './lib'; diff --git a/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts b/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts index f22e87a58ec7..c4a57bbf32c1 100644 --- a/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts +++ b/x-pack/plugins/actions/server/lib/ensure_sufficient_license.ts @@ -6,9 +6,10 @@ import { ActionType } from '../types'; import { LICENSE_TYPE } from '../../../licensing/common/types'; import { ServerLogActionTypeId, IndexActionTypeId } from '../builtin_action_types'; -import { CASE_ACTION_TYPE_ID } from '../../../case/server'; import { ActionTypeConfig, ActionTypeSecrets, ActionTypeParams } from '../types'; +const CASE_ACTION_TYPE_ID = '.case'; + const ACTIONS_SCOPED_WITHIN_STACK = new Set([ ServerLogActionTypeId, IndexActionTypeId, diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index ff43b05b6d89..d1e40563c017 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -12,7 +12,7 @@ import { featuresPluginMock } from '../../features/server/mocks'; import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks'; import { taskManagerMock } from '../../task_manager/server/mocks'; import { eventLogMock } from '../../event_log/server/mocks'; -import { ActionType } from './types'; +import { ActionType, ActionsApiRequestHandlerContext } from './types'; import { ActionsConfig } from './config'; import { ActionsPlugin, @@ -73,7 +73,10 @@ describe('Actions Plugin', () => { }); expect(coreSetup.http.registerRouteHandlerContext).toHaveBeenCalledTimes(1); - const handler = coreSetup.http.registerRouteHandlerContext.mock.calls[0]; + const handler = coreSetup.http.registerRouteHandlerContext.mock.calls[0] as [ + string, + Function + ]; expect(handler[0]).toEqual('actions'); const actionsContextHandler = ((await handler[1]( @@ -91,7 +94,7 @@ describe('Actions Plugin', () => { } as unknown) as RequestHandlerContext, httpServerMock.createKibanaRequest(), httpServerMock.createResponseFactory() - )) as unknown) as RequestHandlerContext['actions']; + )) as unknown) as ActionsApiRequestHandlerContext; actionsContextHandler!.getActionsClient(); }); @@ -101,7 +104,10 @@ describe('Actions Plugin', () => { await plugin.setup(coreSetup as any, pluginsSetup); expect(coreSetup.http.registerRouteHandlerContext).toHaveBeenCalledTimes(1); - const handler = coreSetup.http.registerRouteHandlerContext.mock.calls[0]; + const handler = coreSetup.http.registerRouteHandlerContext.mock.calls[0] as [ + string, + Function + ]; expect(handler[0]).toEqual('actions'); const actionsContextHandler = ((await handler[1]( @@ -114,7 +120,7 @@ describe('Actions Plugin', () => { } as unknown) as RequestHandlerContext, httpServerMock.createKibanaRequest(), httpServerMock.createResponseFactory() - )) as unknown) as RequestHandlerContext['actions']; + )) as unknown) as ActionsApiRequestHandlerContext; expect(() => actionsContextHandler!.getActionsClient()).toThrowErrorMatchingInlineSnapshot( `"Unable to create actions client because the Encrypted Saved Objects plugin uses an ephemeral encryption key. Please set xpack.encryptedSavedObjects.encryptionKey in the kibana.yml or use the bin/kibana-encryption-keys command."` ); diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 133e5f9c6aa2..1543f8d7a07c 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -14,7 +14,6 @@ import { CoreStart, KibanaRequest, Logger, - RequestHandler, IContextProvider, ElasticsearchServiceStart, ILegacyClusterClient, @@ -46,6 +45,7 @@ import { ActionTypeConfig, ActionTypeSecrets, ActionTypeParams, + ActionsRequestHandlerContext, } from './types'; import { getActionsConfigurationUtilities } from './actions_config'; @@ -228,7 +228,7 @@ export class ActionsPlugin implements Plugin, Plugi } this.kibanaIndexConfig.subscribe((config) => { - core.http.registerRouteHandlerContext( + core.http.registerRouteHandlerContext( 'actions', this.createRouteHandlerContext(core, config.kibana.index) ); @@ -243,7 +243,7 @@ export class ActionsPlugin implements Plugin, Plugi }); // Routes - const router = core.http.createRouter(); + const router = core.http.createRouter(); createActionRoute(router, this.licenseState); deleteActionRoute(router, this.licenseState); getActionRoute(router, this.licenseState); @@ -448,7 +448,7 @@ export class ActionsPlugin implements Plugin, Plugi private createRouteHandlerContext = ( core: CoreSetup, defaultKibanaIndex: string - ): IContextProvider, 'actions'> => { + ): IContextProvider => { const { actionTypeRegistry, isESOUsingEphemeralEncryptionKey, diff --git a/x-pack/plugins/actions/server/routes/create.ts b/x-pack/plugins/actions/server/routes/create.ts index 462d3f42b506..3400568c7dc4 100644 --- a/x-pack/plugins/actions/server/routes/create.ts +++ b/x-pack/plugins/actions/server/routes/create.ts @@ -4,15 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; -import { ActionResult } from '../types'; +import { schema } from '@kbn/config-schema'; +import { IRouter } from 'kibana/server'; +import { ActionResult, ActionsRequestHandlerContext } from '../types'; import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../lib'; import { BASE_ACTION_API_PATH } from '../../common'; @@ -23,7 +17,10 @@ export const bodySchema = schema.object({ secrets: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), }); -export const createActionRoute = (router: IRouter, licenseState: ILicenseState) => { +export const createActionRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { router.post( { path: `${BASE_ACTION_API_PATH}/action`, @@ -31,11 +28,7 @@ export const createActionRoute = (router: IRouter, licenseState: ILicenseState) body: bodySchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.actions) { diff --git a/x-pack/plugins/actions/server/routes/delete.ts b/x-pack/plugins/actions/server/routes/delete.ts index a7303247e95b..7183ebb7c823 100644 --- a/x-pack/plugins/actions/server/routes/delete.ts +++ b/x-pack/plugins/actions/server/routes/delete.ts @@ -9,22 +9,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { IRouter } from 'kibana/server'; import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../lib'; import { BASE_ACTION_API_PATH } from '../../common'; +import { ActionsRequestHandlerContext } from '../types'; const paramSchema = schema.object({ id: schema.string(), }); -export const deleteActionRoute = (router: IRouter, licenseState: ILicenseState) => { +export const deleteActionRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { router.delete( { path: `${BASE_ACTION_API_PATH}/action/{id}`, @@ -32,11 +30,7 @@ export const deleteActionRoute = (router: IRouter, licenseState: ILicenseState) params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.actions) { return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); diff --git a/x-pack/plugins/actions/server/routes/execute.ts b/x-pack/plugins/actions/server/routes/execute.ts index 8191b6946d33..b2398a8b366e 100644 --- a/x-pack/plugins/actions/server/routes/execute.ts +++ b/x-pack/plugins/actions/server/routes/execute.ts @@ -3,17 +3,11 @@ * 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, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { IRouter } from 'kibana/server'; import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../lib'; -import { ActionTypeExecutorResult } from '../types'; +import { ActionTypeExecutorResult, ActionsRequestHandlerContext } from '../types'; import { BASE_ACTION_API_PATH } from '../../common'; import { asHttpRequestExecutionSource } from '../lib/action_execution_source'; @@ -25,7 +19,10 @@ const bodySchema = schema.object({ params: schema.recordOf(schema.string(), schema.any()), }); -export const executeActionRoute = (router: IRouter, licenseState: ILicenseState) => { +export const executeActionRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { router.post( { path: `${BASE_ACTION_API_PATH}/action/{id}/_execute`, @@ -34,11 +31,7 @@ export const executeActionRoute = (router: IRouter, licenseState: ILicenseState) params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, TypeOf>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.actions) { diff --git a/x-pack/plugins/actions/server/routes/get.ts b/x-pack/plugins/actions/server/routes/get.ts index 33577fad87c0..401fa93bebd8 100644 --- a/x-pack/plugins/actions/server/routes/get.ts +++ b/x-pack/plugins/actions/server/routes/get.ts @@ -4,22 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { IRouter } from 'kibana/server'; import { ILicenseState, verifyApiAccess } from '../lib'; import { BASE_ACTION_API_PATH } from '../../common'; +import { ActionsRequestHandlerContext } from '../types'; const paramSchema = schema.object({ id: schema.string(), }); -export const getActionRoute = (router: IRouter, licenseState: ILicenseState) => { +export const getActionRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { router.get( { path: `${BASE_ACTION_API_PATH}/action/{id}`, @@ -27,11 +25,7 @@ export const getActionRoute = (router: IRouter, licenseState: ILicenseState) => params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.actions) { return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); diff --git a/x-pack/plugins/actions/server/routes/get_all.ts b/x-pack/plugins/actions/server/routes/get_all.ts index 1b57f31d14a0..fa3bd20858b0 100644 --- a/x-pack/plugins/actions/server/routes/get_all.ts +++ b/x-pack/plugins/actions/server/routes/get_all.ts @@ -4,27 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { IRouter } from 'kibana/server'; import { ILicenseState, verifyApiAccess } from '../lib'; import { BASE_ACTION_API_PATH } from '../../common'; +import { ActionsRequestHandlerContext } from '../types'; -export const getAllActionRoute = (router: IRouter, licenseState: ILicenseState) => { +export const getAllActionRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { router.get( { path: `${BASE_ACTION_API_PATH}`, validate: {}, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.actions) { return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); diff --git a/x-pack/plugins/actions/server/routes/list_action_types.ts b/x-pack/plugins/actions/server/routes/list_action_types.ts index c960a6bac6de..b84eede91306 100644 --- a/x-pack/plugins/actions/server/routes/list_action_types.ts +++ b/x-pack/plugins/actions/server/routes/list_action_types.ts @@ -4,27 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { IRouter } from 'kibana/server'; import { ILicenseState, verifyApiAccess } from '../lib'; import { BASE_ACTION_API_PATH } from '../../common'; +import { ActionsRequestHandlerContext } from '../types'; -export const listActionTypesRoute = (router: IRouter, licenseState: ILicenseState) => { +export const listActionTypesRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { router.get( { path: `${BASE_ACTION_API_PATH}/list_action_types`, validate: {}, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.actions) { return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); diff --git a/x-pack/plugins/actions/server/routes/update.ts b/x-pack/plugins/actions/server/routes/update.ts index 328ce74ef0b0..215d617d270b 100644 --- a/x-pack/plugins/actions/server/routes/update.ts +++ b/x-pack/plugins/actions/server/routes/update.ts @@ -4,16 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import { IRouter } from 'kibana/server'; import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../lib'; import { BASE_ACTION_API_PATH } from '../../common'; +import { ActionsRequestHandlerContext } from '../types'; const paramSchema = schema.object({ id: schema.string(), @@ -25,7 +20,10 @@ const bodySchema = schema.object({ secrets: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), }); -export const updateActionRoute = (router: IRouter, licenseState: ILicenseState) => { +export const updateActionRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { router.put( { path: `${BASE_ACTION_API_PATH}/action/{id}`, @@ -34,11 +32,7 @@ export const updateActionRoute = (router: IRouter, licenseState: ILicenseState) params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, TypeOf>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.actions) { return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index 81d6c3550a53..f545c0fc9663 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -15,6 +15,7 @@ import { SavedObjectsClientContract, SavedObjectAttributes, ElasticsearchClient, + RequestHandlerContext, } from '../../../../src/core/server'; import { ActionTypeExecutorResult } from '../common'; export { ActionTypeExecutorResult } from '../common'; @@ -40,13 +41,13 @@ export interface Services { getLegacyScopedClusterClient(clusterClient: ILegacyClusterClient): ILegacyScopedClusterClient; } -declare module 'src/core/server' { - interface RequestHandlerContext { - actions?: { - getActionsClient: () => ActionsClient; - listTypes: ActionTypeRegistry['list']; - }; - } +export interface ActionsApiRequestHandlerContext { + getActionsClient: () => ActionsClient; + listTypes: ActionTypeRegistry['list']; +} + +export interface ActionsRequestHandlerContext extends RequestHandlerContext { + actions: ActionsApiRequestHandlerContext; } export interface ActionsPlugin { diff --git a/x-pack/plugins/actions/tsconfig.json b/x-pack/plugins/actions/tsconfig.json new file mode 100644 index 000000000000..d5c1105c99ad --- /dev/null +++ b/x-pack/plugins/actions/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "server/**/*", + // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 + "server/**/*.json", + "common/*" + ], + "references": [ + { "path": "../../../src/core/tsconfig.json" }, + { "path": "../spaces/tsconfig.json" }, + { "path": "../security/tsconfig.json" }, + { "path": "../licensing/tsconfig.json" }, + { "path": "../task_manager/tsconfig.json" }, + { "path": "../event_log/tsconfig.json" }, + { "path": "../encrypted_saved_objects/tsconfig.json" }, + { "path": "../features/tsconfig.json" }, + { "path": "../../../src/plugins/usage_collection/tsconfig.json" } + ] +} diff --git a/x-pack/plugins/alerts/server/index.ts b/x-pack/plugins/alerts/server/index.ts index da56da671f9b..50698b840f9c 100644 --- a/x-pack/plugins/alerts/server/index.ts +++ b/x-pack/plugins/alerts/server/index.ts @@ -12,7 +12,7 @@ import { AlertsConfigType } from './types'; export type AlertsClient = PublicMethodsOf; -export { +export type { ActionVariable, AlertType, ActionGroup, @@ -26,6 +26,7 @@ export { PartialAlert, AlertInstanceState, AlertInstanceContext, + AlertingApiRequestHandlerContext, } from './types'; export { PluginSetupContract, PluginStartContract } from './plugin'; export { FindResult } from './alerts_client'; diff --git a/x-pack/plugins/alerts/server/plugin.ts b/x-pack/plugins/alerts/server/plugin.ts index cb165fa56d04..fc76b2383b5b 100644 --- a/x-pack/plugins/alerts/server/plugin.ts +++ b/x-pack/plugins/alerts/server/plugin.ts @@ -28,13 +28,13 @@ import { CoreStart, SavedObjectsServiceStart, IContextProvider, - RequestHandler, ElasticsearchServiceStart, ILegacyClusterClient, StatusServiceSetup, ServiceStatus, SavedObjectsBulkGetObject, } from '../../../../src/core/server'; +import type { AlertingRequestHandlerContext } from './types'; import { aggregateAlertRoute, @@ -255,10 +255,13 @@ export class AlertingPlugin { initializeAlertingHealth(this.logger, plugins.taskManager, core.getStartServices()); - core.http.registerRouteHandlerContext('alerting', this.createRouteHandlerContext(core)); + core.http.registerRouteHandlerContext( + 'alerting', + this.createRouteHandlerContext(core) + ); // Routes - const router = core.http.createRouter(); + const router = core.http.createRouter(); // Register routes aggregateAlertRoute(router, this.licenseState); createAlertRoute(router, this.licenseState); @@ -392,7 +395,7 @@ export class AlertingPlugin { private createRouteHandlerContext = ( core: CoreSetup - ): IContextProvider, 'alerting'> => { + ): IContextProvider => { const { alertTypeRegistry, alertsClientFactory } = this; return async function alertsRouteHandlerContext(context, request) { const [{ savedObjects }] = await core.getStartServices(); diff --git a/x-pack/plugins/alerts/server/routes/_mock_handler_arguments.ts b/x-pack/plugins/alerts/server/routes/_mock_handler_arguments.ts index b3f407b20c14..9883d5ec0dd5 100644 --- a/x-pack/plugins/alerts/server/routes/_mock_handler_arguments.ts +++ b/x-pack/plugins/alerts/server/routes/_mock_handler_arguments.ts @@ -4,18 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - RequestHandlerContext, - KibanaRequest, - KibanaResponseFactory, - ILegacyClusterClient, -} from 'kibana/server'; +import { KibanaRequest, KibanaResponseFactory, ILegacyClusterClient } from 'kibana/server'; import { identity } from 'lodash'; import type { MethodKeysOf } from '@kbn/utility-types'; import { httpServerMock } from '../../../../../src/core/server/mocks'; import { alertsClientMock, AlertsClientMock } from '../alerts_client.mock'; import { AlertsHealth, AlertType } from '../../common'; import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; +import type { AlertingRequestHandlerContext } from '../types'; export function mockHandlerArguments( { @@ -32,7 +28,11 @@ export function mockHandlerArguments( }, req: unknown, res?: Array> -): [RequestHandlerContext, KibanaRequest, KibanaResponseFactory] { +): [ + AlertingRequestHandlerContext, + KibanaRequest, + KibanaResponseFactory +] { const listTypes = jest.fn(() => listTypesRes); return [ ({ @@ -44,7 +44,7 @@ export function mockHandlerArguments( }, getFrameworkHealth, }, - } as unknown) as RequestHandlerContext, + } as unknown) as AlertingRequestHandlerContext, req as KibanaRequest, mockResponseFactory(res), ]; diff --git a/x-pack/plugins/alerts/server/routes/aggregate.ts b/x-pack/plugins/alerts/server/routes/aggregate.ts index 0fcfb6f6147e..3b809196f9b7 100644 --- a/x-pack/plugins/alerts/server/routes/aggregate.ts +++ b/x-pack/plugins/alerts/server/routes/aggregate.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -38,7 +32,7 @@ const querySchema = schema.object({ filter: schema.maybe(schema.string()), }); -export const aggregateAlertRoute = (router: IRouter, licenseState: ILicenseState) => { +export const aggregateAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.get( { path: `${BASE_ALERT_API_PATH}/_aggregate`, @@ -46,11 +40,7 @@ export const aggregateAlertRoute = (router: IRouter, licenseState: ILicenseState query: querySchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/create.ts b/x-pack/plugins/alerts/server/routes/create.ts index a79a9d40b236..2b6735d9063d 100644 --- a/x-pack/plugins/alerts/server/routes/create.ts +++ b/x-pack/plugins/alerts/server/routes/create.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { validateDurationSchema } from '../lib'; @@ -48,7 +42,7 @@ export const bodySchema = schema.object({ notifyWhen: schema.nullable(schema.string({ validate: validateNotifyWhenType })), }); -export const createAlertRoute = (router: IRouter, licenseState: ILicenseState) => { +export const createAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { path: `${BASE_ALERT_API_PATH}/alert`, @@ -57,11 +51,7 @@ export const createAlertRoute = (router: IRouter, licenseState: ILicenseState) = }, }, handleDisabledApiKeysError( - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { diff --git a/x-pack/plugins/alerts/server/routes/delete.ts b/x-pack/plugins/alerts/server/routes/delete.ts index 3ac975d3a154..12991e28d67c 100644 --- a/x-pack/plugins/alerts/server/routes/delete.ts +++ b/x-pack/plugins/alerts/server/routes/delete.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -20,7 +14,7 @@ const paramSchema = schema.object({ id: schema.string(), }); -export const deleteAlertRoute = (router: IRouter, licenseState: ILicenseState) => { +export const deleteAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.delete( { path: `${BASE_ALERT_API_PATH}/alert/{id}`, @@ -28,11 +22,7 @@ export const deleteAlertRoute = (router: IRouter, licenseState: ILicenseState) = params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/disable.ts b/x-pack/plugins/alerts/server/routes/disable.ts index e96cb397f554..91663bd85287 100644 --- a/x-pack/plugins/alerts/server/routes/disable.ts +++ b/x-pack/plugins/alerts/server/routes/disable.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -21,7 +15,7 @@ const paramSchema = schema.object({ id: schema.string(), }); -export const disableAlertRoute = (router: IRouter, licenseState: ILicenseState) => { +export const disableAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { path: `${BASE_ALERT_API_PATH}/alert/{id}/_disable`, @@ -29,11 +23,7 @@ export const disableAlertRoute = (router: IRouter, licenseState: ILicenseState) params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/enable.ts b/x-pack/plugins/alerts/server/routes/enable.ts index 81c5027c7587..cbfcbc44d9f5 100644 --- a/x-pack/plugins/alerts/server/routes/enable.ts +++ b/x-pack/plugins/alerts/server/routes/enable.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -22,7 +16,7 @@ const paramSchema = schema.object({ id: schema.string(), }); -export const enableAlertRoute = (router: IRouter, licenseState: ILicenseState) => { +export const enableAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { path: `${BASE_ALERT_API_PATH}/alert/{id}/_enable`, @@ -31,11 +25,7 @@ export const enableAlertRoute = (router: IRouter, licenseState: ILicenseState) = }, }, handleDisabledApiKeysError( - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/find.ts b/x-pack/plugins/alerts/server/routes/find.ts index 487ff571187f..195abf0a15a9 100644 --- a/x-pack/plugins/alerts/server/routes/find.ts +++ b/x-pack/plugins/alerts/server/routes/find.ts @@ -4,14 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; + import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -43,7 +38,7 @@ const querySchema = schema.object({ filter: schema.maybe(schema.string()), }); -export const findAlertRoute = (router: IRouter, licenseState: ILicenseState) => { +export const findAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.get( { path: `${BASE_ALERT_API_PATH}/_find`, @@ -51,11 +46,7 @@ export const findAlertRoute = (router: IRouter, licenseState: ILicenseState) => query: querySchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/get.ts b/x-pack/plugins/alerts/server/routes/get.ts index ae592f37cd55..ddf16d1022c8 100644 --- a/x-pack/plugins/alerts/server/routes/get.ts +++ b/x-pack/plugins/alerts/server/routes/get.ts @@ -4,23 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; +import type { AlertingRouter } from '../types'; const paramSchema = schema.object({ id: schema.string(), }); -export const getAlertRoute = (router: IRouter, licenseState: ILicenseState) => { +export const getAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.get( { path: `${BASE_ALERT_API_PATH}/alert/{id}`, @@ -28,11 +22,7 @@ export const getAlertRoute = (router: IRouter, licenseState: ILicenseState) => { params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/get_alert_instance_summary.ts b/x-pack/plugins/alerts/server/routes/get_alert_instance_summary.ts index 33f331f7dce0..095b8e492467 100644 --- a/x-pack/plugins/alerts/server/routes/get_alert_instance_summary.ts +++ b/x-pack/plugins/alerts/server/routes/get_alert_instance_summary.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -24,7 +18,10 @@ const querySchema = schema.object({ dateStart: schema.maybe(schema.string()), }); -export const getAlertInstanceSummaryRoute = (router: IRouter, licenseState: ILicenseState) => { +export const getAlertInstanceSummaryRoute = ( + router: AlertingRouter, + licenseState: ILicenseState +) => { router.get( { path: `${BASE_ALERT_API_PATH}/alert/{id}/_instance_summary`, @@ -33,11 +30,7 @@ export const getAlertInstanceSummaryRoute = (router: IRouter, licenseState: ILic query: querySchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, TypeOf, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/get_alert_state.ts b/x-pack/plugins/alerts/server/routes/get_alert_state.ts index 52ad8f9f3187..e818e5d04304 100644 --- a/x-pack/plugins/alerts/server/routes/get_alert_state.ts +++ b/x-pack/plugins/alerts/server/routes/get_alert_state.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -20,7 +14,7 @@ const paramSchema = schema.object({ id: schema.string(), }); -export const getAlertStateRoute = (router: IRouter, licenseState: ILicenseState) => { +export const getAlertStateRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.get( { path: `${BASE_ALERT_API_PATH}/alert/{id}/state`, @@ -28,11 +22,7 @@ export const getAlertStateRoute = (router: IRouter, licenseState: ILicenseState) params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/health.ts b/x-pack/plugins/alerts/server/routes/health.ts index 962ad7e1bb29..b21f389e3b08 100644 --- a/x-pack/plugins/alerts/server/routes/health.ts +++ b/x-pack/plugins/alerts/server/routes/health.ts @@ -4,13 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { AlertingFrameworkHealth } from '../types'; @@ -28,7 +22,7 @@ interface XPackUsageSecurity { } export function healthRoute( - router: IRouter, + router: AlertingRouter, licenseState: ILicenseState, encryptedSavedObjects: EncryptedSavedObjectsPluginSetup ) { @@ -37,11 +31,7 @@ export function healthRoute( path: '/api/alerts/_health', validate: false, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/lib/error_handler.ts b/x-pack/plugins/alerts/server/routes/lib/error_handler.ts index e0c620b0670c..f0ee9087cbe8 100644 --- a/x-pack/plugins/alerts/server/routes/lib/error_handler.ts +++ b/x-pack/plugins/alerts/server/routes/lib/error_handler.ts @@ -5,22 +5,10 @@ */ import { i18n } from '@kbn/i18n'; -import { - RequestHandler, - KibanaRequest, - KibanaResponseFactory, - RequestHandlerContext, - RouteMethod, -} from 'kibana/server'; +import { RequestHandlerWrapper } from 'kibana/server'; -export function handleDisabledApiKeysError( - handler: RequestHandler -): RequestHandler { - return async ( - context: RequestHandlerContext, - request: KibanaRequest, - response: KibanaResponseFactory - ) => { +export const handleDisabledApiKeysError: RequestHandlerWrapper = (handler) => { + return async (context, request, response) => { try { return await handler(context, request, response); } catch (e) { @@ -36,7 +24,7 @@ export function handleDisabledApiKeysError( throw e; } }; -} +}; export function isApiKeyDisabledError(e: Error) { return e?.message?.includes('api keys are not enabled') ?? false; diff --git a/x-pack/plugins/alerts/server/routes/list_alert_types.ts b/x-pack/plugins/alerts/server/routes/list_alert_types.ts index 9b4b352e211f..b5cefccfd4b0 100644 --- a/x-pack/plugins/alerts/server/routes/list_alert_types.ts +++ b/x-pack/plugins/alerts/server/routes/list_alert_types.ts @@ -4,28 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; -export const listAlertTypesRoute = (router: IRouter, licenseState: ILicenseState) => { +export const listAlertTypesRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.get( { path: `${BASE_ALERT_API_PATH}/list_alert_types`, validate: {}, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/mute_all.ts b/x-pack/plugins/alerts/server/routes/mute_all.ts index 224216961bb7..92127a7cec67 100644 --- a/x-pack/plugins/alerts/server/routes/mute_all.ts +++ b/x-pack/plugins/alerts/server/routes/mute_all.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -21,7 +15,7 @@ const paramSchema = schema.object({ id: schema.string(), }); -export const muteAllAlertRoute = (router: IRouter, licenseState: ILicenseState) => { +export const muteAllAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { path: `${BASE_ALERT_API_PATH}/alert/{id}/_mute_all`, @@ -29,11 +23,7 @@ export const muteAllAlertRoute = (router: IRouter, licenseState: ILicenseState) params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/mute_instance.ts b/x-pack/plugins/alerts/server/routes/mute_instance.ts index b37486617723..923bce5b8c31 100644 --- a/x-pack/plugins/alerts/server/routes/mute_instance.ts +++ b/x-pack/plugins/alerts/server/routes/mute_instance.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -24,7 +18,7 @@ const paramSchema = schema.object({ alert_instance_id: schema.string(), }); -export const muteAlertInstanceRoute = (router: IRouter, licenseState: ILicenseState) => { +export const muteAlertInstanceRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { path: `${BASE_ALERT_API_PATH}/alert/{alert_id}/alert_instance/{alert_instance_id}/_mute`, @@ -32,11 +26,7 @@ export const muteAlertInstanceRoute = (router: IRouter, licenseState: ILicenseSt params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/unmute_all.ts b/x-pack/plugins/alerts/server/routes/unmute_all.ts index e249ec7ffa58..8709cf3ae887 100644 --- a/x-pack/plugins/alerts/server/routes/unmute_all.ts +++ b/x-pack/plugins/alerts/server/routes/unmute_all.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -21,7 +15,7 @@ const paramSchema = schema.object({ id: schema.string(), }); -export const unmuteAllAlertRoute = (router: IRouter, licenseState: ILicenseState) => { +export const unmuteAllAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { path: `${BASE_ALERT_API_PATH}/alert/{id}/_unmute_all`, @@ -29,11 +23,7 @@ export const unmuteAllAlertRoute = (router: IRouter, licenseState: ILicenseState params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/unmute_instance.ts b/x-pack/plugins/alerts/server/routes/unmute_instance.ts index bcab6e21578a..d1c62ce36592 100644 --- a/x-pack/plugins/alerts/server/routes/unmute_instance.ts +++ b/x-pack/plugins/alerts/server/routes/unmute_instance.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -22,7 +16,7 @@ const paramSchema = schema.object({ alertInstanceId: schema.string(), }); -export const unmuteAlertInstanceRoute = (router: IRouter, licenseState: ILicenseState) => { +export const unmuteAlertInstanceRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { path: `${BASE_ALERT_API_PATH}/alert/{alertId}/alert_instance/{alertInstanceId}/_unmute`, @@ -30,11 +24,7 @@ export const unmuteAlertInstanceRoute = (router: IRouter, licenseState: ILicense params: paramSchema, }, }, - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/update.ts b/x-pack/plugins/alerts/server/routes/update.ts index d3ecc9eb3e38..2e70843dcb14 100644 --- a/x-pack/plugins/alerts/server/routes/update.ts +++ b/x-pack/plugins/alerts/server/routes/update.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { validateDurationSchema } from '../lib'; @@ -43,7 +37,7 @@ const bodySchema = schema.object({ notifyWhen: schema.nullable(schema.string({ validate: validateNotifyWhenType })), }); -export const updateAlertRoute = (router: IRouter, licenseState: ILicenseState) => { +export const updateAlertRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.put( { path: `${BASE_ALERT_API_PATH}/alert/{id}`, @@ -53,11 +47,7 @@ export const updateAlertRoute = (router: IRouter, licenseState: ILicenseState) = }, }, handleDisabledApiKeysError( - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, TypeOf>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/routes/update_api_key.ts b/x-pack/plugins/alerts/server/routes/update_api_key.ts index fb7639d97598..743435c96e6a 100644 --- a/x-pack/plugins/alerts/server/routes/update_api_key.ts +++ b/x-pack/plugins/alerts/server/routes/update_api_key.ts @@ -4,14 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; +import { schema } from '@kbn/config-schema'; +import type { AlertingRouter } from '../types'; import { ILicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { BASE_ALERT_API_PATH } from '../../common'; @@ -22,7 +16,7 @@ const paramSchema = schema.object({ id: schema.string(), }); -export const updateApiKeyRoute = (router: IRouter, licenseState: ILicenseState) => { +export const updateApiKeyRoute = (router: AlertingRouter, licenseState: ILicenseState) => { router.post( { path: `${BASE_ALERT_API_PATH}/alert/{id}/_update_api_key`, @@ -31,11 +25,7 @@ export const updateApiKeyRoute = (router: IRouter, licenseState: ILicenseState) }, }, handleDisabledApiKeysError( - router.handleLegacyErrors(async function ( - context: RequestHandlerContext, - req: KibanaRequest, unknown, unknown>, - res: KibanaResponseFactory - ): Promise { + router.handleLegacyErrors(async function (context, req, res) { verifyApiAccess(licenseState); if (!context.alerting) { return res.badRequest({ body: 'RouteHandlerContext is not registered for alerting' }); diff --git a/x-pack/plugins/alerts/server/saved_objects/migrations.ts b/x-pack/plugins/alerts/server/saved_objects/migrations.ts index 1b9c5dac23b8..76696d11d5f0 100644 --- a/x-pack/plugins/alerts/server/saved_objects/migrations.ts +++ b/x-pack/plugins/alerts/server/saved_objects/migrations.ts @@ -11,11 +11,9 @@ import { } from '../../../../../src/core/server'; import { RawAlert } from '../types'; import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; -import { - APP_ID as SIEM_APP_ID, - SERVER_APP_ID as SIEM_SERVER_APP_ID, -} from '../../../security_solution/common/constants'; +const SIEM_APP_ID = 'securitySolution'; +const SIEM_SERVER_APP_ID = 'siem'; export const LEGACY_LAST_MODIFIED_VERSION = 'pre-7.10.0'; type AlertMigration = ( diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index 39c52d9653aa..0e686bc1f21d 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import type { IRouter, RequestHandlerContext } from 'src/core/server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { PublicAlertInstance } from './alert_instance'; import { AlertTypeRegistry as OrigAlertTypeRegistry } from './alert_type_registry'; @@ -37,16 +38,27 @@ export type WithoutQueryAndParams = Pick Services; export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined; -declare module 'src/core/server' { - interface RequestHandlerContext { - alerting?: { - getAlertsClient: () => AlertsClient; - listTypes: AlertTypeRegistry['list']; - getFrameworkHealth: () => Promise; - }; - } +/** + * @public + */ +export interface AlertingApiRequestHandlerContext { + getAlertsClient: () => AlertsClient; + listTypes: AlertTypeRegistry['list']; + getFrameworkHealth: () => Promise; +} + +/** + * @internal + */ +export interface AlertingRequestHandlerContext extends RequestHandlerContext { + alerting: AlertingApiRequestHandlerContext; } +/** + * @internal + */ +export type AlertingRouter = IRouter; + export interface Services { /** * @deprecated Use `scopedClusterClient` instead. diff --git a/x-pack/plugins/alerts/tsconfig.json b/x-pack/plugins/alerts/tsconfig.json new file mode 100644 index 000000000000..86ab00faeb5a --- /dev/null +++ b/x-pack/plugins/alerts/tsconfig.json @@ -0,0 +1,30 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "server/**/*", + // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 + "server/**/*.json", + "public/**/*", + "common/*" + ], + "references": [ + { "path": "../../../src/core/tsconfig.json" }, + { "path": "../actions/tsconfig.json" }, + { "path": "../spaces/tsconfig.json" }, + { "path": "../security/tsconfig.json" }, + { "path": "../licensing/tsconfig.json" }, + { "path": "../task_manager/tsconfig.json" }, + { "path": "../event_log/tsconfig.json" }, + { "path": "../encrypted_saved_objects/tsconfig.json" }, + { "path": "../features/tsconfig.json" }, + { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_utils/tsconfig.json" } + ] +} diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx index c810bd3e7c48..447f6d502fe4 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/RumDashboard.tsx @@ -18,7 +18,7 @@ import { UXMetrics } from './UXMetrics'; import { ImpactfulMetrics } from './ImpactfulMetrics'; import { PageLoadAndViews } from './Panels/PageLoadAndViews'; import { VisitorBreakdownsPanel } from './Panels/VisitorBreakdowns'; -import { useBreakPoints } from './hooks/useBreakPoints'; +import { useBreakPoints } from '../../../hooks/use_break_points'; import { getPercentileLabel } from './UXMetrics/translations'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx index 6810b56fb8f8..b1a97dd34b88 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx @@ -113,7 +113,7 @@ export function TransactionDetails({

{transactionName}

- + diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx index 8776b7e9355f..2f83d4804948 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx @@ -129,7 +129,7 @@ export function ServiceInventory() { return ( <> - + diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx index e501dd3bb7a5..eb8068bc8114 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx @@ -57,6 +57,8 @@ function wrapper({ children }: { children?: ReactNode }) { rangeTo: 'now', start: 'mystart', end: 'myend', + comparisonEnabled: true, + comparisonType: 'yesterday', }} > {children} diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx index c6cc59876fe3..bf03b60d6d24 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx @@ -12,6 +12,7 @@ import { isRumAgentName } from '../../../../common/agent_name'; import { AnnotationsContextProvider } from '../../../context/annotations/annotations_context'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context'; +import { useBreakPoints } from '../../../hooks/use_break_points'; import { LatencyChart } from '../../shared/charts/latency_chart'; import { TransactionBreakdownChart } from '../../shared/charts/transaction_breakdown_chart'; import { TransactionErrorRateChart } from '../../shared/charts/transaction_error_rate_chart'; @@ -22,7 +23,6 @@ import { ServiceOverviewErrorsTable } from './service_overview_errors_table'; import { ServiceOverviewInstancesChartAndTable } from './service_overview_instances_chart_and_table'; import { ServiceOverviewThroughputChart } from './service_overview_throughput_chart'; import { ServiceOverviewTransactionsTable } from './service_overview_transactions_table'; -import { useShouldUseMobileLayout } from './use_should_use_mobile_layout'; /** * The height a chart should be if it's next to a table with 5 rows and a title. @@ -44,8 +44,8 @@ export function ServiceOverview({ // The default EuiFlexGroup breaks at 768, but we want to break at 992, so we // observe the window width and set the flex directions of rows accordingly - const shouldUseMobileLayout = useShouldUseMobileLayout(); - const rowDirection = shouldUseMobileLayout ? 'column' : 'row'; + const { isMedium } = useBreakPoints(); + const rowDirection = isMedium ? 'column' : 'row'; const { transactionType } = useApmServiceContext(); const transactionTypeLabel = i18n.translate( @@ -57,7 +57,7 @@ export function ServiceOverview({ return ( - + {isRumAgent && ( diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_table_container.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_table_container.tsx index 76db81a70550..bbd6dd1d498d 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_table_container.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_table_container.tsx @@ -6,7 +6,7 @@ import React, { ReactNode } from 'react'; import styled from 'styled-components'; -import { useShouldUseMobileLayout } from './use_should_use_mobile_layout'; +import { useBreakPoints } from '../../../hooks/use_break_points'; /** * The height for a table on the overview page. Is the height of a 5-row basic @@ -58,12 +58,12 @@ export function ServiceOverviewTableContainer({ children?: ReactNode; isEmptyAndLoading: boolean; }) { - const shouldUseMobileLayout = useShouldUseMobileLayout(); + const { isMedium } = useBreakPoints(); return ( {children} diff --git a/x-pack/plugins/apm/public/components/app/service_overview/use_should_use_mobile_layout.ts b/x-pack/plugins/apm/public/components/app/service_overview/use_should_use_mobile_layout.ts deleted file mode 100644 index bd844a3f2e69..000000000000 --- a/x-pack/plugins/apm/public/components/app/service_overview/use_should_use_mobile_layout.ts +++ /dev/null @@ -1,27 +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 { isWithinMaxBreakpoint } from '@elastic/eui'; -import { useEffect, useState } from 'react'; - -export function useShouldUseMobileLayout() { - const [shouldUseMobileLayout, setShouldUseMobileLayout] = useState( - isWithinMaxBreakpoint(window.innerWidth, 'm') - ); - - useEffect(() => { - const resizeHandler = () => { - setShouldUseMobileLayout(isWithinMaxBreakpoint(window.innerWidth, 'm')); - }; - window.addEventListener('resize', resizeHandler); - - return () => { - window.removeEventListener('resize', resizeHandler); - }; - }); - - return shouldUseMobileLayout; -} diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx index 30fbfe9cc870..5be08477b3bf 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/index.tsx @@ -108,7 +108,7 @@ export function TransactionOverview({ serviceName }: TransactionOverviewProps) { return ( <> - + diff --git a/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx b/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx index bfb9c51bc245..31d90330721d 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_overview/transaction_overview.test.tsx @@ -130,7 +130,7 @@ describe('TransactionOverview', () => { }); expect(history.location.search).toEqual( - '?transactionType=secondType&rangeFrom=now-15m&rangeTo=now' + '?transactionType=secondType&rangeFrom=now-15m&rangeTo=now&comparisonEnabled=true&comparisonType=yesterday' ); expect(getByText(container, 'firstType')).toBeInTheDocument(); expect(getByText(container, 'secondType')).toBeInTheDocument(); @@ -139,7 +139,7 @@ describe('TransactionOverview', () => { expect(history.push).toHaveBeenCalled(); expect(history.location.search).toEqual( - '?transactionType=firstType&rangeFrom=now-15m&rangeTo=now' + '?transactionType=firstType&rangeFrom=now-15m&rangeTo=now&comparisonEnabled=true&comparisonType=yesterday' ); }); }); diff --git a/x-pack/plugins/apm/public/components/shared/Links/url_helpers.ts b/x-pack/plugins/apm/public/components/shared/Links/url_helpers.ts index 8576d9ee8635..e899e4a07e96 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/url_helpers.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/url_helpers.ts @@ -85,6 +85,8 @@ export type APMQueryParams = { searchTerm?: string; percentile?: 50 | 75 | 90 | 95 | 99; latencyAggregationType?: string; + comparisonEnabled?: boolean; + comparisonType?: string; } & { [key in LocalUIFilterName]?: string }; // forces every value of T[K] to be type: string diff --git a/x-pack/plugins/apm/public/components/shared/search_bar.tsx b/x-pack/plugins/apm/public/components/shared/search_bar.tsx index 6382f4937ac0..48d329a85332 100644 --- a/x-pack/plugins/apm/public/components/shared/search_bar.tsx +++ b/x-pack/plugins/apm/public/components/shared/search_bar.tsx @@ -7,22 +7,49 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React from 'react'; import styled from 'styled-components'; +import { px, unit } from '../../style/variables'; import { DatePicker } from './DatePicker'; import { KueryBar } from './KueryBar'; +import { TimeComparison } from './time_comparison'; +import { useBreakPoints } from '../../hooks/use_break_points'; const SearchBarFlexGroup = styled(EuiFlexGroup)` margin: ${({ theme }) => `${theme.eui.euiSizeM} ${theme.eui.euiSizeM} -${theme.eui.gutterTypes.gutterMedium} ${theme.eui.euiSizeM}`}; `; -export function SearchBar(props: { prepend?: React.ReactNode | string }) { +interface Props { + prepend?: React.ReactNode | string; + showTimeComparison?: boolean; +} + +function getRowDirection(showColumn: boolean) { + return showColumn ? 'column' : 'row'; +} + +export function SearchBar({ prepend, showTimeComparison = false }: Props) { + const { isMedium, isLarge } = useBreakPoints(); + const itemsStyle = { marginBottom: isLarge ? px(unit) : 0 }; return ( - - - + + + - - + + + {showTimeComparison && ( + + + + )} + + + + ); diff --git a/x-pack/plugins/apm/public/components/shared/time_comparison/index.test.tsx b/x-pack/plugins/apm/public/components/shared/time_comparison/index.test.tsx new file mode 100644 index 000000000000..6348097a3e3a --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/time_comparison/index.test.tsx @@ -0,0 +1,142 @@ +/* + * 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 { render } from '@testing-library/react'; +import React, { ReactNode } from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { EuiThemeProvider } from '../../../../../observability/public'; +import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider'; +import { IUrlParams } from '../../../context/url_params_context/types'; +import { + expectTextsInDocument, + expectTextsNotInDocument, +} from '../../../utils/testHelpers'; +import { TimeComparison } from './'; +import * as urlHelpers from '../../shared/Links/url_helpers'; + +function getWrapper(params?: IUrlParams) { + return ({ children }: { children?: ReactNode }) => { + return ( + + + {children} + + + ); + }; +} + +describe('TimeComparison', () => { + const spy = jest.spyOn(urlHelpers, 'replace'); + beforeEach(() => { + jest.resetAllMocks(); + }); + describe('Time range is between 0 - 24 hours', () => { + it('sets default values', () => { + const Wrapper = getWrapper({ + start: '2021-01-28T14:45:00.000Z', + end: '2021-01-28T15:00:00.000Z', + }); + render(, { + wrapper: Wrapper, + }); + expect(spy).toHaveBeenCalledWith(expect.anything(), { + query: { + comparisonEnabled: 'true', + comparisonType: 'yesterday', + }, + }); + }); + it('selects yesterday and enables comparison', () => { + const Wrapper = getWrapper({ + start: '2021-01-28T14:45:00.000Z', + end: '2021-01-28T15:00:00.000Z', + comparisonEnabled: true, + comparisonType: 'yesterday', + }); + const component = render(, { + wrapper: Wrapper, + }); + expectTextsInDocument(component, ['Yesterday', 'A week ago']); + expect( + (component.getByTestId('comparisonSelect') as HTMLSelectElement) + .selectedIndex + ).toEqual(0); + }); + }); + + describe('Time range is between 24 hours - 1 week', () => { + it('sets default values', () => { + const Wrapper = getWrapper({ + start: '2021-01-26T15:00:00.000Z', + end: '2021-01-28T15:00:00.000Z', + }); + render(, { + wrapper: Wrapper, + }); + expect(spy).toHaveBeenCalledWith(expect.anything(), { + query: { + comparisonEnabled: 'true', + comparisonType: 'week', + }, + }); + }); + it('selects week and enables comparison', () => { + const Wrapper = getWrapper({ + start: '2021-01-26T15:00:00.000Z', + end: '2021-01-28T15:00:00.000Z', + comparisonEnabled: true, + comparisonType: 'week', + }); + const component = render(, { + wrapper: Wrapper, + }); + expectTextsNotInDocument(component, ['Yesterday']); + expectTextsInDocument(component, ['A week ago']); + expect( + (component.getByTestId('comparisonSelect') as HTMLSelectElement) + .selectedIndex + ).toEqual(0); + }); + }); + + describe('Time range is greater than 7 days', () => { + it('Shows absolute times without year when within the same year', () => { + const Wrapper = getWrapper({ + start: '2021-01-20T15:00:00.000Z', + end: '2021-01-28T15:00:00.000Z', + comparisonEnabled: true, + comparisonType: 'previousPeriod', + }); + const component = render(, { + wrapper: Wrapper, + }); + expect(spy).not.toHaveBeenCalled(); + expectTextsInDocument(component, ['20/01 - 28/01']); + expect( + (component.getByTestId('comparisonSelect') as HTMLSelectElement) + .selectedIndex + ).toEqual(0); + }); + + it('Shows absolute times with year when on different year', () => { + const Wrapper = getWrapper({ + start: '2020-12-20T15:00:00.000Z', + end: '2021-01-28T15:00:00.000Z', + comparisonEnabled: true, + comparisonType: 'previousPeriod', + }); + const component = render(, { + wrapper: Wrapper, + }); + expect(spy).not.toHaveBeenCalled(); + expectTextsInDocument(component, ['20/12/20 - 28/01/21']); + expect( + (component.getByTestId('comparisonSelect') as HTMLSelectElement) + .selectedIndex + ).toEqual(0); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/components/shared/time_comparison/index.tsx b/x-pack/plugins/apm/public/components/shared/time_comparison/index.tsx new file mode 100644 index 000000000000..2195621167e8 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/time_comparison/index.tsx @@ -0,0 +1,144 @@ +/* + * 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 { EuiCheckbox, EuiSelect } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import styled from 'styled-components'; +import { getDateDifference } from '../../../../common/utils/formatters'; +import { useUrlParams } from '../../../context/url_params_context/use_url_params'; +import { px, unit } from '../../../style/variables'; +import * as urlHelpers from '../../shared/Links/url_helpers'; +import { useBreakPoints } from '../../../hooks/use_break_points'; + +const PrependContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + background-color: ${({ theme }) => theme.eui.euiGradientMiddle}; + padding: 0 ${px(unit)}; +`; + +function formatPreviousPeriodDates({ + momentStart, + momentEnd, +}: { + momentStart: moment.Moment; + momentEnd: moment.Moment; +}) { + const isDifferentYears = momentStart.get('year') !== momentEnd.get('year'); + const dateFormat = isDifferentYears ? 'DD/MM/YY' : 'DD/MM'; + return `${momentStart.format(dateFormat)} - ${momentEnd.format(dateFormat)}`; +} + +function getSelectOptions({ start, end }: { start?: string; end?: string }) { + const momentStart = moment(start); + const momentEnd = moment(end); + const dateDiff = getDateDifference(momentStart, momentEnd, 'days'); + + const yesterdayOption = { + value: 'yesterday', + text: i18n.translate('xpack.apm.timeComparison.select.yesterday', { + defaultMessage: 'Yesterday', + }), + }; + + const aWeekAgoOption = { + value: 'week', + text: i18n.translate('xpack.apm.timeComparison.select.weekAgo', { + defaultMessage: 'A week ago', + }), + }; + + const prevPeriodOption = { + value: 'previousPeriod', + text: formatPreviousPeriodDates({ momentStart, momentEnd }), + }; + + // Less than one day + if (dateDiff < 1) { + return [yesterdayOption, aWeekAgoOption]; + } + + // Less than one week + if (dateDiff <= 7) { + return [aWeekAgoOption]; + } + + // above one week + return [prevPeriodOption]; +} + +export function TimeComparison() { + const history = useHistory(); + const { isMedium, isLarge } = useBreakPoints(); + const { + urlParams: { start, end, comparisonEnabled, comparisonType }, + } = useUrlParams(); + + const selectOptions = getSelectOptions({ start, end }); + + // Sets default values + if (comparisonEnabled === undefined || comparisonType === undefined) { + urlHelpers.replace(history, { + query: { + comparisonEnabled: comparisonEnabled === false ? 'false' : 'true', + comparisonType: comparisonType + ? comparisonType + : selectOptions[0].value, + }, + }); + return null; + } + + const isSelectedComparisonTypeAvailable = selectOptions.some( + ({ value }) => value === comparisonType + ); + + // Replaces type when current one is no longer available in the select options + if (selectOptions.length !== 0 && !isSelectedComparisonTypeAvailable) { + urlHelpers.replace(history, { + query: { comparisonType: selectOptions[0].value }, + }); + return null; + } + + return ( + + 0} + onChange={() => { + urlHelpers.push(history, { + query: { + comparisonEnabled: Boolean(!comparisonEnabled).toString(), + }, + }); + }} + /> + + } + onChange={(e) => { + urlHelpers.push(history, { + query: { + comparisonType: e.target.value, + }, + }); + }} + /> + ); +} diff --git a/x-pack/plugins/apm/public/context/url_params_context/resolve_url_params.ts b/x-pack/plugins/apm/public/context/url_params_context/resolve_url_params.ts index 0596d649116a..1b8a131bd88a 100644 --- a/x-pack/plugins/apm/public/context/url_params_context/resolve_url_params.ts +++ b/x-pack/plugins/apm/public/context/url_params_context/resolve_url_params.ts @@ -49,6 +49,8 @@ export function resolveUrlParams(location: Location, state: TimeUrlParams) { searchTerm, percentile, latencyAggregationType = LatencyAggregationType.avg, + comparisonEnabled, + comparisonType, } = query; const localUIFilters = pickKeys(query, ...localUIFilterNames); @@ -78,6 +80,10 @@ export function resolveUrlParams(location: Location, state: TimeUrlParams) { searchTerm: toString(searchTerm), percentile: toNumber(percentile), latencyAggregationType, + comparisonEnabled: comparisonEnabled + ? toBoolean(comparisonEnabled) + : undefined, + comparisonType, // ui filters environment, diff --git a/x-pack/plugins/apm/public/context/url_params_context/types.ts b/x-pack/plugins/apm/public/context/url_params_context/types.ts index d792c93b7d0d..cd5fa55cd132 100644 --- a/x-pack/plugins/apm/public/context/url_params_context/types.ts +++ b/x-pack/plugins/apm/public/context/url_params_context/types.ts @@ -29,4 +29,6 @@ export type IUrlParams = { searchTerm?: string; percentile?: number; latencyAggregationType?: string; + comparisonEnabled?: boolean; + comparisonType?: string; } & Partial>; diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/hooks/useBreakPoints.ts b/x-pack/plugins/apm/public/hooks/use_break_points.ts similarity index 100% rename from x-pack/plugins/apm/public/components/app/RumDashboard/hooks/useBreakPoints.ts rename to x-pack/plugins/apm/public/hooks/use_break_points.ts diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json b/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json index 5a810fa90259..4a791ed18121 100644 --- a/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json +++ b/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json @@ -4,7 +4,11 @@ "./plugins/observability/**/*", "./typings/**/*" ], - "exclude": ["**/__fixtures__/**/*", "./plugins/apm/e2e/cypress/**/*"], + "exclude": [ + "**/__fixtures__/**/*", + "./plugins/apm/e2e", + "./plugins/apm/ftr_e2e" + ], "compilerOptions": { "noErrorTruncation": true } diff --git a/x-pack/plugins/apm/server/feature.ts b/x-pack/plugins/apm/server/feature.ts index 9eba18d44ad5..deb89314fc62 100644 --- a/x-pack/plugins/apm/server/feature.ts +++ b/x-pack/plugins/apm/server/feature.ts @@ -10,7 +10,7 @@ import { AlertType } from '../common/alert_types'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server'; import { LicensingPluginSetup, - LicensingRequestHandlerContext, + LicensingApiRequestHandlerContext, } from '../../licensing/server'; export const APM_FEATURE = { @@ -97,7 +97,7 @@ export function notifyFeatureUsage({ licensingPlugin, featureName, }: { - licensingPlugin: LicensingRequestHandlerContext; + licensingPlugin: LicensingApiRequestHandlerContext; featureName: FeatureName; }) { const feature = features[featureName]; diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 44269b177595..09b75137e12d 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -14,7 +14,6 @@ import { Logger, Plugin, PluginInitializerContext, - RequestHandlerContext, } from 'src/core/server'; import { APMConfig, APMXPackConfig, mergeConfigs } from '.'; import { APMOSSPluginSetup } from '../../../../src/plugins/apm_oss/server'; @@ -42,6 +41,7 @@ import { createApmApi } from './routes/create_apm_api'; import { apmIndices, apmTelemetry } from './saved_objects'; import { createElasticCloudInstructions } from './tutorial/elastic_cloud'; import { uiSettings } from './ui_settings'; +import type { ApmPluginRequestHandlerContext } from './routes/typings'; export interface APMPluginSetup { config$: Observable; @@ -49,7 +49,7 @@ export interface APMPluginSetup { createApmEventClient: (params: { debug?: boolean; request: KibanaRequest; - context: RequestHandlerContext; + context: ApmPluginRequestHandlerContext; }) => Promise>; } @@ -166,7 +166,7 @@ export class APMPlugin implements Plugin { }: { debug?: boolean; request: KibanaRequest; - context: RequestHandlerContext; + context: ApmPluginRequestHandlerContext; }) => { const [indices, includeFrozen] = await Promise.all([ boundGetApmIndices(), diff --git a/x-pack/plugins/apm/server/routes/create_api/index.ts b/x-pack/plugins/apm/server/routes/create_api/index.ts index 94711cf76c14..cfb31670bd52 100644 --- a/x-pack/plugins/apm/server/routes/create_api/index.ts +++ b/x-pack/plugins/apm/server/routes/create_api/index.ts @@ -15,6 +15,7 @@ import { strictKeysRt } from '../../../common/runtime_types/strict_keys_rt'; import { APMConfig } from '../..'; import { ServerAPI } from '../typings'; import { jsonRt } from '../../../common/runtime_types/json_rt'; +import type { ApmPluginRequestHandlerContext } from '../typings'; const debugRt = t.exact( t.partial({ @@ -73,7 +74,10 @@ export function createApi() { const anyObject = schema.object({}, { unknowns: 'allow' }); - (router[typedRouterMethod] as RouteRegistrar)( + (router[typedRouterMethod] as RouteRegistrar< + typeof typedRouterMethod, + ApmPluginRequestHandlerContext + >)( { path, options, diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts index 81b25e572a28..7d7a5c3b0dab 100644 --- a/x-pack/plugins/apm/server/routes/typings.ts +++ b/x-pack/plugins/apm/server/routes/typings.ts @@ -14,6 +14,7 @@ import { import { Observable } from 'rxjs'; import { RequiredKeys } from 'utility-types'; import { ObservabilityPluginSetup } from '../../../observability/server'; +import { LicensingApiRequestHandlerContext } from '../../../licensing/server'; import { SecurityPluginSetup } from '../../../security/server'; import { MlPluginSetup } from '../../../ml/server'; import { FetchOptions } from '../../common/fetch_options'; @@ -64,9 +65,16 @@ export interface Route< handler: RouteHandler; } +/** + * @internal + */ +export interface ApmPluginRequestHandlerContext extends RequestHandlerContext { + licensing: LicensingApiRequestHandlerContext; +} + export type APMRequestHandlerContext< TRouteParams = {} -> = RequestHandlerContext & { +> = ApmPluginRequestHandlerContext & { params: TRouteParams & { query: { _debug: boolean } }; config: APMConfig; logger: Logger; diff --git a/x-pack/plugins/beats_management/server/lib/types.ts b/x-pack/plugins/beats_management/server/lib/types.ts index d86aa8652fdb..20467af7018f 100644 --- a/x-pack/plugins/beats_management/server/lib/types.ts +++ b/x-pack/plugins/beats_management/server/lib/types.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import type { IRouter, RequestHandlerContext } from 'src/core/server'; import { DatabaseAdapter } from './adapters/database/adapter_types'; import { FrameworkUser } from './adapters/framework/adapter_types'; import { BeatEventsLib } from './beat_events'; @@ -40,3 +40,20 @@ export interface AsyncResponse { export interface AsyncResponse { data: DataType; } + +/** + * @internal + */ +export type BeatsManagementApiRequestHandlerContext = CMServerLibs; + +/** + * @internal + */ +export interface BeatsManagementRequestHandlerContext extends RequestHandlerContext { + beatsManagement: CMServerLibs; +} + +/** + * @internal + */ +export type BeatsManagementRouter = IRouter; diff --git a/x-pack/plugins/beats_management/server/plugin.ts b/x-pack/plugins/beats_management/server/plugin.ts index fde0a2efecdd..d52de39ed458 100644 --- a/x-pack/plugins/beats_management/server/plugin.ts +++ b/x-pack/plugins/beats_management/server/plugin.ts @@ -5,17 +5,12 @@ */ import { take } from 'rxjs/operators'; -import { - CoreSetup, - CoreStart, - Plugin, - PluginInitializerContext, -} from '../../../../src/core/server'; +import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { SecurityPluginSetup } from '../../security/server'; import { LicensingPluginStart } from '../../licensing/server'; import { BeatsManagementConfigType } from '../common'; -import { CMServerLibs } from './lib/types'; +import type { BeatsManagementRequestHandlerContext, CMServerLibs } from './lib/types'; import { registerRoutes } from './routes'; import { compose } from './lib/compose/kibana'; import { INDEX_NAMES } from '../common/constants'; @@ -30,12 +25,6 @@ interface StartDeps { licensing: LicensingPluginStart; } -declare module 'src/core/server' { - interface RequestHandlerContext { - beatsManagement?: CMServerLibs; - } -} - export class BeatsManagementPlugin implements Plugin<{}, {}, SetupDeps, StartDeps> { private securitySetup?: SecurityPluginSetup; private beatsLibs?: CMServerLibs; @@ -47,12 +36,15 @@ export class BeatsManagementPlugin implements Plugin<{}, {}, SetupDeps, StartDep public async setup(core: CoreSetup, { features, security }: SetupDeps) { this.securitySetup = security; - const router = core.http.createRouter(); + const router = core.http.createRouter(); registerRoutes(router); - core.http.registerRouteHandlerContext('beatsManagement', (_, req) => { - return this.beatsLibs!; - }); + core.http.registerRouteHandlerContext( + 'beatsManagement', + (_, req) => { + return this.beatsLibs!; + } + ); features.registerElasticsearchFeature({ id: 'beats_management', diff --git a/x-pack/plugins/beats_management/server/routes/beats/configuration.ts b/x-pack/plugins/beats_management/server/routes/beats/configuration.ts index 1496e4bbfc99..363b8e3f0716 100644 --- a/x-pack/plugins/beats_management/server/routes/beats/configuration.ts +++ b/x-pack/plugins/beats_management/server/routes/beats/configuration.ts @@ -5,12 +5,12 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { ConfigurationBlock } from '../../../common/domain_types'; import { ReturnTypeList } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerGetBeatConfigurationRoute = (router: IRouter) => { +export const registerGetBeatConfigurationRoute = (router: BeatsManagementRouter) => { router.get( { path: '/api/beats/agent/{beatId}/configuration', diff --git a/x-pack/plugins/beats_management/server/routes/beats/enroll.ts b/x-pack/plugins/beats_management/server/routes/beats/enroll.ts index be8fff3b7c43..919d52942cd6 100644 --- a/x-pack/plugins/beats_management/server/routes/beats/enroll.ts +++ b/x-pack/plugins/beats_management/server/routes/beats/enroll.ts @@ -5,14 +5,14 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { BeatsManagementRouter } from '../../lib/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ensureRawRequest } from '../../../../../../src/core/server/http/router'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { BeatEnrollmentStatus } from '../../lib/types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerBeatEnrollmentRoute = (router: IRouter) => { +export const registerBeatEnrollmentRoute = (router: BeatsManagementRouter) => { // TODO: write to Kibana audit log file https://github.com/elastic/kibana/issues/26024 router.post( { diff --git a/x-pack/plugins/beats_management/server/routes/beats/events.ts b/x-pack/plugins/beats_management/server/routes/beats/events.ts index b87e6d684228..c8e66c6d745d 100644 --- a/x-pack/plugins/beats_management/server/routes/beats/events.ts +++ b/x-pack/plugins/beats_management/server/routes/beats/events.ts @@ -5,11 +5,11 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'kibana/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { ReturnTypeBulkAction } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerBeatEventsRoute = (router: IRouter) => { +export const registerBeatEventsRoute = (router: BeatsManagementRouter) => { router.post( { path: '/api/beats/{beatId}/events', diff --git a/x-pack/plugins/beats_management/server/routes/beats/get.ts b/x-pack/plugins/beats_management/server/routes/beats/get.ts index 8762f325e748..8e2279c7b9c2 100644 --- a/x-pack/plugins/beats_management/server/routes/beats/get.ts +++ b/x-pack/plugins/beats_management/server/routes/beats/get.ts @@ -5,12 +5,12 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'kibana/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { CMBeat } from '../../../common/domain_types'; import { ReturnTypeGet } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerGetBeatRoute = (router: IRouter) => { +export const registerGetBeatRoute = (router: BeatsManagementRouter) => { router.get( { path: '/api/beats/agent/{beatId}/{token?}', diff --git a/x-pack/plugins/beats_management/server/routes/beats/list.ts b/x-pack/plugins/beats_management/server/routes/beats/list.ts index e4108238e3f2..1ae77134f2b2 100644 --- a/x-pack/plugins/beats_management/server/routes/beats/list.ts +++ b/x-pack/plugins/beats_management/server/routes/beats/list.ts @@ -5,13 +5,13 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'kibana/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { CMBeat } from '../../../common/domain_types'; import { ReturnTypeList } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerListAgentsRoute = (router: IRouter) => { +export const registerListAgentsRoute = (router: BeatsManagementRouter) => { router.get( { path: '/api/beats/agents/{listByAndValue*}', @@ -33,7 +33,7 @@ export const registerListAgentsRoute = (router: IRouter) => { requiredLicense: REQUIRED_LICENSES, }, async (context, request, response) => { - const beatsManagement = context.beatsManagement!; + const beatsManagement = context.beatsManagement; const user = beatsManagement.framework.getUser(request); const listByAndValueParts = request.params.listByAndValue?.split('/') ?? []; diff --git a/x-pack/plugins/beats_management/server/routes/beats/tag_assignment.ts b/x-pack/plugins/beats_management/server/routes/beats/tag_assignment.ts index 0397f8ec4398..12205747ed56 100644 --- a/x-pack/plugins/beats_management/server/routes/beats/tag_assignment.ts +++ b/x-pack/plugins/beats_management/server/routes/beats/tag_assignment.ts @@ -5,14 +5,14 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'kibana/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { ReturnTypeBulkAction } from '../../../common/return_types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import type { BeatsTagAssignment } from '../../../public/lib/adapters/beats/adapter_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerTagAssignmentsRoute = (router: IRouter) => { +export const registerTagAssignmentsRoute = (router: BeatsManagementRouter) => { // TODO: write to Kibana audit log file https://github.com/elastic/kibana/issues/26024 router.post( { diff --git a/x-pack/plugins/beats_management/server/routes/beats/tag_removal.ts b/x-pack/plugins/beats_management/server/routes/beats/tag_removal.ts index a04ed81fb183..195810d6bf3e 100644 --- a/x-pack/plugins/beats_management/server/routes/beats/tag_removal.ts +++ b/x-pack/plugins/beats_management/server/routes/beats/tag_removal.ts @@ -5,12 +5,12 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'kibana/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { ReturnTypeBulkAction } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerTagRemovalsRoute = (router: IRouter) => { +export const registerTagRemovalsRoute = (router: BeatsManagementRouter) => { // TODO: write to Kibana audit log file https://github.com/elastic/kibana/issues/26024 router.post( { @@ -33,7 +33,7 @@ export const registerTagRemovalsRoute = (router: IRouter) => { requiredRoles: ['beats_admin'], }, async (context, request, response) => { - const beatsManagement = context.beatsManagement!; + const beatsManagement = context.beatsManagement; const user = beatsManagement.framework.getUser(request); const { removals } = request.body; diff --git a/x-pack/plugins/beats_management/server/routes/beats/update.ts b/x-pack/plugins/beats_management/server/routes/beats/update.ts index 21bd6555b28d..1e9c2db02557 100644 --- a/x-pack/plugins/beats_management/server/routes/beats/update.ts +++ b/x-pack/plugins/beats_management/server/routes/beats/update.ts @@ -5,7 +5,7 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'kibana/server'; +import type { BeatsManagementRouter } from '../../lib/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ensureRawRequest } from '../../../../../../src/core/server/http/router'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; @@ -14,7 +14,7 @@ import { ReturnTypeUpdate } from '../../../common/return_types'; import { internalUser } from '../../lib/adapters/framework/adapter_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerBeatUpdateRoute = (router: IRouter) => { +export const registerBeatUpdateRoute = (router: BeatsManagementRouter) => { // TODO: write to Kibana audit log file (include who did the verification as well) https://github.com/elastic/kibana/issues/26024 router.put( { @@ -44,7 +44,7 @@ export const registerBeatUpdateRoute = (router: IRouter) => { requiredRoles: ['beats_admin'], }, async (context, request, response) => { - const beatsManagement = context.beatsManagement!; + const beatsManagement = context.beatsManagement; const accessToken = request.headers['kbn-beats-access-token'] as string; const { beatId } = request.params; const user = beatsManagement.framework.getUser(request); diff --git a/x-pack/plugins/beats_management/server/routes/configurations/delete.ts b/x-pack/plugins/beats_management/server/routes/configurations/delete.ts index b60d3bd2d5a9..4fee192c9362 100644 --- a/x-pack/plugins/beats_management/server/routes/configurations/delete.ts +++ b/x-pack/plugins/beats_management/server/routes/configurations/delete.ts @@ -5,12 +5,12 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { ReturnTypeBulkDelete } from '../../../common/return_types'; -export const registerDeleteConfigurationBlocksRoute = (router: IRouter) => { +export const registerDeleteConfigurationBlocksRoute = (router: BeatsManagementRouter) => { router.delete( { path: '/api/beats/configurations/{ids}', @@ -26,7 +26,7 @@ export const registerDeleteConfigurationBlocksRoute = (router: IRouter) => { requiredRoles: ['beats_admin'], }, async (context, request, response) => { - const beatsManagement = context.beatsManagement!; + const beatsManagement = context.beatsManagement; const ids = request.params.ids.split(',').filter((id) => id.length > 0); const user = beatsManagement.framework.getUser(request); diff --git a/x-pack/plugins/beats_management/server/routes/configurations/get.ts b/x-pack/plugins/beats_management/server/routes/configurations/get.ts index 6f422ca9ca8b..def913e0204b 100644 --- a/x-pack/plugins/beats_management/server/routes/configurations/get.ts +++ b/x-pack/plugins/beats_management/server/routes/configurations/get.ts @@ -5,13 +5,13 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { ConfigurationBlock } from '../../../common/domain_types'; import { ReturnTypeList } from '../../../common/return_types'; -export const registerGetConfigurationBlocksRoute = (router: IRouter) => { +export const registerGetConfigurationBlocksRoute = (router: BeatsManagementRouter) => { router.get( { path: '/api/beats/configurations/{tagIds}/{page?}', @@ -28,7 +28,7 @@ export const registerGetConfigurationBlocksRoute = (router: IRouter) => { requiredRoles: ['beats_admin'], }, async (context, request, response) => { - const beatsManagement = context.beatsManagement!; + const beatsManagement = context.beatsManagement; const tagIds = request.params.tagIds.split(',').filter((id) => id.length > 0); const user = beatsManagement.framework.getUser(request); const result = await beatsManagement.configurationBlocks.getForTags( diff --git a/x-pack/plugins/beats_management/server/routes/configurations/upsert.ts b/x-pack/plugins/beats_management/server/routes/configurations/upsert.ts index e235b172e7d0..002e98187532 100644 --- a/x-pack/plugins/beats_management/server/routes/configurations/upsert.ts +++ b/x-pack/plugins/beats_management/server/routes/configurations/upsert.ts @@ -7,7 +7,7 @@ import { PathReporter } from 'io-ts/lib/PathReporter'; import { isLeft } from 'fp-ts/lib/Either'; import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { REQUIRED_LICENSES } from '../../../common/constants'; import { ConfigurationBlock, @@ -16,7 +16,7 @@ import { import { ReturnTypeBulkUpsert } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerUpsertConfigurationBlocksRoute = (router: IRouter) => { +export const registerUpsertConfigurationBlocksRoute = (router: BeatsManagementRouter) => { // TODO: write to Kibana audit log file router.put( { @@ -31,7 +31,7 @@ export const registerUpsertConfigurationBlocksRoute = (router: IRouter) => { requiredRoles: ['beats_admin'], }, async (context, request, response) => { - const beatsManagement = context.beatsManagement!; + const beatsManagement = context.beatsManagement; const user = beatsManagement.framework.getUser(request); const input = request.body as ConfigurationBlock[]; diff --git a/x-pack/plugins/beats_management/server/routes/index.ts b/x-pack/plugins/beats_management/server/routes/index.ts index 423ecc85a579..14af4e274b5a 100644 --- a/x-pack/plugins/beats_management/server/routes/index.ts +++ b/x-pack/plugins/beats_management/server/routes/index.ts @@ -3,8 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { IRouter } from 'src/core/server'; +import type { BeatsManagementRouter } from '../lib/types'; import { registerDeleteConfigurationBlocksRoute, registerGetConfigurationBlocksRoute, @@ -29,7 +28,7 @@ import { registerGetBeatConfigurationRoute, } from './beats'; -export const registerRoutes = (router: IRouter) => { +export const registerRoutes = (router: BeatsManagementRouter) => { // configurations registerGetConfigurationBlocksRoute(router); registerDeleteConfigurationBlocksRoute(router); diff --git a/x-pack/plugins/beats_management/server/routes/tags/assignable.ts b/x-pack/plugins/beats_management/server/routes/tags/assignable.ts index 60d4748bf1fa..254d1015221b 100644 --- a/x-pack/plugins/beats_management/server/routes/tags/assignable.ts +++ b/x-pack/plugins/beats_management/server/routes/tags/assignable.ts @@ -5,14 +5,14 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; import { flatten } from 'lodash'; +import type { BeatsManagementRouter } from '../../lib/types'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { BeatTag } from '../../../common/domain_types'; import { ReturnTypeBulkGet } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerAssignableTagsRoute = (router: IRouter) => { +export const registerAssignableTagsRoute = (router: BeatsManagementRouter) => { router.get( { path: '/api/beats/tags/assignable/{beatIds}', diff --git a/x-pack/plugins/beats_management/server/routes/tags/delete.ts b/x-pack/plugins/beats_management/server/routes/tags/delete.ts index 78d0c80d4206..4d689dfe49c5 100644 --- a/x-pack/plugins/beats_management/server/routes/tags/delete.ts +++ b/x-pack/plugins/beats_management/server/routes/tags/delete.ts @@ -5,12 +5,12 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'kibana/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { ReturnTypeBulkDelete } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerDeleteTagsWithIdsRoute = (router: IRouter) => { +export const registerDeleteTagsWithIdsRoute = (router: BeatsManagementRouter) => { router.delete( { path: '/api/beats/tags/{tagIds}', diff --git a/x-pack/plugins/beats_management/server/routes/tags/get.ts b/x-pack/plugins/beats_management/server/routes/tags/get.ts index 48da829aa09e..a4154eaf092a 100644 --- a/x-pack/plugins/beats_management/server/routes/tags/get.ts +++ b/x-pack/plugins/beats_management/server/routes/tags/get.ts @@ -5,13 +5,13 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { BeatTag } from '../../../common/domain_types'; import { ReturnTypeBulkGet } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerGetTagsWithIdsRoute = (router: IRouter) => { +export const registerGetTagsWithIdsRoute = (router: BeatsManagementRouter) => { router.get( { path: '/api/beats/tags/{tagIds}', diff --git a/x-pack/plugins/beats_management/server/routes/tags/list.ts b/x-pack/plugins/beats_management/server/routes/tags/list.ts index ce913cda337c..3faa3d0f6662 100644 --- a/x-pack/plugins/beats_management/server/routes/tags/list.ts +++ b/x-pack/plugins/beats_management/server/routes/tags/list.ts @@ -5,13 +5,13 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'kibana/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { BeatTag } from '../../../common/domain_types'; import { ReturnTypeList } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerListTagsRoute = (router: IRouter) => { +export const registerListTagsRoute = (router: BeatsManagementRouter) => { router.get( { path: '/api/beats/tags', diff --git a/x-pack/plugins/beats_management/server/routes/tags/set.ts b/x-pack/plugins/beats_management/server/routes/tags/set.ts index ef9e181514a5..b80faa5c5c5e 100644 --- a/x-pack/plugins/beats_management/server/routes/tags/set.ts +++ b/x-pack/plugins/beats_management/server/routes/tags/set.ts @@ -5,13 +5,13 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { REQUIRED_LICENSES } from '../../../common/constants'; import { BeatTag } from '../../../common/domain_types'; import { ReturnTypeUpsert } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; -export const registerSetTagRoute = (router: IRouter) => { +export const registerSetTagRoute = (router: BeatsManagementRouter) => { // TODO: write to Kibana audit log file router.put( { diff --git a/x-pack/plugins/beats_management/server/routes/tokens/create.ts b/x-pack/plugins/beats_management/server/routes/tokens/create.ts index 2fd7d4614c57..5a2f04e21fcc 100644 --- a/x-pack/plugins/beats_management/server/routes/tokens/create.ts +++ b/x-pack/plugins/beats_management/server/routes/tokens/create.ts @@ -5,14 +5,14 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { BeatsManagementRouter } from '../../lib/types'; import { REQUIRED_LICENSES } from '../../../common/constants/security'; import { ReturnTypeBulkCreate } from '../../../common/return_types'; import { wrapRouteWithSecurity } from '../wrap_route_with_security'; const DEFAULT_NUM_TOKENS = 1; -export const registerCreateTokenRoute = (router: IRouter) => { +export const registerCreateTokenRoute = (router: BeatsManagementRouter) => { // TODO: write to Kibana audit log file router.post( { diff --git a/x-pack/plugins/beats_management/server/routes/wrap_route_with_security.ts b/x-pack/plugins/beats_management/server/routes/wrap_route_with_security.ts index ad4f8080127b..ab116d2af3b2 100644 --- a/x-pack/plugins/beats_management/server/routes/wrap_route_with_security.ts +++ b/x-pack/plugins/beats_management/server/routes/wrap_route_with_security.ts @@ -4,24 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - KibanaRequest, - KibanaResponseFactory, - RequestHandler, - RequestHandlerContext, - RouteMethod, -} from 'src/core/server'; +import { KibanaRequest, KibanaResponseFactory, RequestHandler, RouteMethod } from 'src/core/server'; import { difference } from 'lodash'; +import type { BeatsManagementRequestHandlerContext } from '../lib/types'; -export function wrapRouteWithSecurity( +export function wrapRouteWithSecurity< + P, + Q, + B, + Context extends BeatsManagementRequestHandlerContext +>( { requiredLicense = [], requiredRoles = [], }: { requiredLicense?: string[]; requiredRoles?: string[] }, - handler: RequestHandler -): RequestHandler { + handler: RequestHandler +): RequestHandler { return async ( - context: RequestHandlerContext, + context: Context, request: KibanaRequest, response: KibanaResponseFactory ) => { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx index b4fbba96e8df..341913a033c0 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/debug.tsx @@ -26,9 +26,11 @@ export const debug: RendererFactory = () => ({ ReactDOM.render(renderDebug(), domNode, () => handlers.done()); - handlers.onResize(() => { - ReactDOM.render(renderDebug(), domNode, () => handlers.done()); - }); + if (handlers.onResize) { + handlers.onResize(() => { + ReactDOM.render(renderDebug(), domNode, () => handlers.done()); + }); + } handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); }, diff --git a/x-pack/plugins/case/common/api/connectors/mappings.ts b/x-pack/plugins/case/common/api/connectors/mappings.ts index b91f9d69e85e..f8e9830fed7c 100644 --- a/x-pack/plugins/case/common/api/connectors/mappings.ts +++ b/x-pack/plugins/case/common/api/connectors/mappings.ts @@ -7,7 +7,6 @@ /* eslint-disable @kbn/eslint/no-restricted-paths */ import * as rt from 'io-ts'; -import { ElasticUser } from '../../../../security_solution/public/cases/containers/types'; import { PushToServiceApiParams as JiraPushToServiceApiParams, Incident as JiraIncident, @@ -24,6 +23,13 @@ import { ResilientFieldsRT } from './resilient'; import { ServiceNowFieldsRT } from './servicenow'; import { JiraFieldsRT } from './jira'; +// Formerly imported from security_solution +export interface ElasticUser { + readonly email?: string | null; + readonly fullName?: string | null; + readonly username?: string | null; +} + export { JiraPushToServiceApiParams, ResilientPushToServiceApiParams, diff --git a/x-pack/plugins/case/server/client/index.test.ts b/x-pack/plugins/case/server/client/index.test.ts index 0c54db11287d..a7f093f42357 100644 --- a/x-pack/plugins/case/server/client/index.test.ts +++ b/x-pack/plugins/case/server/client/index.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, RequestHandlerContext } from 'kibana/server'; +import { KibanaRequest } from 'kibana/server'; import { savedObjectsClientMock } from '../../../../../src/core/server/mocks'; import { createCaseClient } from '.'; import { @@ -19,6 +19,7 @@ import { create } from './cases/create'; import { update } from './cases/update'; import { addComment } from './comments/add'; import { updateAlertsStatus } from './alerts/update_status'; +import type { CasesRequestHandlerContext } from '../types'; jest.mock('./cases/create'); jest.mock('./cases/update'); @@ -32,7 +33,7 @@ const connectorMappingsService = connectorMappingsServiceMock(); const request = {} as KibanaRequest; const savedObjectsClient = savedObjectsClientMock.create(); const userActionService = createUserActionServiceMock(); -const context = {} as RequestHandlerContext; +const context = {} as CasesRequestHandlerContext; const createMock = create as jest.Mock; const updateMock = update as jest.Mock; @@ -57,7 +58,6 @@ describe('createCaseClient()', () => { caseConfigureService, caseService, connectorMappingsService, - context, request, savedObjectsClient, userActionService, @@ -68,7 +68,6 @@ describe('createCaseClient()', () => { caseConfigureService, caseService, connectorMappingsService, - context, request, savedObjectsClient, userActionService, @@ -79,7 +78,6 @@ describe('createCaseClient()', () => { caseConfigureService, caseService, connectorMappingsService, - context, request, savedObjectsClient, userActionService, diff --git a/x-pack/plugins/case/server/client/index.ts b/x-pack/plugins/case/server/client/index.ts index 70eb3282dd24..c4eb1334eb1e 100644 --- a/x-pack/plugins/case/server/client/index.ts +++ b/x-pack/plugins/case/server/client/index.ts @@ -30,7 +30,6 @@ export const createCaseClient = ({ caseConfigureService, caseService, connectorMappingsService, - context, request, savedObjectsClient, userActionService, @@ -40,7 +39,6 @@ export const createCaseClient = ({ caseConfigureService, caseService, connectorMappingsService, - context, request, savedObjectsClient, userActionService, @@ -50,7 +48,6 @@ export const createCaseClient = ({ caseConfigureService, caseService, connectorMappingsService, - context, request, savedObjectsClient, userActionService, @@ -61,7 +58,6 @@ export const createCaseClient = ({ caseConfigureService, caseService, connectorMappingsService, - context, request, savedObjectsClient, userActionService, diff --git a/x-pack/plugins/case/server/client/mocks.ts b/x-pack/plugins/case/server/client/mocks.ts index 78cb7f71cef4..2db00ff8ca6d 100644 --- a/x-pack/plugins/case/server/client/mocks.ts +++ b/x-pack/plugins/case/server/client/mocks.ts @@ -5,7 +5,7 @@ */ import { omit } from 'lodash/fp'; -import { KibanaRequest, RequestHandlerContext } from 'kibana/server'; +import { KibanaRequest } from 'kibana/server'; import { loggingSystemMock } from '../../../../../src/core/server/mocks'; import { actionsClientMock } from '../../../actions/server/mocks'; import { @@ -19,6 +19,7 @@ import { CaseClient } from './types'; import { authenticationMock } from '../routes/api/__fixtures__'; import { createCaseClient } from '.'; import { getActions } from '../routes/api/__mocks__/request_responses'; +import type { CasesRequestHandlerContext } from '../types'; export type CaseClientMock = jest.Mocked; export const createCaseClientMock = (): CaseClientMock => ({ @@ -92,7 +93,7 @@ export const createCaseClientWithMockSavedObjectsClient = async ({ connectorMappingsService, userActionService, alertsService, - context: (omit(omitFromContext, context) as unknown) as RequestHandlerContext, + context: (omit(omitFromContext, context) as unknown) as CasesRequestHandlerContext, }); return { client: caseClient, diff --git a/x-pack/plugins/case/server/client/types.ts b/x-pack/plugins/case/server/client/types.ts index ec83f1ec1ff7..fe80b1ba46a7 100644 --- a/x-pack/plugins/case/server/client/types.ts +++ b/x-pack/plugins/case/server/client/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, SavedObjectsClientContract, RequestHandlerContext } from 'kibana/server'; +import { KibanaRequest, SavedObjectsClientContract } from 'kibana/server'; import { ActionsClient } from '../../../actions/server'; import { CasePostRequest, @@ -23,6 +23,8 @@ import { AlertServiceContract, } from '../services'; import { ConnectorMappingsServiceSetup } from '../services/connector_mappings'; +import type { CasesRequestHandlerContext } from '../types'; + export interface CaseClientCreate { theCase: CasePostRequest; } @@ -43,8 +45,6 @@ export interface CaseClientUpdateAlertsStatus { status: CaseStatuses; } -type PartialExceptFor = Partial & Pick; - export interface CaseClientFactoryArguments { caseConfigureService: CaseConfigureServiceSetup; caseService: CaseServiceSetup; @@ -53,7 +53,7 @@ export interface CaseClientFactoryArguments { savedObjectsClient: SavedObjectsClientContract; userActionService: CaseUserActionServiceSetup; alertsService: AlertServiceContract; - context?: PartialExceptFor; + context?: Omit; } export interface ConfigureFields { diff --git a/x-pack/plugins/case/server/connectors/case/index.ts b/x-pack/plugins/case/server/connectors/case/index.ts index 2195786f718a..07bfb70f1f2a 100644 --- a/x-pack/plugins/case/server/connectors/case/index.ts +++ b/x-pack/plugins/case/server/connectors/case/index.ts @@ -6,7 +6,7 @@ import { curry } from 'lodash'; -import { KibanaRequest, RequestHandlerContext } from 'kibana/server'; +import { KibanaRequest } from 'kibana/server'; import { ActionTypeExecutorResult } from '../../../../actions/common'; import { CasePatchRequest, CasePostRequest } from '../../../common/api'; import { createCaseClient } from '../../client'; @@ -18,12 +18,12 @@ import { CaseActionTypeExecutorOptions, } from './types'; import * as i18n from './translations'; +import type { CasesRequestHandlerContext } from '../../types'; import { GetActionTypeParams } from '..'; const supportedSubActions: string[] = ['create', 'update', 'addComment']; -export const CASE_ACTION_TYPE_ID = '.case'; // action type definition export function getActionType({ logger, @@ -34,7 +34,7 @@ export function getActionType({ alertsService, }: GetActionTypeParams): CaseActionType { return { - id: CASE_ACTION_TYPE_ID, + id: '.case', minimumLicenseRequired: 'basic', name: i18n.NAME, validate: { @@ -78,7 +78,7 @@ async function executor( userActionService, alertsService, // TODO: When case connector is enabled we should figure out how to pass the context. - context: {} as RequestHandlerContext, + context: {} as CasesRequestHandlerContext, }); if (!supportedSubActions.includes(subAction)) { diff --git a/x-pack/plugins/case/server/connectors/index.ts b/x-pack/plugins/case/server/connectors/index.ts index 7fd09e61f214..1a1e8ce718b0 100644 --- a/x-pack/plugins/case/server/connectors/index.ts +++ b/x-pack/plugins/case/server/connectors/index.ts @@ -21,7 +21,6 @@ import { } from '../services'; import { getActionType as getCaseConnector } from './case'; -export { CASE_ACTION_TYPE_ID } from './case'; export interface GetActionTypeParams { logger: Logger; diff --git a/x-pack/plugins/case/server/index.ts b/x-pack/plugins/case/server/index.ts index 19f3a1539672..91b8cdc33a0e 100644 --- a/x-pack/plugins/case/server/index.ts +++ b/x-pack/plugins/case/server/index.ts @@ -8,7 +8,6 @@ import { PluginInitializerContext } from 'kibana/server'; import { ConfigSchema } from './config'; import { CasePlugin } from './plugin'; -export { CASE_ACTION_TYPE_ID } from './connectors'; export { CaseRequestContext } from './types'; export const config = { schema: ConfigSchema }; export const plugin = (initializerContext: PluginInitializerContext) => diff --git a/x-pack/plugins/case/server/plugin.ts b/x-pack/plugins/case/server/plugin.ts index 915656895e8c..0b9712e78c2b 100644 --- a/x-pack/plugins/case/server/plugin.ts +++ b/x-pack/plugins/case/server/plugin.ts @@ -5,14 +5,7 @@ */ import { first, map } from 'rxjs/operators'; -import { - IContextProvider, - KibanaRequest, - Logger, - PluginInitializerContext, - RequestHandler, - RequestHandlerContext, -} from 'kibana/server'; +import { IContextProvider, KibanaRequest, Logger, PluginInitializerContext } from 'kibana/server'; import { CoreSetup, CoreStart } from 'src/core/server'; import { SecurityPluginSetup } from '../../security/server'; @@ -42,6 +35,7 @@ import { } from './services'; import { createCaseClient } from './client'; import { registerConnectors } from './connectors'; +import type { CasesRequestHandlerContext } from './types'; function createConfig$(context: PluginInitializerContext) { return context.config.create().pipe(map((config) => config)); @@ -91,7 +85,7 @@ export class CasePlugin { this.userActionService = await new CaseUserActionService(this.log).setup(); this.alertsService = new AlertService(); - core.http.registerRouteHandlerContext( + core.http.registerRouteHandlerContext( APP_ID, this.createRouteHandlerContext({ core, @@ -103,7 +97,7 @@ export class CasePlugin { }) ); - const router = core.http.createRouter(); + const router = core.http.createRouter(); initCaseApi({ caseService: this.caseService, caseConfigureService: this.caseConfigureService, @@ -128,7 +122,7 @@ export class CasePlugin { this.alertsService!.initialize(core.elasticsearch.client); const getCaseClientWithRequestAndContext = async ( - context: RequestHandlerContext, + context: CasesRequestHandlerContext, request: KibanaRequest ) => { return createCaseClient({ @@ -166,7 +160,7 @@ export class CasePlugin { connectorMappingsService: ConnectorMappingsServiceSetup; userActionService: CaseUserActionServiceSetup; alertsService: AlertServiceContract; - }): IContextProvider, typeof APP_ID> => { + }): IContextProvider => { return async (context, request) => { const [{ savedObjects }] = await core.getStartServices(); return { diff --git a/x-pack/plugins/case/server/routes/api/__fixtures__/route_contexts.ts b/x-pack/plugins/case/server/routes/api/__fixtures__/route_contexts.ts index b2d232dbb7cc..40911496d649 100644 --- a/x-pack/plugins/case/server/routes/api/__fixtures__/route_contexts.ts +++ b/x-pack/plugins/case/server/routes/api/__fixtures__/route_contexts.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext, KibanaRequest } from 'src/core/server'; +import { KibanaRequest } from 'src/core/server'; import { loggingSystemMock, elasticsearchServiceMock } from 'src/core/server/mocks'; import { actionsClientMock } from '../../../../../actions/server/mocks'; import { createCaseClient } from '../../../client'; @@ -16,6 +16,7 @@ import { } from '../../../services'; import { getActions } from '../__mocks__/request_responses'; import { authenticationMock } from '../__fixtures__'; +import type { CasesRequestHandlerContext } from '../../../types'; export const createRouteContext = async (client: any, badAuth = false) => { const actionsMock = actionsClientMock.create(); @@ -49,7 +50,7 @@ export const createRouteContext = async (client: any, badAuth = false) => { getSignalsIndex: () => '.siem-signals', }), }, - } as unknown) as RequestHandlerContext; + } as unknown) as CasesRequestHandlerContext; const connectorMappingsService = await connectorMappingsServicePlugin.setup(); const caseClient = createCaseClient({ diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.test.ts b/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.test.ts index c77d2bd45a79..b744a6dc0481 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.test.ts @@ -59,6 +59,7 @@ describe('GET connectors', () => { }) ); + // @ts-expect-error context.actions = undefined; const res = await routeHandler(context, req, kibanaResponseFactory); diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.test.ts b/x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.test.ts index ff0939fdcce1..4746a203b40f 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/post_push_to_service.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { kibanaResponseFactory, RequestHandler, RequestHandlerContext } from 'src/core/server'; +import { kibanaResponseFactory, RequestHandler } from 'src/core/server'; import { httpServerMock } from 'src/core/server/mocks'; import { @@ -17,6 +17,7 @@ import { import { initPostPushToService } from './post_push_to_service'; import { executePushResponse, newPostPushRequest } from '../../__mocks__/request_responses'; import { CASE_CONFIGURE_PUSH_URL } from '../../../../../common/constants'; +import type { CasesRequestHandlerContext } from '../../../../types'; describe('Post push to service', () => { let routeHandler: RequestHandler; @@ -28,7 +29,7 @@ describe('Post push to service', () => { }, body: newPostPushRequest, }); - let context: RequestHandlerContext; + let context: CasesRequestHandlerContext; beforeAll(async () => { routeHandler = await createRoute(initPostPushToService, 'post'); const spyOnDate = jest.spyOn(global, 'Date') as jest.SpyInstance<{}, []>; @@ -67,7 +68,7 @@ describe('Post push to service', () => { }; }, }, - } as unknown) as RequestHandlerContext; + } as unknown) as CasesRequestHandlerContext; const res = await routeHandler(betterContext, req, kibanaResponseFactory); @@ -81,7 +82,7 @@ describe('Post push to service', () => { const betterContext = ({ ...context, case: null, - } as unknown) as RequestHandlerContext; + } as unknown) as CasesRequestHandlerContext; const res = await routeHandler(betterContext, req, kibanaResponseFactory); expect(res.status).toEqual(400); @@ -94,7 +95,7 @@ describe('Post push to service', () => { const betterContext = ({ ...context, actions: null, - } as unknown) as RequestHandlerContext; + } as unknown) as CasesRequestHandlerContext; const res = await routeHandler(betterContext, req, kibanaResponseFactory); expect(res.status).toEqual(404); diff --git a/x-pack/plugins/case/server/routes/api/types.ts b/x-pack/plugins/case/server/routes/api/types.ts index 0b93d844fe9a..c01ec3d232a0 100644 --- a/x-pack/plugins/case/server/routes/api/types.ts +++ b/x-pack/plugins/case/server/routes/api/types.ts @@ -4,19 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; -import { +import type { CaseConfigureServiceSetup, CaseServiceSetup, CaseUserActionServiceSetup, ConnectorMappingsServiceSetup, } from '../../services'; +import type { CasesRouter } from '../../types'; + export interface RouteDeps { caseConfigureService: CaseConfigureServiceSetup; caseService: CaseServiceSetup; connectorMappingsService: ConnectorMappingsServiceSetup; - router: IRouter; + router: CasesRouter; userActionService: CaseUserActionServiceSetup; } diff --git a/x-pack/plugins/case/server/types.ts b/x-pack/plugins/case/server/types.ts index d0dfc26aa7b8..34be3a89716a 100644 --- a/x-pack/plugins/case/server/types.ts +++ b/x-pack/plugins/case/server/types.ts @@ -4,19 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AppRequestContext } from '../../security_solution/server/types'; +import type { IRouter, RequestHandlerContext } from 'src/core/server'; +import type { AppRequestContext } from '../../security_solution/server'; +import type { ActionsApiRequestHandlerContext } from '../../actions/server'; import { CaseClient } from './client'; export interface CaseRequestContext { getCaseClient: () => CaseClient; } -declare module 'src/core/server' { - interface RequestHandlerContext { - case?: CaseRequestContext; - // TODO: Remove when triggers_ui do not import case's types. - // PR https://github.com/elastic/kibana/pull/84587. - securitySolution?: AppRequestContext; - } +/** + * @internal + */ +export interface CasesRequestHandlerContext extends RequestHandlerContext { + case: CaseRequestContext; + actions: ActionsApiRequestHandlerContext; + // TODO: Remove when triggers_ui do not import case's types. + // PR https://github.com/elastic/kibana/pull/84587. + securitySolution: AppRequestContext; } + +/** + * @internal + */ +export type CasesRouter = IRouter; diff --git a/x-pack/plugins/cross_cluster_replication/server/plugin.ts b/x-pack/plugins/cross_cluster_replication/server/plugin.ts index d40a53f28987..3fb488dde4c3 100644 --- a/x-pack/plugins/cross_cluster_replication/server/plugin.ts +++ b/x-pack/plugins/cross_cluster_replication/server/plugin.ts @@ -4,12 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -declare module 'src/core/server' { - interface RequestHandlerContext { - crossClusterReplication?: CrossClusterReplicationContext; - } -} - import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; @@ -20,12 +14,11 @@ import { Logger, PluginInitializerContext, LegacyAPICaller, - ILegacyScopedClusterClient, } from 'src/core/server'; import { Index } from '../../index_management/server'; import { PLUGIN } from '../common/constants'; -import { Dependencies } from './types'; +import type { Dependencies, CcrRequestHandlerContext } from './types'; import { registerApiRoutes } from './routes'; import { License } from './services'; import { elasticsearchJsPlugin } from './client/elasticsearch_ccr'; @@ -33,10 +26,6 @@ import { CrossClusterReplicationConfig } from './config'; import { isEsError } from './shared_imports'; import { formatEsError } from './lib/format_es_error'; -interface CrossClusterReplicationContext { - client: ILegacyScopedClusterClient; -} - async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { const [core] = await getStartServices(); // Extend the elasticsearchJs client with additional endpoints. @@ -137,12 +126,15 @@ export class CrossClusterReplicationServerPlugin implements Plugin { - this.ccrEsClient = this.ccrEsClient ?? (await getCustomEsClient(getStartServices)); - return { - client: this.ccrEsClient.asScoped(request), - }; - }); + http.registerRouteHandlerContext( + 'crossClusterReplication', + async (ctx, request) => { + this.ccrEsClient = this.ccrEsClient ?? (await getCustomEsClient(getStartServices)); + return { + client: this.ccrEsClient.asScoped(request), + }; + } + ); registerApiRoutes({ router: http.createRouter(), diff --git a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_update_route.ts b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_update_route.ts index 521de7718097..463df5cc2079 100644 --- a/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_update_route.ts +++ b/x-pack/plugins/cross_cluster_replication/server/routes/api/follower_index/register_update_route.ts @@ -49,7 +49,7 @@ export const registerUpdateRoute = ({ try { const { follower_indices: followerIndices, - } = await context.crossClusterReplication!.client.callAsCurrentUser('ccr.info', { id }); + } = await context.crossClusterReplication.client.callAsCurrentUser('ccr.info', { id }); const followerIndexInfo = followerIndices && followerIndices[0]; diff --git a/x-pack/plugins/cross_cluster_replication/server/services/license.ts b/x-pack/plugins/cross_cluster_replication/server/services/license.ts index 5424092a01ee..cccc54dcc058 100644 --- a/x-pack/plugins/cross_cluster_replication/server/services/license.ts +++ b/x-pack/plugins/cross_cluster_replication/server/services/license.ts @@ -4,12 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { Logger } from 'src/core/server'; -import { - KibanaRequest, - KibanaResponseFactory, - RequestHandler, - RequestHandlerContext, -} from 'src/core/server'; +import { KibanaRequest, KibanaResponseFactory, RequestHandler } from 'src/core/server'; +import type { CcrRequestHandlerContext } from '../types'; import { LicensingPluginSetup } from '../../../licensing/server'; import { LicenseType } from '../../../licensing/common/types'; @@ -59,11 +55,11 @@ export class License { }); } - guardApiRoute(handler: RequestHandler) { + guardApiRoute(handler: RequestHandler) { const license = this; return function licenseCheck( - ctx: RequestHandlerContext, + ctx: CcrRequestHandlerContext, request: KibanaRequest, response: KibanaResponseFactory ) { diff --git a/x-pack/plugins/cross_cluster_replication/server/types.ts b/x-pack/plugins/cross_cluster_replication/server/types.ts index 62c96b48c437..48ded67566b3 100644 --- a/x-pack/plugins/cross_cluster_replication/server/types.ts +++ b/x-pack/plugins/cross_cluster_replication/server/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; +import { IRouter, ILegacyScopedClusterClient, RequestHandlerContext } from 'src/core/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { IndexManagementPluginSetup } from '../../index_management/server'; @@ -21,10 +21,24 @@ export interface Dependencies { } export interface RouteDependencies { - router: IRouter; + router: CcrPluginRouter; license: License; lib: { isEsError: typeof isEsError; formatEsError: typeof formatEsError; }; } + +/** + * @internal + */ +export interface CcrRequestHandlerContext extends RequestHandlerContext { + crossClusterReplication: { + client: ILegacyScopedClusterClient; + }; +} + +/** + * @internal + */ +type CcrPluginRouter = IRouter; diff --git a/x-pack/plugins/data_enhanced/common/search/session/types.ts b/x-pack/plugins/data_enhanced/common/search/session/types.ts index ada7988c31f3..9eefdf43aa24 100644 --- a/x-pack/plugins/data_enhanced/common/search/session/types.ts +++ b/x-pack/plugins/data_enhanced/common/search/session/types.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { SearchSessionStatus } from './'; + export interface SearchSessionSavedObjectAttributes { /** * User-facing session name to be displayed in session management @@ -24,7 +26,7 @@ export interface SearchSessionSavedObjectAttributes { /** * status */ - status: string; + status: SearchSessionStatus; /** * urlGeneratorId */ @@ -44,7 +46,6 @@ export interface SearchSessionSavedObjectAttributes { */ idMapping: Record; } - export interface SearchSessionRequestInfo { /** * ID of the async search request diff --git a/x-pack/plugins/data_enhanced/config.ts b/x-pack/plugins/data_enhanced/config.ts index 4c90b1fb4c81..981c39801983 100644 --- a/x-pack/plugins/data_enhanced/config.ts +++ b/x-pack/plugins/data_enhanced/config.ts @@ -15,6 +15,12 @@ export const configSchema = schema.object({ inMemTimeout: schema.duration({ defaultValue: '1m' }), maxUpdateRetries: schema.number({ defaultValue: 3 }), defaultExpiration: schema.duration({ defaultValue: '7d' }), + management: schema.object({ + maxSessions: schema.number({ defaultValue: 10000 }), + refreshInterval: schema.duration({ defaultValue: '10s' }), + refreshTimeout: schema.duration({ defaultValue: '1m' }), + expiresSoonWarning: schema.duration({ defaultValue: '1d' }), + }), }), }), }); diff --git a/x-pack/plugins/data_enhanced/kibana.json b/x-pack/plugins/data_enhanced/kibana.json index 3951468f6e56..037f52fcb4b0 100644 --- a/x-pack/plugins/data_enhanced/kibana.json +++ b/x-pack/plugins/data_enhanced/kibana.json @@ -2,15 +2,8 @@ "id": "dataEnhanced", "version": "8.0.0", "kibanaVersion": "kibana", - "configPath": [ - "xpack", "data_enhanced" - ], - "requiredPlugins": [ - "bfetch", - "data", - "features", - "taskManager" - ], + "configPath": ["xpack", "data_enhanced"], + "requiredPlugins": ["bfetch", "data", "features", "management", "share", "taskManager"], "optionalPlugins": ["kibanaUtils", "usageCollection"], "server": true, "ui": true, diff --git a/x-pack/plugins/data_enhanced/public/plugin.ts b/x-pack/plugins/data_enhanced/public/plugin.ts index fed2b4e71ab5..add7a966fee3 100644 --- a/x-pack/plugins/data_enhanced/public/plugin.ts +++ b/x-pack/plugins/data_enhanced/public/plugin.ts @@ -8,10 +8,13 @@ import React from 'react'; import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { BfetchPublicSetup } from '../../../../src/plugins/bfetch/public'; +import { ManagementSetup } from '../../../../src/plugins/management/public'; +import { SharePluginStart } from '../../../../src/plugins/share/public'; import { setAutocompleteService } from './services'; import { setupKqlQuerySuggestionProvider, KUERY_LANGUAGE_NAME } from './autocomplete'; import { EnhancedSearchInterceptor } from './search/search_interceptor'; +import { registerSearchSessionsMgmt } from './search/sessions_mgmt'; import { toMountPoint } from '../../../../src/plugins/kibana_react/public'; import { createConnectedSearchSessionIndicator } from './search'; import { ConfigSchema } from '../config'; @@ -19,9 +22,11 @@ import { ConfigSchema } from '../config'; export interface DataEnhancedSetupDependencies { bfetch: BfetchPublicSetup; data: DataPublicPluginSetup; + management: ManagementSetup; } export interface DataEnhancedStartDependencies { data: DataPublicPluginStart; + share: SharePluginStart; } export type DataEnhancedSetup = ReturnType; @@ -30,12 +35,13 @@ export type DataEnhancedStart = ReturnType; export class DataEnhancedPlugin implements Plugin { private enhancedSearchInterceptor!: EnhancedSearchInterceptor; + private config!: ConfigSchema; constructor(private initializerContext: PluginInitializerContext) {} public setup( core: CoreSetup, - { bfetch, data }: DataEnhancedSetupDependencies + { bfetch, data, management }: DataEnhancedSetupDependencies ) { data.autocomplete.addQuerySuggestionProvider( KUERY_LANGUAGE_NAME, @@ -57,12 +63,18 @@ export class DataEnhancedPlugin searchInterceptor: this.enhancedSearchInterceptor, }, }); + + this.config = this.initializerContext.config.get(); + if (this.config.search.sessions.enabled) { + const { management: sessionsMgmtConfig } = this.config.search.sessions; + registerSearchSessionsMgmt(core, sessionsMgmtConfig, { management }); + } } public start(core: CoreStart, plugins: DataEnhancedStartDependencies) { setAutocompleteService(plugins.data.autocomplete); - if (this.initializerContext.config.get().search.sessions.enabled) { + if (this.config.search.sessions.enabled) { core.chrome.setBreadcrumbsAppendExtension({ content: toMountPoint( React.createElement( diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/__mocks__/index.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/__mocks__/index.tsx new file mode 100644 index 000000000000..e9fc8e6ac6bf --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/__mocks__/index.tsx @@ -0,0 +1,18 @@ +/* + * 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, { ReactNode } from 'react'; +import { IntlProvider } from 'react-intl'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { UrlGeneratorsStart } from '../../../../../../../src/plugins/share/public/url_generators'; + +export function LocaleWrapper({ children }: { children?: ReactNode }) { + return {children}; +} + +export const mockUrls = ({ + getUrlGenerator: (id: string) => ({ createUrl: () => `hello-cool-${id}-url` }), +} as unknown) as UrlGeneratorsStart; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx new file mode 100644 index 000000000000..27f1482a4d20 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx @@ -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 { CoreSetup } from 'kibana/public'; +import type { ManagementAppMountParams } from 'src/plugins/management/public'; +import type { + AppDependencies, + IManagementSectionsPluginsSetup, + IManagementSectionsPluginsStart, + SessionsMgmtConfigSchema, +} from '../'; +import { APP } from '../'; +import { SearchSessionsMgmtAPI } from '../lib/api'; +import { AsyncSearchIntroDocumentation } from '../lib/documentation'; +import { renderApp } from './render'; + +export class SearchSessionsMgmtApp { + constructor( + private coreSetup: CoreSetup, + private config: SessionsMgmtConfigSchema, + private params: ManagementAppMountParams, + private pluginsSetup: IManagementSectionsPluginsSetup + ) {} + + public async mountManagementSection() { + const { coreSetup, params, pluginsSetup } = this; + const [coreStart, pluginsStart] = await coreSetup.getStartServices(); + + const { + chrome: { docTitle }, + http, + docLinks, + i18n, + notifications, + uiSettings, + application, + } = coreStart; + const { data, share } = pluginsStart; + + const pluginName = APP.getI18nName(); + docTitle.change(pluginName); + params.setBreadcrumbs([{ text: pluginName }]); + + const { sessionsClient } = data.search; + const api = new SearchSessionsMgmtAPI(sessionsClient, this.config, { + notifications, + urls: share.urlGenerators, + application, + }); + + const documentation = new AsyncSearchIntroDocumentation(docLinks); + + const dependencies: AppDependencies = { + plugins: pluginsSetup, + config: this.config, + documentation, + core: coreStart, + api, + http, + i18n, + uiSettings, + share, + }; + + const { element } = params; + const unmountAppCb = renderApp(element, dependencies); + + return () => { + docTitle.reset(); + unmountAppCb(); + }; + } +} + +export { renderApp }; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/render.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/render.tsx new file mode 100644 index 000000000000..f5ee35fcff9a --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/render.tsx @@ -0,0 +1,39 @@ +/* + * 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 { render, unmountComponentAtNode } from 'react-dom'; +import { AppDependencies } from '../'; +import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public'; +import { SearchSessionsMgmtMain } from '../components/main'; + +export const renderApp = ( + elem: HTMLElement | null, + { i18n, uiSettings, ...homeDeps }: AppDependencies +) => { + if (!elem) { + return () => undefined; + } + + const { Context: I18nContext } = i18n; + // uiSettings is required by the listing table to format dates in the timezone from Settings + const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ + uiSettings, + }); + + render( + + + + + , + elem + ); + + return () => { + unmountComponentAtNode(elem); + }; +}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/cancel_button.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/cancel_button.tsx new file mode 100644 index 000000000000..8f4c8845de23 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/cancel_button.tsx @@ -0,0 +1,89 @@ +/* + * 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 { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { useState } from 'react'; +import { SearchSessionsMgmtAPI } from '../../lib/api'; +import { TableText } from '../'; +import { OnActionComplete } from './types'; + +interface CancelButtonProps { + id: string; + name: string; + api: SearchSessionsMgmtAPI; + onActionComplete: OnActionComplete; +} + +const CancelConfirm = ({ + onConfirmDismiss, + ...props +}: CancelButtonProps & { onConfirmDismiss: () => void }) => { + const { id, name, api, onActionComplete } = props; + const [isLoading, setIsLoading] = useState(false); + + const title = i18n.translate('xpack.data.mgmt.searchSessions.cancelModal.title', { + defaultMessage: 'Cancel search session', + }); + const confirm = i18n.translate('xpack.data.mgmt.searchSessions.cancelModal.cancelButton', { + defaultMessage: 'Cancel', + }); + const cancel = i18n.translate('xpack.data.mgmt.searchSessions.cancelModal.dontCancelButton', { + defaultMessage: 'Dismiss', + }); + const message = i18n.translate('xpack.data.mgmt.searchSessions.cancelModal.message', { + defaultMessage: `Canceling the search session \'{name}\' will expire any cached results, so that quick restore will no longer be available. You will still be able to re-run it, using the reload action.`, + values: { + name, + }, + }); + + return ( + + { + setIsLoading(true); + await api.sendCancel(id); + onActionComplete(); + }} + confirmButtonText={confirm} + confirmButtonDisabled={isLoading} + cancelButtonText={cancel} + defaultFocusedButton="confirm" + buttonColor="danger" + > + {message} + + + ); +}; + +export const CancelButton = (props: CancelButtonProps) => { + const [showConfirm, setShowConfirm] = useState(false); + + const onClick = () => { + setShowConfirm(true); + }; + + const onConfirmDismiss = () => { + setShowConfirm(false); + }; + + return ( + <> + + + + {showConfirm ? : null} + + ); +}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/extend_button.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/extend_button.tsx new file mode 100644 index 000000000000..4c8a7b021768 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/extend_button.tsx @@ -0,0 +1,89 @@ +/* + * 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 { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { useState } from 'react'; +import { SearchSessionsMgmtAPI } from '../../lib/api'; +import { TableText } from '../'; +import { OnActionComplete } from './types'; + +interface ExtendButtonProps { + id: string; + name: string; + api: SearchSessionsMgmtAPI; + onActionComplete: OnActionComplete; +} + +const ExtendConfirm = ({ + onConfirmDismiss, + ...props +}: ExtendButtonProps & { onConfirmDismiss: () => void }) => { + const { id, name, api, onActionComplete } = props; + const [isLoading, setIsLoading] = useState(false); + + const title = i18n.translate('xpack.data.mgmt.searchSessions.extendModal.title', { + defaultMessage: 'Extend search session expiration', + }); + const confirm = i18n.translate('xpack.data.mgmt.searchSessions.extendModal.extendButton', { + defaultMessage: 'Extend', + }); + const extend = i18n.translate('xpack.data.mgmt.searchSessions.extendModal.dontExtendButton', { + defaultMessage: 'Cancel', + }); + const message = i18n.translate('xpack.data.mgmt.searchSessions.extendModal.extendMessage', { + defaultMessage: "When would you like the search session '{name}' to expire?", + values: { + name, + }, + }); + + return ( + + { + setIsLoading(true); + await api.sendExtend(id, '1'); + onActionComplete(); + }} + confirmButtonText={confirm} + confirmButtonDisabled={isLoading} + cancelButtonText={extend} + defaultFocusedButton="confirm" + buttonColor="primary" + > + {message} + + + ); +}; + +export const ExtendButton = (props: ExtendButtonProps) => { + const [showConfirm, setShowConfirm] = useState(false); + + const onClick = () => { + setShowConfirm(true); + }; + + const onConfirmDismiss = () => { + setShowConfirm(false); + }; + + return ( + <> + + + + {showConfirm ? : null} + + ); +}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/get_action.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/get_action.tsx new file mode 100644 index 000000000000..5bf0fbda5b5c --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/get_action.tsx @@ -0,0 +1,53 @@ +/* + * 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 { IClickActionDescriptor } from '../'; +import extendSessionIcon from '../../icons/extend_session.svg'; +import { SearchSessionsMgmtAPI } from '../../lib/api'; +import { UISession } from '../../types'; +import { CancelButton } from './cancel_button'; +import { ExtendButton } from './extend_button'; +import { ReloadButton } from './reload_button'; +import { ACTION, OnActionComplete } from './types'; + +export const getAction = ( + api: SearchSessionsMgmtAPI, + actionType: string, + { id, name, reloadUrl }: UISession, + onActionComplete: OnActionComplete +): IClickActionDescriptor | null => { + switch (actionType) { + case ACTION.CANCEL: + return { + iconType: 'crossInACircleFilled', + textColor: 'default', + label: , + }; + + case ACTION.RELOAD: + return { + iconType: 'refresh', + textColor: 'default', + label: , + }; + + case ACTION.EXTEND: + return { + iconType: extendSessionIcon, + textColor: 'default', + label: , + }; + + default: + // eslint-disable-next-line no-console + console.error(`Unknown action: ${actionType}`); + } + + // Unknown action: do not show + + return null; +}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/index.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/index.tsx new file mode 100644 index 000000000000..82b4d84aa7ea --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/index.tsx @@ -0,0 +1,8 @@ +/* + * 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 { PopoverActionsMenu } from './popover_actions'; +export * from './types'; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/popover_actions.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/popover_actions.tsx new file mode 100644 index 000000000000..b9b915c0b17c --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/popover_actions.tsx @@ -0,0 +1,135 @@ +/* + * 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. + */ + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + EuiButtonIcon, + EuiContextMenu, + EuiContextMenuPanelDescriptor, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPopover, + EuiTextProps, + EuiToolTip, +} from '@elastic/eui'; +import { + EuiContextMenuPanelItemDescriptorEntry, + EuiContextMenuPanelItemSeparator, +} from '@elastic/eui/src/components/context_menu/context_menu'; +import { i18n } from '@kbn/i18n'; +import React, { ReactElement, useState } from 'react'; +import { TableText } from '../'; +import { SearchSessionsMgmtAPI } from '../../lib/api'; +import { UISession } from '../../types'; +import { getAction } from './get_action'; +import { ACTION, OnActionComplete } from './types'; + +// interfaces +interface PopoverActionProps { + textColor?: EuiTextProps['color']; + iconType: string; + children: string | ReactElement; +} + +interface PopoverActionItemsProps { + session: UISession; + api: SearchSessionsMgmtAPI; + onActionComplete: OnActionComplete; +} + +// helper +const PopoverAction = ({ textColor, iconType, children, ...props }: PopoverActionProps) => ( + + + + + + {children} + + +); + +export const PopoverActionsMenu = ({ api, onActionComplete, session }: PopoverActionItemsProps) => { + const [isPopoverOpen, setPopover] = useState(false); + + const onPopoverClick = () => { + setPopover(!isPopoverOpen); + }; + + const closePopover = () => { + setPopover(false); + }; + + const renderPopoverButton = () => ( + + + + ); + + const actions = session.actions || []; + // Generic set of actions - up to the API to return what is available + const items = actions.reduce((itemSet, actionType) => { + const actionDef = getAction(api, actionType, session, onActionComplete); + if (actionDef) { + const { label, textColor, iconType } = actionDef; + + // add a line above the delete action (when there are multiple) + // NOTE: Delete action MUST be the final action[] item + if (actions.length > 1 && actionType === ACTION.CANCEL) { + itemSet.push({ isSeparator: true, key: 'separadorable' }); + } + + return [ + ...itemSet, + { + key: `action-${actionType}`, + name: ( + + {label} + + ), + }, + ]; + } + return itemSet; + }, [] as Array); + + const panels: EuiContextMenuPanelDescriptor[] = [{ id: 0, items }]; + + return actions.length ? ( + + + + ) : null; +}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/reload_button.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/reload_button.tsx new file mode 100644 index 000000000000..9a98ab204477 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/reload_button.tsx @@ -0,0 +1,32 @@ +/* + * 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 { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; +import { TableText } from '../'; +import { SearchSessionsMgmtAPI } from '../../lib/api'; + +interface ReloadButtonProps { + api: SearchSessionsMgmtAPI; + reloadUrl: string; +} + +export const ReloadButton = (props: ReloadButtonProps) => { + function onClick() { + props.api.reloadSearchSession(props.reloadUrl); + } + + return ( + <> + + + + + ); +}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/types.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/types.ts new file mode 100644 index 000000000000..4b81fd7fda9a --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/types.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. + */ + +export type OnActionComplete = () => void; + +export enum ACTION { + EXTEND = 'extend', + CANCEL = 'cancel', + RELOAD = 'reload', +} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/index.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/index.tsx new file mode 100644 index 000000000000..ffb0992469a8 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/index.tsx @@ -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 { EuiLinkProps, EuiText, EuiTextProps } from '@elastic/eui'; +import React from 'react'; +import extendSessionIcon from '../icons/extend_session.svg'; + +export { OnActionComplete, PopoverActionsMenu } from './actions'; + +export const TableText = ({ children, ...props }: EuiTextProps) => { + return ( + + {children} + + ); +}; + +export interface IClickActionDescriptor { + label: string | React.ReactElement; + iconType: 'trash' | 'cancel' | typeof extendSessionIcon; + textColor: EuiTextProps['color']; +} + +export interface IHrefActionDescriptor { + label: string; + props: EuiLinkProps; +} + +export interface StatusDef { + textColor?: EuiTextProps['color']; + icon?: React.ReactElement; + label: React.ReactElement; + toolTipContent: string; +} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx new file mode 100644 index 000000000000..e01d1a28c5e5 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx @@ -0,0 +1,93 @@ +/* + * 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 { MockedKeys } from '@kbn/utility-types/jest'; +import { mount, ReactWrapper } from 'enzyme'; +import { CoreSetup, CoreStart, DocLinksStart } from 'kibana/public'; +import moment from 'moment'; +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { coreMock } from 'src/core/public/mocks'; +import { SessionsClient } from 'src/plugins/data/public/search'; +import { SessionsMgmtConfigSchema } from '..'; +import { SearchSessionsMgmtAPI } from '../lib/api'; +import { AsyncSearchIntroDocumentation } from '../lib/documentation'; +import { LocaleWrapper, mockUrls } from '../__mocks__'; +import { SearchSessionsMgmtMain } from './main'; + +let mockCoreSetup: MockedKeys; +let mockCoreStart: MockedKeys; +let mockConfig: SessionsMgmtConfigSchema; +let sessionsClient: SessionsClient; +let api: SearchSessionsMgmtAPI; + +describe('Background Search Session Management Main', () => { + beforeEach(() => { + mockCoreSetup = coreMock.createSetup(); + mockCoreStart = coreMock.createStart(); + mockConfig = { + expiresSoonWarning: moment.duration(1, 'days'), + maxSessions: 2000, + refreshInterval: moment.duration(1, 'seconds'), + refreshTimeout: moment.duration(10, 'minutes'), + }; + + sessionsClient = new SessionsClient({ http: mockCoreSetup.http }); + + api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { + urls: mockUrls, + notifications: mockCoreStart.notifications, + application: mockCoreStart.application, + }); + }); + + describe('renders', () => { + const docLinks: DocLinksStart = { + ELASTIC_WEBSITE_URL: 'boo/', + DOC_LINK_VERSION: '#foo', + links: {} as any, + }; + + let main: ReactWrapper; + + beforeEach(async () => { + mockCoreSetup.uiSettings.get.mockImplementation((key: string) => { + return key === 'dateFormat:tz' ? 'UTC' : null; + }); + + await act(async () => { + main = mount( + + + + ); + }); + }); + + test('page title', () => { + expect(main.find('h1').text()).toBe('Search Sessions'); + }); + + test('documentation link', () => { + const docLink = main.find('a[href]').first(); + expect(docLink.text()).toBe('Documentation'); + expect(docLink.prop('href')).toBe( + 'boo/guide/en/elasticsearch/reference/#foo/async-search-intro.html' + ); + }); + + test('table is present', () => { + expect(main.find(`[data-test-subj="search-sessions-mgmt-table"]`).exists()).toBe(true); + }); + }); +}); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx new file mode 100644 index 000000000000..80c6a580dd18 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx @@ -0,0 +1,79 @@ +/* + * 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 { + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiPageBody, + EuiPageContent, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import type { CoreStart, HttpStart } from 'kibana/public'; +import React from 'react'; +import type { SessionsMgmtConfigSchema } from '../'; +import type { SearchSessionsMgmtAPI } from '../lib/api'; +import type { AsyncSearchIntroDocumentation } from '../lib/documentation'; +import { TableText } from './'; +import { SearchSessionsMgmtTable } from './table'; + +interface Props { + documentation: AsyncSearchIntroDocumentation; + core: CoreStart; + api: SearchSessionsMgmtAPI; + http: HttpStart; + timezone: string; + config: SessionsMgmtConfigSchema; +} + +export function SearchSessionsMgmtMain({ documentation, ...tableProps }: Props) { + return ( + + + + + +

+ +

+
+
+ + + + + +
+ + +

+ +

+
+ + + + +
+
+ ); +} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.test.tsx new file mode 100644 index 000000000000..706001ac4214 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.test.tsx @@ -0,0 +1,132 @@ +/* + * 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 { EuiTextProps, EuiToolTipProps } from '@elastic/eui'; +import { mount } from 'enzyme'; +import React from 'react'; +import { SearchSessionStatus } from '../../../../common/search'; +import { UISession } from '../types'; +import { LocaleWrapper } from '../__mocks__'; +import { getStatusText, StatusIndicator } from './status'; + +let tz: string; +let session: UISession; + +const mockNowTime = new Date(); +mockNowTime.setTime(1607026176061); + +describe('Background Search Session management status labels', () => { + beforeEach(() => { + tz = 'Browser'; + session = { + name: 'amazing test', + id: 'wtywp9u2802hahgp-gsla', + restoreUrl: '/app/great-app-url/#45', + reloadUrl: '/app/great-app-url/#45', + appId: 'security', + status: SearchSessionStatus.IN_PROGRESS, + created: '2020-12-02T00:19:32Z', + expires: '2020-12-07T00:19:32Z', + }; + }); + + describe('getStatusText', () => { + test('in progress', () => { + expect(getStatusText(SearchSessionStatus.IN_PROGRESS)).toBe('In progress'); + }); + test('expired', () => { + expect(getStatusText(SearchSessionStatus.EXPIRED)).toBe('Expired'); + }); + test('cancelled', () => { + expect(getStatusText(SearchSessionStatus.CANCELLED)).toBe('Cancelled'); + }); + test('complete', () => { + expect(getStatusText(SearchSessionStatus.COMPLETE)).toBe('Complete'); + }); + test('error', () => { + expect(getStatusText('error')).toBe('Error'); + }); + }); + + describe('StatusIndicator', () => { + test('render in progress', () => { + const statusIndicator = mount( + + + + ); + + const label = statusIndicator.find( + `.euiText[data-test-subj="sessionManagementStatusLabel"][data-test-status="in_progress"]` + ); + expect(label.text()).toMatchInlineSnapshot(`"In progress"`); + }); + + test('complete', () => { + session.status = SearchSessionStatus.COMPLETE; + + const statusIndicator = mount( + + + + ); + + const label = statusIndicator + .find(`[data-test-subj="sessionManagementStatusLabel"][data-test-status="complete"]`) + .first(); + expect((label.props() as EuiTextProps).color).toBe('secondary'); + expect(label.text()).toBe('Complete'); + }); + + test('complete - expires soon', () => { + session.status = SearchSessionStatus.COMPLETE; + + const statusIndicator = mount( + + + + ); + + const tooltip = statusIndicator.find('EuiToolTip'); + expect((tooltip.first().props() as EuiToolTipProps).content).toMatchInlineSnapshot( + `"Expires on 6 Dec, 2020, 19:19:32"` + ); + }); + + test('expired', () => { + session.status = SearchSessionStatus.EXPIRED; + + const statusIndicator = mount( + + + + ); + + const label = statusIndicator + .find(`[data-test-subj="sessionManagementStatusLabel"][data-test-status="expired"]`) + .first(); + expect(label.text()).toBe('Expired'); + }); + + test('error handling', () => { + session.status = SearchSessionStatus.COMPLETE; + (session as any).created = null; + (session as any).expires = null; + + const statusIndicator = mount( + + + + ); + + // no unhandled errors + const tooltip = statusIndicator.find('EuiToolTip'); + expect((tooltip.first().props() as EuiToolTipProps).content).toMatchInlineSnapshot( + `"Expires on unknown"` + ); + }); + }); +}); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.tsx new file mode 100644 index 000000000000..8e0946c14028 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/status.tsx @@ -0,0 +1,203 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLoadingSpinner, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { ReactElement } from 'react'; +import { SearchSessionStatus } from '../../../../common/search'; +import { dateString } from '../lib/date_string'; +import { UISession } from '../types'; +import { StatusDef as StatusAttributes, TableText } from './'; + +// Shared helper function +export const getStatusText = (statusType: string): string => { + switch (statusType) { + case SearchSessionStatus.IN_PROGRESS: + return i18n.translate('xpack.data.mgmt.searchSessions.status.label.inProgress', { + defaultMessage: 'In progress', + }); + case SearchSessionStatus.EXPIRED: + return i18n.translate('xpack.data.mgmt.searchSessions.status.label.expired', { + defaultMessage: 'Expired', + }); + case SearchSessionStatus.CANCELLED: + return i18n.translate('xpack.data.mgmt.searchSessions.status.label.cancelled', { + defaultMessage: 'Cancelled', + }); + case SearchSessionStatus.COMPLETE: + return i18n.translate('xpack.data.mgmt.searchSessions.status.label.complete', { + defaultMessage: 'Complete', + }); + case SearchSessionStatus.ERROR: + return i18n.translate('xpack.data.mgmt.searchSessions.status.label.error', { + defaultMessage: 'Error', + }); + default: + // eslint-disable-next-line no-console + console.error(`Unknown status ${statusType}`); + return statusType; + } +}; + +interface StatusIndicatorProps { + now?: string; + session: UISession; + timezone: string; +} + +// Get the fields needed to show each status type +// can throw errors around date conversions +const getStatusAttributes = ({ + now, + session, + timezone, +}: StatusIndicatorProps): StatusAttributes | null => { + let expireDate: string; + if (session.expires) { + expireDate = dateString(session.expires!, timezone); + } else { + expireDate = i18n.translate('xpack.data.mgmt.searchSessions.status.expireDateUnknown', { + defaultMessage: 'unknown', + }); + } + + switch (session.status) { + case SearchSessionStatus.IN_PROGRESS: + try { + return { + textColor: 'default', + icon: , + label: {getStatusText(session.status)}, + toolTipContent: i18n.translate( + 'xpack.data.mgmt.searchSessions.status.message.createdOn', + { + defaultMessage: 'Expires on {expireDate}', + values: { expireDate }, + } + ), + }; + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + throw new Error(`Could not instantiate a createdDate object from: ${session.created}`); + } + + case SearchSessionStatus.EXPIRED: + try { + const toolTipContent = i18n.translate( + 'xpack.data.mgmt.searchSessions.status.message.expiredOn', + { + defaultMessage: 'Expired on {expireDate}', + values: { expireDate }, + } + ); + + return { + icon: , + label: {getStatusText(session.status)}, + toolTipContent, + }; + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + throw new Error(`Could not instantiate an expiration Date object from: ${session.expires}`); + } + + case SearchSessionStatus.CANCELLED: + return { + icon: , + label: {getStatusText(session.status)}, + toolTipContent: i18n.translate('xpack.data.mgmt.searchSessions.status.message.cancelled', { + defaultMessage: 'Cancelled by user', + }), + }; + + case SearchSessionStatus.ERROR: + return { + textColor: 'danger', + icon: , + label: {getStatusText(session.status)}, + toolTipContent: i18n.translate('xpack.data.mgmt.searchSessions.status.message.error', { + defaultMessage: 'Error: {error}', + values: { error: (session as any).error || 'unknown' }, + }), + }; + + case SearchSessionStatus.COMPLETE: + try { + const toolTipContent = i18n.translate('xpack.data.mgmt.searchSessions.status.expiresOn', { + defaultMessage: 'Expires on {expireDate}', + values: { expireDate }, + }); + + return { + textColor: 'secondary', + icon: , + label: {getStatusText(session.status)}, + toolTipContent, + }; + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + throw new Error( + `Could not instantiate an expiration Date object for completed session from: ${session.expires}` + ); + } + + // Error was thrown + return null; + + default: + throw new Error(`Unknown status: ${session.status}`); + } +}; + +export const StatusIndicator = (props: StatusIndicatorProps) => { + try { + const statusDef = getStatusAttributes(props); + const { session } = props; + + if (statusDef) { + const { toolTipContent } = statusDef; + let icon: ReactElement | undefined = statusDef.icon; + let label: ReactElement = statusDef.label; + + if (icon && toolTipContent) { + icon = {icon}; + } + if (toolTipContent) { + label = ( + + + {statusDef.label} + + + ); + } + + return ( + + {icon} + + + {label} + + + + ); + } + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + } + + // Exception has been caught + return {props.session.status}; +}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/app_filter.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/app_filter.tsx new file mode 100644 index 000000000000..236fc492031c --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/app_filter.tsx @@ -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 { FieldValueOptionType, SearchFilterConfig } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { capitalize } from 'lodash'; +import { UISession } from '../../types'; + +export const getAppFilter: (tableData: UISession[]) => SearchFilterConfig = (tableData) => ({ + type: 'field_value_selection', + name: i18n.translate('xpack.data.mgmt.searchSessions.search.filterApp', { + defaultMessage: 'App', + }), + field: 'appId', + multiSelect: 'or', + options: tableData.reduce((options: FieldValueOptionType[], { appId }) => { + const existingOption = options.find((o) => o.value === appId); + if (!existingOption) { + return [...options, { value: appId, view: capitalize(appId) }]; + } + + return options; + }, []), +}); diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/index.js b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/index.ts similarity index 82% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/index.js rename to x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/index.ts index 760a206dfa6b..83ca1c223dfc 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/index.js +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { FieldsStats } from './fields_stats'; +export { SearchSessionsMgmtTable } from './table'; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/status_filter.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/status_filter.tsx new file mode 100644 index 000000000000..04421ad66e58 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/status_filter.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FieldValueOptionType, SearchFilterConfig } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { TableText } from '../'; +import { UISession } from '../../types'; +import { getStatusText } from '../status'; + +export const getStatusFilter: (tableData: UISession[]) => SearchFilterConfig = (tableData) => ({ + type: 'field_value_selection', + name: i18n.translate('xpack.data.mgmt.searchSessions.search.filterStatus', { + defaultMessage: 'Status', + }), + field: 'status', + multiSelect: 'or', + options: tableData.reduce((options: FieldValueOptionType[], session) => { + const { status: statusType } = session; + const existingOption = options.find((o) => o.value === statusType); + if (!existingOption) { + const view = {getStatusText(session.status)}; + return [...options, { value: statusType, view }]; + } + + return options; + }, []), +}); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx new file mode 100644 index 000000000000..51cec8f2afef --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx @@ -0,0 +1,193 @@ +/* + * 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 { MockedKeys } from '@kbn/utility-types/jest'; +import { act, waitFor } from '@testing-library/react'; +import { mount, ReactWrapper } from 'enzyme'; +import { CoreSetup, CoreStart } from 'kibana/public'; +import moment from 'moment'; +import React from 'react'; +import { coreMock } from 'src/core/public/mocks'; +import { SessionsClient } from 'src/plugins/data/public/search'; +import { SearchSessionStatus } from '../../../../../common/search'; +import { SessionsMgmtConfigSchema } from '../../'; +import { SearchSessionsMgmtAPI } from '../../lib/api'; +import { LocaleWrapper, mockUrls } from '../../__mocks__'; +import { SearchSessionsMgmtTable } from './table'; + +let mockCoreSetup: MockedKeys; +let mockCoreStart: CoreStart; +let mockConfig: SessionsMgmtConfigSchema; +let sessionsClient: SessionsClient; +let api: SearchSessionsMgmtAPI; + +describe('Background Search Session Management Table', () => { + beforeEach(async () => { + mockCoreSetup = coreMock.createSetup(); + mockCoreStart = coreMock.createStart(); + mockConfig = { + expiresSoonWarning: moment.duration(1, 'days'), + maxSessions: 2000, + refreshInterval: moment.duration(1, 'seconds'), + refreshTimeout: moment.duration(10, 'minutes'), + }; + + sessionsClient = new SessionsClient({ http: mockCoreSetup.http }); + api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { + urls: mockUrls, + notifications: mockCoreStart.notifications, + application: mockCoreStart.application, + }); + }); + + describe('renders', () => { + let table: ReactWrapper; + + const getInitialResponse = () => { + return { + saved_objects: [ + { + id: 'wtywp9u2802hahgp-flps', + attributes: { + name: 'very background search', + id: 'wtywp9u2802hahgp-flps', + url: '/app/great-app-url/#48', + appId: 'canvas', + status: SearchSessionStatus.IN_PROGRESS, + created: '2020-12-02T00:19:32Z', + expires: '2020-12-07T00:19:32Z', + }, + }, + ], + }; + }; + + test('table header cells', async () => { + sessionsClient.find = jest.fn().mockImplementation(async () => { + return getInitialResponse(); + }); + + await act(async () => { + table = mount( + + + + ); + }); + + expect(table.find('thead th').map((node) => node.text())).toMatchInlineSnapshot(` + Array [ + "AppClick to sort in ascending order", + "NameClick to sort in ascending order", + "StatusClick to sort in ascending order", + "CreatedClick to unsort", + "ExpirationClick to sort in ascending order", + ] + `); + }); + + test('table body cells', async () => { + sessionsClient.find = jest.fn().mockImplementation(async () => { + return getInitialResponse(); + }); + + await act(async () => { + table = mount( + + + + ); + }); + table.update(); + + expect(table.find('tbody td').map((node) => node.text())).toMatchInlineSnapshot(` + Array [ + "App", + "Namevery background search", + "StatusIn progress", + "Created2 Dec, 2020, 00:19:32", + "Expiration7 Dec, 2020, 00:19:32", + "", + "", + ] + `); + }); + }); + + // FLAKY: https://github.com/elastic/kibana/issues/88928 + describe.skip('fetching sessions data', () => { + test('re-fetches data', async () => { + jest.useFakeTimers(); + sessionsClient.find = jest.fn(); + mockConfig = { + ...mockConfig, + refreshInterval: moment.duration(10, 'seconds'), + }; + + await act(async () => { + mount( + + + + ); + jest.advanceTimersByTime(20000); + }); + + // 1 for initial load + 2 refresh calls + expect(sessionsClient.find).toBeCalledTimes(3); + + jest.useRealTimers(); + }); + + test('refresh button uses the session client', async () => { + sessionsClient.find = jest.fn(); + + mockConfig = { + ...mockConfig, + refreshInterval: moment.duration(1, 'day'), + refreshTimeout: moment.duration(2, 'days'), + }; + + await act(async () => { + const table = mount( + + + + ); + + const buttonSelector = `[data-test-subj="sessionManagementRefreshBtn"] button`; + + await waitFor(() => { + table.find(buttonSelector).first().simulate('click'); + table.update(); + }); + }); + + // initial call + click + expect(sessionsClient.find).toBeCalledTimes(2); + }); + }); +}); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.tsx new file mode 100644 index 000000000000..f7aecdbd58a2 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.tsx @@ -0,0 +1,122 @@ +/* + * 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 { EuiButton, EuiInMemoryTable, EuiSearchBarProps } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { CoreStart } from 'kibana/public'; +import moment from 'moment'; +import React, { useCallback, useMemo, useRef, useEffect, useState } from 'react'; +import useDebounce from 'react-use/lib/useDebounce'; +import useInterval from 'react-use/lib/useInterval'; +import { TableText } from '../'; +import { SessionsMgmtConfigSchema } from '../..'; +import { SearchSessionsMgmtAPI } from '../../lib/api'; +import { getColumns } from '../../lib/get_columns'; +import { UISession } from '../../types'; +import { OnActionComplete } from '../actions'; +import { getAppFilter } from './app_filter'; +import { getStatusFilter } from './status_filter'; + +const TABLE_ID = 'searchSessionsMgmtTable'; + +interface Props { + core: CoreStart; + api: SearchSessionsMgmtAPI; + timezone: string; + config: SessionsMgmtConfigSchema; +} + +export function SearchSessionsMgmtTable({ core, api, timezone, config, ...props }: Props) { + const [tableData, setTableData] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [debouncedIsLoading, setDebouncedIsLoading] = useState(false); + const [pagination, setPagination] = useState({ pageIndex: 0 }); + const showLatestResultsHandler = useRef(); + const refreshInterval = useMemo(() => moment.duration(config.refreshInterval).asMilliseconds(), [ + config.refreshInterval, + ]); + + // Debounce rendering the state of the Refresh button + useDebounce( + () => { + setDebouncedIsLoading(isLoading); + }, + 250, + [isLoading] + ); + + // refresh behavior + const doRefresh = useCallback(async () => { + setIsLoading(true); + const renderResults = (results: UISession[]) => { + setTableData(results); + }; + showLatestResultsHandler.current = renderResults; + let results: UISession[] = []; + try { + results = await api.fetchTableData(); + } catch (e) {} // eslint-disable-line no-empty + + if (showLatestResultsHandler.current === renderResults) { + renderResults(results); + setIsLoading(false); + } + }, [api]); + + // initial data load + useEffect(() => { + doRefresh(); + }, [doRefresh]); + + useInterval(doRefresh, refreshInterval); + + const onActionComplete: OnActionComplete = () => { + doRefresh(); + }; + + // table config: search / filters + const search: EuiSearchBarProps = { + box: { incremental: true }, + filters: [getStatusFilter(tableData), getAppFilter(tableData)], + toolsRight: ( + + + + + + ), + }; + + return ( + + {...props} + id={TABLE_ID} + data-test-subj={TABLE_ID} + rowProps={() => ({ + 'data-test-subj': 'searchSessionsRow', + })} + columns={getColumns(core, api, config, timezone, onActionComplete)} + items={tableData} + pagination={pagination} + search={search} + sorting={{ sort: { field: 'created', direction: 'desc' } }} + onTableChange={({ page: { index } }) => { + setPagination({ pageIndex: index }); + }} + tableLayout="auto" + /> + ); +} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/icons/extend_session.svg b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/icons/extend_session.svg new file mode 100644 index 000000000000..7cb9f7e6a24c --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/icons/extend_session.svg @@ -0,0 +1,3 @@ + + + diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts new file mode 100644 index 000000000000..76a5d440cd89 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts @@ -0,0 +1,64 @@ +/* + * 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 type { CoreStart, HttpStart, I18nStart, IUiSettingsClient } from 'kibana/public'; +import { CoreSetup } from 'kibana/public'; +import type { DataPublicPluginStart } from 'src/plugins/data/public'; +import type { ManagementSetup } from 'src/plugins/management/public'; +import type { SharePluginStart } from 'src/plugins/share/public'; +import type { ConfigSchema } from '../../../config'; +import type { DataEnhancedStartDependencies } from '../../plugin'; +import type { SearchSessionsMgmtAPI } from './lib/api'; +import type { AsyncSearchIntroDocumentation } from './lib/documentation'; + +export interface IManagementSectionsPluginsSetup { + management: ManagementSetup; +} + +export interface IManagementSectionsPluginsStart { + data: DataPublicPluginStart; + share: SharePluginStart; +} + +export interface AppDependencies { + plugins: IManagementSectionsPluginsSetup; + share: SharePluginStart; + uiSettings: IUiSettingsClient; + documentation: AsyncSearchIntroDocumentation; + core: CoreStart; // for RedirectAppLinks + api: SearchSessionsMgmtAPI; + http: HttpStart; + i18n: I18nStart; + config: SessionsMgmtConfigSchema; +} + +export const APP = { + id: 'search_sessions', + getI18nName: (): string => + i18n.translate('xpack.data.mgmt.searchSessions.appTitle', { + defaultMessage: 'Search Sessions', + }), +}; + +export type SessionsMgmtConfigSchema = ConfigSchema['search']['sessions']['management']; + +export function registerSearchSessionsMgmt( + coreSetup: CoreSetup, + config: SessionsMgmtConfigSchema, + services: IManagementSectionsPluginsSetup +) { + services.management.sections.section.kibana.registerApp({ + id: APP.id, + title: APP.getI18nName(), + order: 2, + mount: async (params) => { + const { SearchSessionsMgmtApp: MgmtApp } = await import('./application'); + const mgmtApp = new MgmtApp(coreSetup, config, params, services); + return mgmtApp.mountManagementSection(); + }, + }); +} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts new file mode 100644 index 000000000000..5b337dfd03eb --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts @@ -0,0 +1,214 @@ +/* + * 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 type { MockedKeys } from '@kbn/utility-types/jest'; +import { CoreSetup, CoreStart } from 'kibana/public'; +import moment from 'moment'; +import { coreMock } from 'src/core/public/mocks'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import type { SavedObjectsFindResponse } from 'src/core/server'; +import { SessionsClient } from 'src/plugins/data/public/search'; +import type { SessionsMgmtConfigSchema } from '../'; +import { SearchSessionStatus } from '../../../../common/search'; +import { mockUrls } from '../__mocks__'; +import { SearchSessionsMgmtAPI } from './api'; + +let mockCoreSetup: MockedKeys; +let mockCoreStart: MockedKeys; +let mockConfig: SessionsMgmtConfigSchema; +let sessionsClient: SessionsClient; + +describe('Search Sessions Management API', () => { + beforeEach(() => { + mockCoreSetup = coreMock.createSetup(); + mockCoreStart = coreMock.createStart(); + mockConfig = { + expiresSoonWarning: moment.duration('1d'), + maxSessions: 2000, + refreshInterval: moment.duration('1s'), + refreshTimeout: moment.duration('10m'), + }; + + sessionsClient = new SessionsClient({ http: mockCoreSetup.http }); + }); + + describe('listing', () => { + test('fetchDataTable calls the listing endpoint', async () => { + sessionsClient.find = jest.fn().mockImplementation(async () => { + return { + saved_objects: [ + { + id: 'hello-pizza-123', + attributes: { name: 'Veggie', appId: 'pizza', status: 'complete' }, + }, + ], + } as SavedObjectsFindResponse; + }); + + const api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { + urls: mockUrls, + notifications: mockCoreStart.notifications, + application: mockCoreStart.application, + }); + expect(await api.fetchTableData()).toMatchInlineSnapshot(` + Array [ + Object { + "actions": Array [ + "reload", + "extend", + "cancel", + ], + "appId": "pizza", + "created": undefined, + "expires": undefined, + "id": "hello-pizza-123", + "name": "Veggie", + "reloadUrl": "hello-cool-undefined-url", + "restoreUrl": "hello-cool-undefined-url", + "status": "complete", + }, + ] + `); + }); + + test('handle error from sessionsClient response', async () => { + sessionsClient.find = jest.fn().mockRejectedValue(new Error('implementation is so bad')); + + const api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { + urls: mockUrls, + notifications: mockCoreStart.notifications, + application: mockCoreStart.application, + }); + await api.fetchTableData(); + + expect(mockCoreStart.notifications.toasts.addError).toHaveBeenCalledWith( + new Error('implementation is so bad'), + { title: 'Failed to refresh the page!' } + ); + }); + + test('handle timeout error', async () => { + mockConfig = { + ...mockConfig, + refreshInterval: moment.duration(1, 'hours'), + refreshTimeout: moment.duration(1, 'seconds'), + }; + + sessionsClient.find = jest.fn().mockImplementation(async () => { + return new Promise((resolve) => { + setTimeout(resolve, 2000); + }); + }); + + const api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { + urls: mockUrls, + notifications: mockCoreStart.notifications, + application: mockCoreStart.application, + }); + await api.fetchTableData(); + + expect(mockCoreStart.notifications.toasts.addDanger).toHaveBeenCalledWith( + 'Fetching the Search Session info timed out after 1 seconds' + ); + }); + }); + + describe('cancel', () => { + beforeEach(() => { + sessionsClient.find = jest.fn().mockImplementation(async () => { + return { + saved_objects: [ + { + id: 'hello-pizza-123', + attributes: { name: 'Veggie', appId: 'pizza', status: 'baked' }, + }, + ], + } as SavedObjectsFindResponse; + }); + }); + + test('send cancel calls the cancel endpoint with a session ID', async () => { + const api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { + urls: mockUrls, + notifications: mockCoreStart.notifications, + application: mockCoreStart.application, + }); + await api.sendCancel('abc-123-cool-session-ID'); + + expect(mockCoreStart.notifications.toasts.addSuccess).toHaveBeenCalledWith({ + title: 'The search session was canceled and expired.', + }); + }); + + test('error if deleting shows a toast message', async () => { + sessionsClient.delete = jest.fn().mockRejectedValue(new Error('implementation is so bad')); + + const api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { + urls: mockUrls, + notifications: mockCoreStart.notifications, + application: mockCoreStart.application, + }); + await api.sendCancel('abc-123-cool-session-ID'); + + expect(mockCoreStart.notifications.toasts.addError).toHaveBeenCalledWith( + new Error('implementation is so bad'), + { title: 'Failed to cancel the search session!' } + ); + }); + }); + + describe('reload', () => { + beforeEach(() => { + sessionsClient.find = jest.fn().mockImplementation(async () => { + return { + saved_objects: [ + { + id: 'hello-pizza-123', + attributes: { name: 'Veggie', appId: 'pizza', status: SearchSessionStatus.COMPLETE }, + }, + ], + } as SavedObjectsFindResponse; + }); + }); + + test('send cancel calls the cancel endpoint with a session ID', async () => { + const api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { + urls: mockUrls, + notifications: mockCoreStart.notifications, + application: mockCoreStart.application, + }); + await api.reloadSearchSession('www.myurl.com'); + + expect(mockCoreStart.application.navigateToUrl).toHaveBeenCalledWith('www.myurl.com'); + }); + }); + + describe('extend', () => { + beforeEach(() => { + sessionsClient.find = jest.fn().mockImplementation(async () => { + return { + saved_objects: [ + { + id: 'hello-pizza-123', + attributes: { name: 'Veggie', appId: 'pizza', status: SearchSessionStatus.COMPLETE }, + }, + ], + } as SavedObjectsFindResponse; + }); + }); + + test('send extend throws an error for now', async () => { + const api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { + urls: mockUrls, + notifications: mockCoreStart.notifications, + application: mockCoreStart.application, + }); + await api.sendExtend('my-id', '5d'); + + expect(mockCoreStart.notifications.toasts.addError).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts new file mode 100644 index 000000000000..a2bd6b1a549b --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts @@ -0,0 +1,182 @@ +/* + * 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 type { ApplicationStart, NotificationsStart, SavedObject } from 'kibana/public'; +import moment from 'moment'; +import { from, race, timer } from 'rxjs'; +import { mapTo, tap } from 'rxjs/operators'; +import type { SharePluginStart } from 'src/plugins/share/public'; +import { SessionsMgmtConfigSchema } from '../'; +import type { ISessionsClient } from '../../../../../../../src/plugins/data/public'; +import type { SearchSessionSavedObjectAttributes } from '../../../../common'; +import { SearchSessionStatus } from '../../../../common/search'; +import { ACTION } from '../components/actions'; +import { UISession } from '../types'; + +type UrlGeneratorsStart = SharePluginStart['urlGenerators']; + +function getActions(status: SearchSessionStatus) { + const actions: ACTION[] = []; + actions.push(ACTION.RELOAD); + if (status === SearchSessionStatus.IN_PROGRESS || status === SearchSessionStatus.COMPLETE) { + actions.push(ACTION.EXTEND); + actions.push(ACTION.CANCEL); + } + return actions; +} + +async function getUrlFromState( + urls: UrlGeneratorsStart, + urlGeneratorId: string, + state: Record +) { + let url = '/'; + try { + url = await urls.getUrlGenerator(urlGeneratorId).createUrl(state); + } catch (err) { + // eslint-disable-next-line no-console + console.error('Could not create URL from restoreState'); + // eslint-disable-next-line no-console + console.error(err); + } + return url; +} + +// Helper: factory for a function to map server objects to UI objects +const mapToUISession = ( + urls: UrlGeneratorsStart, + { expiresSoonWarning }: SessionsMgmtConfigSchema +) => async (savedObject: SavedObject): Promise => { + const { + name, + appId, + created, + expires, + status, + urlGeneratorId, + initialState, + restoreState, + } = savedObject.attributes; + + const actions = getActions(status); + + // TODO: initialState should be saved without the searchSessionID + if (initialState) delete initialState.searchSessionId; + // derive the URL and add it in + const reloadUrl = await getUrlFromState(urls, urlGeneratorId, initialState); + const restoreUrl = await getUrlFromState(urls, urlGeneratorId, restoreState); + + return { + id: savedObject.id, + name, + appId, + created, + expires, + status, + actions, + restoreUrl, + reloadUrl, + }; +}; + +interface SearcgSessuibManagementDeps { + urls: UrlGeneratorsStart; + notifications: NotificationsStart; + application: ApplicationStart; +} + +export class SearchSessionsMgmtAPI { + constructor( + private sessionsClient: ISessionsClient, + private config: SessionsMgmtConfigSchema, + private deps: SearcgSessuibManagementDeps + ) {} + + public async fetchTableData(): Promise { + interface FetchResult { + saved_objects: object[]; + } + + const refreshTimeout = moment.duration(this.config.refreshTimeout); + + const fetch$ = from( + this.sessionsClient.find({ + page: 1, + perPage: this.config.maxSessions, + sortField: 'created', + sortOrder: 'asc', + }) + ); + const timeout$ = timer(refreshTimeout.asMilliseconds()).pipe( + tap(() => { + this.deps.notifications.toasts.addDanger( + i18n.translate('xpack.data.mgmt.searchSessions.api.fetchTimeout', { + defaultMessage: 'Fetching the Search Session info timed out after {timeout} seconds', + values: { timeout: refreshTimeout.asSeconds() }, + }) + ); + }), + mapTo(null) + ); + + // fetch the search sessions before timeout triggers + try { + const result = await race(fetch$, timeout$).toPromise(); + if (result && result.saved_objects) { + const savedObjects = result.saved_objects as Array< + SavedObject + >; + return await Promise.all(savedObjects.map(mapToUISession(this.deps.urls, this.config))); + } + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + this.deps.notifications.toasts.addError(err, { + title: i18n.translate('xpack.data.mgmt.searchSessions.api.fetchError', { + defaultMessage: 'Failed to refresh the page!', + }), + }); + } + + return []; + } + + public reloadSearchSession(reloadUrl: string) { + this.deps.application.navigateToUrl(reloadUrl); + } + + // Cancel and expire + public async sendCancel(id: string): Promise { + try { + await this.sessionsClient.delete(id); + + this.deps.notifications.toasts.addSuccess({ + title: i18n.translate('xpack.data.mgmt.searchSessions.api.canceled', { + defaultMessage: 'The search session was canceled and expired.', + }), + }); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + + this.deps.notifications.toasts.addError(err, { + title: i18n.translate('xpack.data.mgmt.searchSessions.api.cancelError', { + defaultMessage: 'Failed to cancel the search session!', + }), + }); + } + } + + // Extend + public async sendExtend(id: string, ttl: string): Promise { + this.deps.notifications.toasts.addError(new Error('Not implemented'), { + title: i18n.translate('xpack.data.mgmt.searchSessions.api.extendError', { + defaultMessage: 'Failed to extend the session expiration!', + }), + }); + } +} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/date_string.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/date_string.ts new file mode 100644 index 000000000000..7640d8b80766 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/date_string.ts @@ -0,0 +1,22 @@ +/* + * 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'; +import { DATE_STRING_FORMAT } from '../types'; + +export const dateString = (inputString: string, tz: string): string => { + if (inputString == null) { + throw new Error('Invalid date string!'); + } + let returnString: string; + if (tz === 'Browser') { + returnString = moment.utc(inputString).tz(moment.tz.guess()).format(DATE_STRING_FORMAT); + } else { + returnString = moment(inputString).tz(tz).format(DATE_STRING_FORMAT); + } + + return returnString; +}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/documentation.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/documentation.ts new file mode 100644 index 000000000000..eac3245dfe2b --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/documentation.ts @@ -0,0 +1,22 @@ +/* + * 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 { DocLinksStart } from 'kibana/public'; + +export class AsyncSearchIntroDocumentation { + private docsBasePath: string = ''; + + constructor(docs: DocLinksStart) { + const { DOC_LINK_VERSION, ELASTIC_WEBSITE_URL } = docs; + const docsBase = `${ELASTIC_WEBSITE_URL}guide/en`; + // TODO: There should be Kibana documentation link about Search Sessions in Kibana + this.docsBasePath = `${docsBase}/elasticsearch/reference/${DOC_LINK_VERSION}`; + } + + public getElasticsearchDocLink() { + return `${this.docsBasePath}/async-search-intro.html`; + } +} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.test.tsx new file mode 100644 index 000000000000..ce441efea738 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.test.tsx @@ -0,0 +1,208 @@ +/* + * 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 { EuiTableFieldDataColumnType } from '@elastic/eui'; +import { MockedKeys } from '@kbn/utility-types/jest'; +import { mount } from 'enzyme'; +import { CoreSetup, CoreStart } from 'kibana/public'; +import moment from 'moment'; +import { ReactElement } from 'react'; +import { coreMock } from 'src/core/public/mocks'; +import { SessionsClient } from 'src/plugins/data/public/search'; +import { SessionsMgmtConfigSchema } from '../'; +import { SearchSessionStatus } from '../../../../common/search'; +import { OnActionComplete } from '../components'; +import { UISession } from '../types'; +import { mockUrls } from '../__mocks__'; +import { SearchSessionsMgmtAPI } from './api'; +import { getColumns } from './get_columns'; + +let mockCoreSetup: MockedKeys; +let mockCoreStart: CoreStart; +let mockConfig: SessionsMgmtConfigSchema; +let api: SearchSessionsMgmtAPI; +let sessionsClient: SessionsClient; +let handleAction: OnActionComplete; +let mockSession: UISession; + +let tz = 'UTC'; + +describe('Search Sessions Management table column factory', () => { + beforeEach(async () => { + mockCoreSetup = coreMock.createSetup(); + mockCoreStart = coreMock.createStart(); + mockConfig = { + expiresSoonWarning: moment.duration(1, 'days'), + maxSessions: 2000, + refreshInterval: moment.duration(1, 'seconds'), + refreshTimeout: moment.duration(10, 'minutes'), + }; + sessionsClient = new SessionsClient({ http: mockCoreSetup.http }); + + api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { + urls: mockUrls, + notifications: mockCoreStart.notifications, + application: mockCoreStart.application, + }); + tz = 'UTC'; + + handleAction = () => { + throw new Error('not testing handle action'); + }; + + mockSession = { + name: 'Cool mock session', + id: 'wtywp9u2802hahgp-thao', + reloadUrl: '/app/great-app-url', + restoreUrl: '/app/great-app-url/#42', + appId: 'discovery', + status: SearchSessionStatus.IN_PROGRESS, + created: '2020-12-02T00:19:32Z', + expires: '2020-12-07T00:19:32Z', + }; + }); + + test('returns columns', () => { + const columns = getColumns(mockCoreStart, api, mockConfig, tz, handleAction); + expect(columns).toMatchInlineSnapshot(` + Array [ + Object { + "field": "appId", + "name": "App", + "render": [Function], + "sortable": true, + }, + Object { + "field": "name", + "name": "Name", + "render": [Function], + "sortable": true, + "width": "20%", + }, + Object { + "field": "status", + "name": "Status", + "render": [Function], + "sortable": true, + }, + Object { + "field": "created", + "name": "Created", + "render": [Function], + "sortable": true, + }, + Object { + "field": "expires", + "name": "Expiration", + "render": [Function], + "sortable": true, + }, + Object { + "field": "status", + "name": "", + "render": [Function], + "sortable": false, + }, + Object { + "field": "actions", + "name": "", + "render": [Function], + "sortable": false, + }, + ] + `); + }); + + describe('name', () => { + test('rendering', () => { + const [, nameColumn] = getColumns(mockCoreStart, api, mockConfig, tz, handleAction) as Array< + EuiTableFieldDataColumnType + >; + + const name = mount(nameColumn.render!(mockSession.name, mockSession) as ReactElement); + + expect(name.text()).toBe('Cool mock session'); + }); + }); + + // Status column + describe('status', () => { + test('render in_progress', () => { + const [, , status] = getColumns(mockCoreStart, api, mockConfig, tz, handleAction) as Array< + EuiTableFieldDataColumnType + >; + + const statusLine = mount(status.render!(mockSession.status, mockSession) as ReactElement); + expect( + statusLine.find('.euiText[data-test-subj="sessionManagementStatusTooltip"]').text() + ).toMatchInlineSnapshot(`"In progress"`); + }); + + test('error handling', () => { + const [, , status] = getColumns(mockCoreStart, api, mockConfig, tz, handleAction) as Array< + EuiTableFieldDataColumnType + >; + + mockSession.status = 'INVALID' as SearchSessionStatus; + const statusLine = mount(status.render!(mockSession.status, mockSession) as ReactElement); + + // no unhandled error + + expect(statusLine.text()).toMatchInlineSnapshot(`"INVALID"`); + }); + }); + + // Start Date column + describe('startedDate', () => { + test('render using Browser timezone', () => { + tz = 'Browser'; + + const [, , , createdDateCol] = getColumns( + mockCoreStart, + api, + mockConfig, + tz, + handleAction + ) as Array>; + + const date = mount(createdDateCol.render!(mockSession.created, mockSession) as ReactElement); + + expect(date.text()).toBe('1 Dec, 2020, 19:19:32'); + }); + + test('render using AK timezone', () => { + tz = 'US/Alaska'; + + const [, , , createdDateCol] = getColumns( + mockCoreStart, + api, + mockConfig, + tz, + handleAction + ) as Array>; + + const date = mount(createdDateCol.render!(mockSession.created, mockSession) as ReactElement); + + expect(date.text()).toBe('1 Dec, 2020, 15:19:32'); + }); + + test('error handling', () => { + const [, , , createdDateCol] = getColumns( + mockCoreStart, + api, + mockConfig, + tz, + handleAction + ) as Array>; + + mockSession.created = 'INVALID'; + const date = mount(createdDateCol.render!(mockSession.created, mockSession) as ReactElement); + + // no unhandled error + expect(date.text()).toBe('Invalid date'); + }); + }); +}); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.tsx new file mode 100644 index 000000000000..090336c37a98 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.tsx @@ -0,0 +1,233 @@ +/* + * 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 { + EuiBadge, + EuiBasicTableColumn, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiIconTip, + EuiLink, + EuiToolTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { CoreStart } from 'kibana/public'; +import { capitalize } from 'lodash'; +import React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { RedirectAppLinks } from '../../../../../../../src/plugins/kibana_react/public'; +import { SessionsMgmtConfigSchema } from '../'; +import { SearchSessionStatus } from '../../../../common/search'; +import { TableText } from '../components'; +import { OnActionComplete, PopoverActionsMenu } from '../components'; +import { StatusIndicator } from '../components/status'; +import { dateString } from '../lib/date_string'; +import { SearchSessionsMgmtAPI } from './api'; +import { getExpirationStatus } from './get_expiration_status'; +import { UISession } from '../types'; + +// Helper function: translate an app string to EuiIcon-friendly string +const appToIcon = (app: string) => { + if (app === 'dashboards') { + return 'dashboard'; + } + return app; +}; + +function isSessionRestorable(status: SearchSessionStatus) { + return status === SearchSessionStatus.IN_PROGRESS || status === SearchSessionStatus.COMPLETE; +} + +export const getColumns = ( + core: CoreStart, + api: SearchSessionsMgmtAPI, + config: SessionsMgmtConfigSchema, + timezone: string, + onActionComplete: OnActionComplete +): Array> => { + // Use a literal array of table column definitions to detail a UISession object + return [ + // App + { + field: 'appId', + name: i18n.translate('xpack.data.mgmt.searchSessions.table.headerType', { + defaultMessage: 'App', + }), + sortable: true, + render: (appId: UISession['appId'], { id }) => { + const app = `${appToIcon(appId)}`; + return ( + + + + ); + }, + }, + + // Name, links to app and displays the search session data + { + field: 'name', + name: i18n.translate('xpack.data.mgmt.searchSessions.table.headerName', { + defaultMessage: 'Name', + }), + sortable: true, + width: '20%', + render: (name: UISession['name'], { restoreUrl, reloadUrl, status }) => { + const isRestorable = isSessionRestorable(status); + const notRestorableWarning = isRestorable ? null : ( + <> + {' '} + + } + /> + + ); + return ( + + + + {name} + {notRestorableWarning} + + + + ); + }, + }, + + // Session status + { + field: 'status', + name: i18n.translate('xpack.data.mgmt.searchSessions.table.headerStatus', { + defaultMessage: 'Status', + }), + sortable: true, + render: (statusType: UISession['status'], session) => ( + + ), + }, + + // Started date + { + field: 'created', + name: i18n.translate('xpack.data.mgmt.searchSessions.table.headerStarted', { + defaultMessage: 'Created', + }), + sortable: true, + render: (created: UISession['created'], { id }) => { + try { + const startedOn = dateString(created, timezone); + return ( + + {startedOn} + + ); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + return {created}; + } + }, + }, + + // Expiration date + { + field: 'expires', + name: i18n.translate('xpack.data.mgmt.searchSessions.table.headerExpiration', { + defaultMessage: 'Expiration', + }), + sortable: true, + render: (expires: UISession['expires'], { id, status }) => { + if ( + expires && + status !== SearchSessionStatus.EXPIRED && + status !== SearchSessionStatus.CANCELLED && + status !== SearchSessionStatus.ERROR + ) { + try { + const expiresOn = dateString(expires, timezone); + + // return + return ( + + {expiresOn} + + ); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err); + return {expires}; + } + } + return ( + + -- + + ); + }, + }, + + // Highlight Badge, if completed session expires soon + { + field: 'status', + name: '', + sortable: false, + render: (status, { expires }) => { + const expirationStatus = getExpirationStatus(config, expires); + if (expirationStatus) { + const { toolTipContent, statusContent } = expirationStatus; + + return ( + + + {statusContent} + + + ); + } + + return ; + }, + }, + + // Action(s) in-line in the row, additional action(s) in the popover, no column header + { + field: 'actions', + name: '', + sortable: false, + render: (actions: UISession['actions'], session) => { + if (actions && actions.length) { + return ( + + + + + + ); + } + }, + }, + ]; +}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_expiration_status.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_expiration_status.ts new file mode 100644 index 000000000000..3c167d6dbe41 --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_expiration_status.ts @@ -0,0 +1,47 @@ +/* + * 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 moment from 'moment'; +import { SessionsMgmtConfigSchema } from '../'; + +export const getExpirationStatus = (config: SessionsMgmtConfigSchema, expires: string | null) => { + const tNow = moment.utc().valueOf(); + const tFuture = moment.utc(expires).valueOf(); + + // NOTE this could end up negative. If server time is off from the browser's clock + // and the session was early expired when the browser refreshed the listing + const durationToExpire = moment.duration(tFuture - tNow); + const expiresInDays = Math.floor(durationToExpire.asDays()); + const sufficientDays = Math.ceil(moment.duration(config.expiresSoonWarning).asDays()); + + let toolTipContent = i18n.translate('xpack.data.mgmt.searchSessions.status.expiresSoonInDays', { + defaultMessage: 'Expires in {numDays} days', + values: { numDays: expiresInDays }, + }); + let statusContent = i18n.translate( + 'xpack.data.mgmt.searchSessions.status.expiresSoonInDaysTooltip', + { defaultMessage: '{numDays} days', values: { numDays: expiresInDays } } + ); + + if (expiresInDays === 0) { + // switch to show expires in hours + const expiresInHours = Math.floor(durationToExpire.asHours()); + + toolTipContent = i18n.translate('xpack.data.mgmt.searchSessions.status.expiresSoonInHours', { + defaultMessage: 'This session expires in {numHours} hours', + values: { numHours: expiresInHours }, + }); + statusContent = i18n.translate( + 'xpack.data.mgmt.searchSessions.status.expiresSoonInHoursTooltip', + { defaultMessage: '{numHours} hours', values: { numHours: expiresInHours } } + ); + } + + if (durationToExpire.valueOf() > 0 && expiresInDays <= sufficientDays) { + return { toolTipContent, statusContent }; + } +}; diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts new file mode 100644 index 000000000000..78b91f7ca8ac --- /dev/null +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/types.ts @@ -0,0 +1,22 @@ +/* + * 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 { SearchSessionStatus } from '../../../common'; +import { ACTION } from './components/actions'; + +export const DATE_STRING_FORMAT = 'D MMM, YYYY, HH:mm:ss'; + +export interface UISession { + id: string; + name: string; + appId: string; + created: string; + expires: string | null; + status: SearchSessionStatus; + actions?: ACTION[]; + reloadUrl: string; + restoreUrl: string; +} diff --git a/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/search_session_indicator.tsx b/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/search_session_indicator.tsx index ed022e18c34d..361688581b4f 100644 --- a/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/search_session_indicator.tsx +++ b/x-pack/plugins/data_enhanced/public/search/ui/search_session_indicator/search_session_indicator.tsx @@ -66,7 +66,7 @@ const ContinueInBackgroundButton = ({ ); const ViewAllSearchSessionsButton = ({ - viewSearchSessionsLink = 'management', + viewSearchSessionsLink = 'management/kibana/search_sessions', buttonProps = {}, }: ActionButtonProps) => ( (); + registerSessionRoutes(router, this.logger); this.sessionService.setup(core, { taskManager: deps.taskManager, diff --git a/x-pack/plugins/data_enhanced/server/routes/mocks.ts b/x-pack/plugins/data_enhanced/server/routes/mocks.ts index 3e7b89ed2cca..4bad563bf393 100644 --- a/x-pack/plugins/data_enhanced/server/routes/mocks.ts +++ b/x-pack/plugins/data_enhanced/server/routes/mocks.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'kibana/server'; +import type { DataRequestHandlerContext } from '../../../../../src/plugins/data/server'; import { coreMock } from '../../../../../src/core/server/mocks'; export function createSearchRequestHandlerContext() { @@ -22,5 +22,5 @@ export function createSearchRequestHandlerContext() { update: jest.fn(), }, }, - } as unknown) as jest.Mocked; + } as unknown) as jest.Mocked; } diff --git a/x-pack/plugins/data_enhanced/server/routes/session.test.ts b/x-pack/plugins/data_enhanced/server/routes/session.test.ts index 313dfb1e0f1f..c4433b562e97 100644 --- a/x-pack/plugins/data_enhanced/server/routes/session.test.ts +++ b/x-pack/plugins/data_enhanced/server/routes/session.test.ts @@ -5,20 +5,26 @@ */ import type { MockedKeys } from '@kbn/utility-types/jest'; -import type { CoreSetup, RequestHandlerContext } from 'kibana/server'; + +import type { CoreSetup, Logger } from 'kibana/server'; import { coreMock, httpServerMock } from '../../../../../src/core/server/mocks'; -import { PluginStart as DataPluginStart } from '../../../../../src/plugins/data/server'; +import type { + PluginStart as DataPluginStart, + DataRequestHandlerContext, +} from '../../../../../src/plugins/data/server'; import { createSearchRequestHandlerContext } from './mocks'; import { registerSessionRoutes } from './session'; describe('registerSessionRoutes', () => { let mockCoreSetup: MockedKeys>; - let mockContext: jest.Mocked; + let mockContext: jest.Mocked; + let mockLogger: Logger; beforeEach(() => { mockCoreSetup = coreMock.createSetup(); + mockLogger = coreMock.createPluginInitializerContext().logger.get(); mockContext = createSearchRequestHandlerContext(); - registerSessionRoutes(mockCoreSetup.http.createRouter()); + registerSessionRoutes(mockCoreSetup.http.createRouter(), mockLogger); }); it('save calls session.save with sessionId and attributes', async () => { diff --git a/x-pack/plugins/data_enhanced/server/routes/session.ts b/x-pack/plugins/data_enhanced/server/routes/session.ts index b056513f1d2f..cbf683bd18fd 100644 --- a/x-pack/plugins/data_enhanced/server/routes/session.ts +++ b/x-pack/plugins/data_enhanced/server/routes/session.ts @@ -5,10 +5,11 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import { Logger } from 'src/core/server'; import { reportServerError } from '../../../../../src/plugins/kibana_utils/server'; +import { DataEnhancedPluginRouter } from '../type'; -export function registerSessionRoutes(router: IRouter): void { +export function registerSessionRoutes(router: DataEnhancedPluginRouter, logger: Logger): void { router.post( { path: '/internal/session', @@ -49,6 +50,7 @@ export function registerSessionRoutes(router: IRouter): void { body: response, }); } catch (err) { + logger.error(err); return reportServerError(res, err); } } @@ -73,6 +75,7 @@ export function registerSessionRoutes(router: IRouter): void { }); } catch (e) { const err = e.output?.payload || e; + logger.error(err); return reportServerError(res, err); } } @@ -106,6 +109,7 @@ export function registerSessionRoutes(router: IRouter): void { body: response, }); } catch (err) { + logger.error(err); return reportServerError(res, err); } } @@ -128,6 +132,7 @@ export function registerSessionRoutes(router: IRouter): void { return res.ok(); } catch (e) { const err = e.output?.payload || e; + logger.error(err); return reportServerError(res, err); } } @@ -156,6 +161,7 @@ export function registerSessionRoutes(router: IRouter): void { body: response, }); } catch (err) { + logger.error(err); return reportServerError(res, err); } } diff --git a/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts b/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts index f37aaf71fded..1107ed815508 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/session_service.test.ts @@ -114,6 +114,7 @@ describe('SearchSessionService', () => { maxUpdateRetries: 3, defaultExpiration: moment.duration(7, 'd'), trackingInterval: moment.duration(10, 's'), + management: {} as any, }, }, }); diff --git a/x-pack/plugins/data_enhanced/server/type.ts b/x-pack/plugins/data_enhanced/server/type.ts new file mode 100644 index 000000000000..a0dcbd81a5dd --- /dev/null +++ b/x-pack/plugins/data_enhanced/server/type.ts @@ -0,0 +1,18 @@ +/* + * 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 type { IRouter } from 'kibana/server'; +import type { DataRequestHandlerContext } from '../../../../src/plugins/data/server'; + +/** + * @internal + */ +export type DataEnhancedRequestHandlerContext = DataRequestHandlerContext; + +/** + * @internal + */ +export type DataEnhancedPluginRouter = IRouter; diff --git a/x-pack/plugins/data_enhanced/tsconfig.json b/x-pack/plugins/data_enhanced/tsconfig.json index ec5c656ac50b..c4b09276880d 100644 --- a/x-pack/plugins/data_enhanced/tsconfig.json +++ b/x-pack/plugins/data_enhanced/tsconfig.json @@ -12,6 +12,7 @@ "public/**/*", "server/**/*", "config.ts", + "../../../typings/**/*", // have to declare *.json explicitly due to https://github.com/microsoft/TypeScript/issues/25636 "public/autocomplete/providers/kql_query_suggestion/__fixtures__/*.json" ], @@ -22,6 +23,7 @@ { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, + { "path": "../../../src/plugins/management/tsconfig.json" }, { "path": "../task_manager/tsconfig.json" }, { "path": "../features/tsconfig.json" }, diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts index 85ec08fb7388..90700f8fa752 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.test.ts @@ -1452,6 +1452,140 @@ describe('#get', () => { }); }); +describe('#resolve', () => { + it('redirects request to underlying base client and does not alter response if type is not registered', async () => { + const mockedResponse = { + saved_object: { + id: 'some-id', + type: 'unknown-type', + attributes: { attrOne: 'one', attrSecret: 'secret', attrThree: 'three' }, + references: [], + }, + outcome: 'exactMatch' as 'exactMatch', + }; + + mockBaseClient.resolve.mockResolvedValue(mockedResponse); + + const options = { namespace: 'some-ns' }; + await expect(wrapper.resolve('unknown-type', 'some-id', options)).resolves.toEqual( + mockedResponse + ); + expect(mockBaseClient.resolve).toHaveBeenCalledTimes(1); + expect(mockBaseClient.resolve).toHaveBeenCalledWith('unknown-type', 'some-id', options); + }); + + it('redirects request to underlying base client and strips encrypted attributes except for ones with `dangerouslyExposeValue` set to `true` if type is registered', async () => { + const mockedResponse = { + saved_object: { + id: 'some-id', + type: 'known-type', + attributes: { + attrOne: 'one', + attrSecret: '*secret*', + attrNotSoSecret: '*not-so-secret*', + attrThree: 'three', + }, + references: [], + }, + outcome: 'exactMatch' as 'exactMatch', + }; + + mockBaseClient.resolve.mockResolvedValue(mockedResponse); + + const options = { namespace: 'some-ns' }; + await expect(wrapper.resolve('known-type', 'some-id', options)).resolves.toEqual({ + ...mockedResponse, + saved_object: { + ...mockedResponse.saved_object, + attributes: { attrOne: 'one', attrNotSoSecret: 'not-so-secret', attrThree: 'three' }, + }, + }); + expect(mockBaseClient.resolve).toHaveBeenCalledTimes(1); + expect(mockBaseClient.resolve).toHaveBeenCalledWith('known-type', 'some-id', options); + + expect(encryptedSavedObjectsServiceMockInstance.stripOrDecryptAttributes).toHaveBeenCalledTimes( + 1 + ); + expect(encryptedSavedObjectsServiceMockInstance.stripOrDecryptAttributes).toHaveBeenCalledWith( + { type: 'known-type', id: 'some-id', namespace: 'some-ns' }, + { + attrOne: 'one', + attrSecret: '*secret*', + attrNotSoSecret: '*not-so-secret*', + attrThree: 'three', + }, + undefined, + { user: mockAuthenticatedUser() } + ); + }); + + it('includes both attributes and error with modified outcome if decryption fails.', async () => { + const mockedResponse = { + saved_object: { + id: 'some-id', + type: 'known-type', + attributes: { + attrOne: 'one', + attrSecret: '*secret*', + attrNotSoSecret: '*not-so-secret*', + attrThree: 'three', + }, + references: [], + }, + outcome: 'exactMatch' as 'exactMatch', + }; + + mockBaseClient.resolve.mockResolvedValue(mockedResponse); + + const decryptionError = new EncryptionError( + 'something failed', + 'attrNotSoSecret', + EncryptionErrorOperation.Decryption + ); + encryptedSavedObjectsServiceMockInstance.stripOrDecryptAttributes.mockResolvedValue({ + attributes: { attrOne: 'one', attrThree: 'three' }, + error: decryptionError, + }); + + const options = { namespace: 'some-ns' }; + await expect(wrapper.resolve('known-type', 'some-id', options)).resolves.toEqual({ + ...mockedResponse, + saved_object: { + ...mockedResponse.saved_object, + attributes: { attrOne: 'one', attrThree: 'three' }, + error: decryptionError, + }, + }); + expect(mockBaseClient.resolve).toHaveBeenCalledTimes(1); + expect(mockBaseClient.resolve).toHaveBeenCalledWith('known-type', 'some-id', options); + + expect(encryptedSavedObjectsServiceMockInstance.stripOrDecryptAttributes).toHaveBeenCalledTimes( + 1 + ); + expect(encryptedSavedObjectsServiceMockInstance.stripOrDecryptAttributes).toHaveBeenCalledWith( + { type: 'known-type', id: 'some-id', namespace: 'some-ns' }, + { + attrOne: 'one', + attrSecret: '*secret*', + attrNotSoSecret: '*not-so-secret*', + attrThree: 'three', + }, + undefined, + { user: mockAuthenticatedUser() } + ); + }); + + it('fails if base client fails', async () => { + const failureReason = new Error('Something bad happened...'); + mockBaseClient.resolve.mockRejectedValue(failureReason); + + await expect(wrapper.resolve('known-type', 'some-id')).rejects.toThrowError(failureReason); + + expect(mockBaseClient.resolve).toHaveBeenCalledTimes(1); + expect(mockBaseClient.resolve).toHaveBeenCalledWith('known-type', 'some-id', undefined); + }); +}); + describe('#update', () => { it('redirects request to underlying base client if type is not registered', async () => { const attributes = { attrOne: 'one', attrSecret: 'secret', attrThree: 'three' }; diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts index 313e7c7da9eb..c3008a8e8650 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/encrypted_saved_objects_client_wrapper.ts @@ -181,6 +181,19 @@ export class EncryptedSavedObjectsClientWrapper implements SavedObjectsClientCon ); } + public async resolve(type: string, id: string, options?: SavedObjectsBaseOptions) { + const resolveResult = await this.options.baseClient.resolve(type, id, options); + const object = await this.handleEncryptedAttributesInResponse( + resolveResult.saved_object, + undefined as unknown, + getDescriptorNamespace(this.options.baseTypeRegistry, type, options?.namespace) + ); + return { + ...resolveResult, + saved_object: object, + }; + } + public async update( type: string, id: string, diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_logic.mock.ts new file mode 100644 index 000000000000..6326a41c1d2c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/engine_logic.mock.ts @@ -0,0 +1,21 @@ +/* + * 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 { generatePath } from 'react-router-dom'; + +export const mockEngineValues = { + engineName: 'some-engine', + engine: {}, +}; + +export const mockGenerateEnginePath = jest.fn((path, pathParams = {}) => + generatePath(path, { engineName: mockEngineValues.engineName, ...pathParams }) +); + +jest.mock('../components/engine', () => ({ + EngineLogic: { values: mockEngineValues }, + generateEnginePath: mockGenerateEnginePath, +})); diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/index.ts similarity index 80% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/index.ts rename to x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/index.ts index 35a785e3cba6..0b0a85b6fca9 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/__mocks__/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { DataVisualizerDataGrid } from './stats_datagrid'; +export { mockEngineValues } from './engine_logic.mock'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx index 82d2a6614a32..2cc6ff32d0ad 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.test.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import '../../__mocks__/engine_logic.mock'; + import React from 'react'; import { shallow } from 'enzyme'; import { Route, Switch } from 'react-router-dom'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx index ac5c472a9a38..f549a1a8d909 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/analytics_router.tsx @@ -12,8 +12,6 @@ import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chro import { BreadcrumbTrail } from '../../../shared/kibana_chrome/generate_breadcrumbs'; import { NotFound } from '../../../shared/not_found'; import { - getEngineRoute, - ENGINE_PATH, ENGINE_ANALYTICS_PATH, ENGINE_ANALYTICS_TOP_QUERIES_PATH, ENGINE_ANALYTICS_TOP_QUERIES_NO_RESULTS_PATH, @@ -23,6 +21,8 @@ import { ENGINE_ANALYTICS_QUERY_DETAILS_PATH, ENGINE_ANALYTICS_QUERY_DETAIL_PATH, } from '../../routes'; +import { generateEnginePath } from '../engine'; + import { ANALYTICS_TITLE, TOP_QUERIES, @@ -31,7 +31,6 @@ import { TOP_QUERIES_WITH_CLICKS, RECENT_QUERIES, } from './constants'; - import { Analytics, TopQueries, @@ -47,39 +46,38 @@ interface Props { } export const AnalyticsRouter: React.FC = ({ engineBreadcrumb }) => { const ANALYTICS_BREADCRUMB = [...engineBreadcrumb, ANALYTICS_TITLE]; - const engineName = engineBreadcrumb[1]; return ( - + - + - + - + - + - + - + - - + + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx index 93aff04b3f7c..bd4d088bc1d8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.test.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { setMockValues, setMockActions } from '../../../__mocks__/kea.mock'; +import { setMockActions } from '../../../__mocks__/kea.mock'; +import '../../__mocks__/engine_logic.mock'; import React from 'react'; import { shallow } from 'enzyme'; @@ -14,16 +15,12 @@ import { EuiCardTo } from '../../../shared/react_router_helpers'; import { DocumentCreationButtons } from './'; describe('DocumentCreationButtons', () => { - const values = { - engineName: 'test-engine', - }; const actions = { openDocumentCreation: jest.fn(), }; beforeEach(() => { jest.clearAllMocks(); - setMockValues(values); setMockActions(actions); }); @@ -57,6 +54,6 @@ describe('DocumentCreationButtons', () => { it('renders the crawler button with a link to the crawler page', () => { const wrapper = shallow(); - expect(wrapper.find(EuiCardTo).prop('to')).toEqual('/engines/test-engine/crawler'); + expect(wrapper.find(EuiCardTo).prop('to')).toEqual('/engines/some-engine/crawler'); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx index ce7cae567833..3a53b3c83d9e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/document_creation/document_creation_buttons.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { useActions, useValues } from 'kea'; +import { useActions } from 'kea'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -21,8 +21,8 @@ import { } from '@elastic/eui'; import { EuiCardTo } from '../../../shared/react_router_helpers'; -import { DOCS_PREFIX, getEngineRoute, ENGINE_CRAWLER_PATH } from '../../routes'; -import { EngineLogic } from '../engine'; +import { DOCS_PREFIX, ENGINE_CRAWLER_PATH } from '../../routes'; +import { generateEnginePath } from '../engine'; import { DocumentCreationLogic } from './'; @@ -33,8 +33,7 @@ interface Props { export const DocumentCreationButtons: React.FC = ({ disabled = false }) => { const { openDocumentCreation } = useActions(DocumentCreationLogic); - const { engineName } = useValues(EngineLogic); - const crawlerLink = getEngineRoute(engineName) + ENGINE_CRAWLER_PATH; + const crawlerLink = generateEnginePath(ENGINE_CRAWLER_PATH); return ( <> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts index f7476083009d..e33cd9b0e9e7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.test.ts @@ -11,10 +11,7 @@ import { mockFlashMessageHelpers, expectedAsyncError, } from '../../../__mocks__'; - -jest.mock('../engine', () => ({ - EngineLogic: { values: { engineName: 'engine1' } }, -})); +import { mockEngineValues } from '../../__mocks__'; import { DocumentDetailLogic } from './document_detail_logic'; import { InternalSchemaTypes } from '../../../shared/types'; @@ -32,6 +29,7 @@ describe('DocumentDetailLogic', () => { beforeEach(() => { jest.clearAllMocks(); + mockEngineValues.engineName = 'engine1'; }); describe('actions', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.ts index 62db2bf17235..8141ba73d418 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/document_detail_logic.ts @@ -7,12 +7,14 @@ import { kea, MakeLogicType } from 'kea'; import { i18n } from '@kbn/i18n'; -import { HttpLogic } from '../../../shared/http'; -import { EngineLogic } from '../engine'; import { flashAPIErrors, setQueuedSuccessMessage } from '../../../shared/flash_messages'; -import { FieldDetails } from './types'; import { KibanaLogic } from '../../../shared/kibana'; -import { ENGINE_DOCUMENTS_PATH, getEngineRoute } from '../../routes'; +import { HttpLogic } from '../../../shared/http'; + +import { ENGINE_DOCUMENTS_PATH } from '../../routes'; +import { EngineLogic, generateEnginePath } from '../engine'; + +import { FieldDetails } from './types'; interface DocumentDetailLogicValues { dataLoading: boolean; @@ -27,19 +29,6 @@ interface DocumentDetailLogicActions { type DocumentDetailLogicType = MakeLogicType; -const CONFIRM_DELETE = i18n.translate( - 'xpack.enterpriseSearch.appSearch.documentDetail.confirmDelete', - { - defaultMessage: 'Are you sure you want to delete this document?', - } -); -const DELETE_SUCCESS = i18n.translate( - 'xpack.enterpriseSearch.appSearch.documentDetail.deleteSuccess', - { - defaultMessage: 'Successfully marked document for deletion. It will be deleted momentarily.', - } -); - export const DocumentDetailLogic = kea({ path: ['enterprise_search', 'app_search', 'document_detail_logic'], actions: () => ({ @@ -64,6 +53,7 @@ export const DocumentDetailLogic = kea({ listeners: ({ actions }) => ({ getDocumentDetails: async ({ documentId }) => { const { engineName } = EngineLogic.values; + const { navigateToUrl } = KibanaLogic.values; try { const { http } = HttpLogic.values; @@ -76,20 +66,31 @@ export const DocumentDetailLogic = kea({ // error that will prevent the page from loading, so redirect to the documents page and // show the error flashAPIErrors(e, { isQueued: true }); - const engineRoute = getEngineRoute(engineName); - KibanaLogic.values.navigateToUrl(engineRoute + ENGINE_DOCUMENTS_PATH); + navigateToUrl(generateEnginePath(ENGINE_DOCUMENTS_PATH)); } }, deleteDocument: async ({ documentId }) => { const { engineName } = EngineLogic.values; + const { navigateToUrl } = KibanaLogic.values; + + const CONFIRM_DELETE = i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentDetail.confirmDelete', + { defaultMessage: 'Are you sure you want to delete this document?' } + ); + const DELETE_SUCCESS = i18n.translate( + 'xpack.enterpriseSearch.appSearch.documentDetail.deleteSuccess', + { + defaultMessage: + 'Successfully marked document for deletion. It will be deleted momentarily.', + } + ); if (window.confirm(CONFIRM_DELETE)) { try { const { http } = HttpLogic.values; await http.delete(`/api/app_search/engines/${engineName}/documents/${documentId}`); setQueuedSuccessMessage(DELETE_SUCCESS); - const engineRoute = getEngineRoute(engineName); - KibanaLogic.values.navigateToUrl(engineRoute + ENGINE_DOCUMENTS_PATH); + navigateToUrl(generateEnginePath(ENGINE_DOCUMENTS_PATH)); } catch (e) { flashAPIErrors(e); } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.test.ts index dd52f6b8227b..a87b73bd4e14 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.test.ts @@ -15,24 +15,49 @@ describe('buildSearchUIConfig', () => { foo: 'text' as SchemaTypes, bar: 'number' as SchemaTypes, }; + const fields = { + filterFields: ['fieldA', 'fieldB'], + sortFields: [], + }; - const config = buildSearchUIConfig(connector, schema); - expect(config.apiConnector).toEqual(connector); - expect(config.searchQuery.result_fields).toEqual({ - bar: { - raw: {}, - snippet: { - fallback: true, - size: 300, - }, + const config = buildSearchUIConfig(connector, schema, fields); + expect(config).toEqual({ + alwaysSearchOnInitialLoad: true, + apiConnector: connector, + initialState: { + sortDirection: 'desc', + sortField: 'id', }, - foo: { - raw: {}, - snippet: { - fallback: true, - size: 300, + searchQuery: { + disjunctiveFacets: ['fieldA', 'fieldB'], + facets: { + fieldA: { + size: 30, + type: 'value', + }, + fieldB: { + size: 30, + type: 'value', + }, + }, + result_fields: { + bar: { + raw: {}, + snippet: { + fallback: true, + size: 300, + }, + }, + foo: { + raw: {}, + snippet: { + fallback: true, + size: 300, + }, + }, }, }, + trackUrlState: false, }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts index 78e1fa9e7f3a..ac10e2db7191 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_search_ui_config.ts @@ -5,8 +5,17 @@ */ import { Schema } from '../../../../shared/types'; +import { Fields } from './types'; + +export const buildSearchUIConfig = (apiConnector: object, schema: Schema, fields: Fields) => { + const facets = fields.filterFields.reduce( + (facetsConfig, fieldName) => ({ + ...facetsConfig, + [fieldName]: { type: 'value', size: 30 }, + }), + {} + ); -export const buildSearchUIConfig = (apiConnector: object, schema: Schema) => { return { alwaysSearchOnInitialLoad: true, apiConnector, @@ -16,6 +25,8 @@ export const buildSearchUIConfig = (apiConnector: object, schema: Schema) => { sortField: 'id', }, searchQuery: { + disjunctiveFacets: fields.filterFields, + facets, result_fields: Object.keys(schema).reduce((acc: { [key: string]: object }, key: string) => { acc[key] = { snippet: { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_sort_options.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_sort_options.test.ts new file mode 100644 index 000000000000..e95f91f6f6f8 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_sort_options.test.ts @@ -0,0 +1,63 @@ +/* + * 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 { buildSortOptions } from './build_sort_options'; + +describe('buildSortOptions', () => { + it('builds sort options from a list of field names', () => { + const sortOptions = buildSortOptions( + { + filterFields: [], + sortFields: ['fieldA', 'fieldB'], + }, + [ + { + name: 'Relevance (asc)', + value: 'id', + direction: 'desc', + }, + { + name: 'Relevance (desc)', + value: 'id', + direction: 'asc', + }, + ] + ); + + expect(sortOptions).toEqual([ + { + name: 'Relevance (asc)', + value: 'id', + direction: 'desc', + }, + { + name: 'Relevance (desc)', + value: 'id', + direction: 'asc', + }, + { + direction: 'asc', + name: 'fieldA (asc)', + value: 'fieldA', + }, + { + direction: 'desc', + name: 'fieldA (desc)', + value: 'fieldA', + }, + { + direction: 'asc', + name: 'fieldB (asc)', + value: 'fieldB', + }, + { + direction: 'desc', + name: 'fieldB (desc)', + value: 'fieldB', + }, + ]); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_sort_options.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_sort_options.ts new file mode 100644 index 000000000000..8b80e557e4b6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/build_sort_options.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 { flatten } from 'lodash'; + +import { Fields, SortOption, SortDirection } from './types'; +import { ASCENDING, DESCENDING } from './constants'; + +const fieldNameToSortOptions = (fieldName: string): SortOption[] => + ['asc', 'desc'].map((direction) => ({ + name: direction === 'asc' ? ASCENDING(fieldName) : DESCENDING(fieldName), + value: fieldName, + direction: direction as SortDirection, + })); + +/** + * Adds two sort options for a given field, a "desc" and an "asc" option. + */ +export const buildSortOptions = ( + fields: Fields, + defaultSortOptions: SortOption[] +): SortOption[] => { + const sortFieldsOptions = flatten(fields.sortFields.map(fieldNameToSortOptions)); + const sortingOptions = [...defaultSortOptions, ...sortFieldsOptions]; + return sortingOptions; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/constants.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/constants.ts new file mode 100644 index 000000000000..1c7c3f5a65bd --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/constants.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 { i18n } from '@kbn/i18n'; + +export const ASCENDING = (fieldName: string) => + i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.sortBy.option.ascendingDropDownOptionLabel', + { + defaultMessage: '{fieldName} (asc)', + values: { fieldName }, + } + ); + +export const DESCENDING = (fieldName: string) => + i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.sortBy.option.descendingDropDownOptionLabel', + { + defaultMessage: '{fieldName} (desc)', + values: { fieldName }, + } + ); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss index ba9931dc90fd..d2e0a8155fa5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.scss @@ -25,4 +25,12 @@ background-color: $euiPageBackgroundColor; padding: $euiSizeL; } + + .documentsSearchExperience__facet { + line-height: 0; + + .euiCheckbox__label { + @include euiTextTruncate; + } + } } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx index 5df132a27bbb..410a4ea5bab7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.test.tsx @@ -7,25 +7,19 @@ import '../../../../__mocks__/enterprise_search_url.mock'; import { setMockValues } from '../../../../__mocks__'; -const mockSetFields = jest.fn(); - jest.mock('../../../../shared/use_local_storage', () => ({ - useLocalStorage: jest.fn(() => [ - { - filterFields: ['a', 'b', 'c'], - sortFields: ['d', 'c'], - }, - mockSetFields, - ]), + useLocalStorage: jest.fn(), })); +import { useLocalStorage } from '../../../../shared/use_local_storage'; import React from 'react'; // @ts-expect-error types are not available for this package yet -import { SearchProvider } from '@elastic/react-search-ui'; -import { shallow } from 'enzyme'; +import { SearchProvider, Facet } from '@elastic/react-search-ui'; +import { shallow, ShallowWrapper } from 'enzyme'; import { CustomizationCallout } from './customization_callout'; import { CustomizationModal } from './customization_modal'; +import { Fields } from './types'; import { SearchExperience } from './search_experience'; @@ -36,8 +30,16 @@ describe('SearchExperience', () => { apiKey: '1234', }, }; + const mockSetFields = jest.fn(); + const setFieldsInLocalStorage = (fields: Fields) => { + (useLocalStorage as jest.Mock).mockImplementation(() => [fields, mockSetFields]); + }; beforeEach(() => { + setFieldsInLocalStorage({ + filterFields: ['a', 'b', 'c'], + sortFields: ['d', 'c'], + }); jest.clearAllMocks(); setMockValues(values); }); @@ -47,12 +49,60 @@ describe('SearchExperience', () => { expect(wrapper.find(SearchProvider).length).toBe(1); }); + describe('when there are no selected filter fields', () => { + let wrapper: ShallowWrapper; + beforeEach(() => { + setFieldsInLocalStorage({ + filterFields: [], + sortFields: ['a', 'b'], + }); + wrapper = shallow(); + }); + + it('shows a customize callout instead of a button if no fields are yet selected', () => { + expect(wrapper.find(CustomizationCallout).exists()).toBe(true); + expect(wrapper.find('[data-test-subj="customize"]').exists()).toBe(false); + }); + + it('will show the customization modal when clicked', () => { + expect(wrapper.find(CustomizationModal).exists()).toBe(false); + wrapper.find(CustomizationCallout).simulate('click'); + + expect(wrapper.find(CustomizationModal).exists()).toBe(true); + }); + }); + + describe('when there are selected filter fields', () => { + let wrapper: ShallowWrapper; + beforeEach(() => { + setFieldsInLocalStorage({ + filterFields: ['a', 'b'], + sortFields: ['a', 'b'], + }); + wrapper = shallow(); + }); + + it('shows a customize button', () => { + expect(wrapper.find(CustomizationCallout).exists()).toBe(false); + expect(wrapper.find('[data-test-subj="customize"]').exists()).toBe(true); + }); + }); + + it('renders Facet components for filter fields', () => { + setFieldsInLocalStorage({ + filterFields: ['a', 'b', 'c'], + sortFields: [], + }); + const wrapper = shallow(); + expect(wrapper.find(Facet).length).toBe(3); + }); + describe('customization modal', () => { it('has a customization modal which can be opened and closed', () => { const wrapper = shallow(); expect(wrapper.find(CustomizationModal).exists()).toBe(false); - wrapper.find(CustomizationCallout).simulate('click'); + wrapper.find('[data-test-subj="customize"]').simulate('click'); expect(wrapper.find(CustomizationModal).exists()).toBe(true); wrapper.find(CustomizationModal).prop('onClose')(); @@ -61,14 +111,14 @@ describe('SearchExperience', () => { it('passes values from localStorage to the customization modal', () => { const wrapper = shallow(); - wrapper.find(CustomizationCallout).simulate('click'); + wrapper.find('[data-test-subj="customize"]').simulate('click'); expect(wrapper.find(CustomizationModal).prop('filterFields')).toEqual(['a', 'b', 'c']); expect(wrapper.find(CustomizationModal).prop('sortFields')).toEqual(['d', 'c']); }); it('updates selected fields in localStorage and closes modal on save', () => { const wrapper = shallow(); - wrapper.find(CustomizationCallout).simulate('click'); + wrapper.find('[data-test-subj="customize"]').simulate('click'); wrapper.find(CustomizationModal).prop('onSave')({ filterFields: ['new', 'filters'], sortFields: ['new', 'sorts'], diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx index e80ab2e18b2d..d829042bef11 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/search_experience.tsx @@ -7,36 +7,41 @@ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { useValues } from 'kea'; -import { EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiButton, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; // @ts-expect-error types are not available for this package yet; -import { SearchProvider, SearchBox, Sorting } from '@elastic/react-search-ui'; +import { SearchProvider, SearchBox, Sorting, Facet } from '@elastic/react-search-ui'; // @ts-expect-error types are not available for this package yet import AppSearchAPIConnector from '@elastic/search-ui-app-search-connector'; import './search_experience.scss'; -import { EngineLogic } from '../../engine'; import { externalUrl } from '../../../../shared/enterprise_search_url'; import { useLocalStorage } from '../../../../shared/use_local_storage'; +import { EngineLogic } from '../../engine'; -import { SearchBoxView, SortingView } from './views'; +import { Fields, SortOption } from './types'; +import { SearchBoxView, SortingView, MultiCheckboxFacetsView } from './views'; import { SearchExperienceContent } from './search_experience_content'; import { buildSearchUIConfig } from './build_search_ui_config'; import { CustomizationCallout } from './customization_callout'; import { CustomizationModal } from './customization_modal'; +import { buildSortOptions } from './build_sort_options'; +import { ASCENDING, DESCENDING } from './constants'; -const DEFAULT_SORT_OPTIONS = [ +const RECENTLY_UPLOADED = i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.sortBy.option.recentlyUploaded', + { + defaultMessage: 'Recently Uploaded', + } +); +const DEFAULT_SORT_OPTIONS: SortOption[] = [ { - name: i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.recentlyUploadedDesc', { - defaultMessage: 'Recently Uploaded (desc)', - }), + name: DESCENDING(RECENTLY_UPLOADED), value: 'id', direction: 'desc', }, { - name: i18n.translate('xpack.enterpriseSearch.appSearch.documents.search.recentlyUploadedAsc', { - defaultMessage: 'Recently Uploaded (asc)', - }), + name: ASCENDING(RECENTLY_UPLOADED), value: 'id', direction: 'asc', }, @@ -50,16 +55,15 @@ export const SearchExperience: React.FC = () => { const openCustomizationModal = () => setShowCustomizationModal(true); const closeCustomizationModal = () => setShowCustomizationModal(false); - const [fields, setFields] = useLocalStorage( + const [fields, setFields] = useLocalStorage( `documents-search-experience-customization--${engine.name}`, { - filterFields: [] as string[], - sortFields: [] as string[], + filterFields: [], + sortFields: [], } ); - // TODO const sortFieldsOptions = _flatten(fields.sortFields.map(fieldNameToSortOptions)) // we need to flatten this array since fieldNameToSortOptions returns an array of two sorting options - const sortingOptions = [...DEFAULT_SORT_OPTIONS /* TODO ...sortFieldsOptions*/]; + const sortingOptions = buildSortOptions(fields, DEFAULT_SORT_OPTIONS); const connector = new AppSearchAPIConnector({ cacheResponses: false, @@ -68,7 +72,7 @@ export const SearchExperience: React.FC = () => { searchKey: engine.apiKey, }); - const searchProviderConfig = buildSearchUIConfig(connector, engine.schema || {}); + const searchProviderConfig = buildSearchUIConfig(connector, engine.schema || {}, fields); return (
@@ -101,7 +105,36 @@ export const SearchExperience: React.FC = () => { view={SortingView} /> - + {fields.filterFields.length > 0 ? ( + <> + {fields.filterFields.map((fieldName) => ( +
+ + +
+ ))} + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.customizationButton', + { + defaultMessage: 'Customize filters and sort', + } + )} + + + ) : ( + + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/types.ts new file mode 100644 index 000000000000..0cde0f94b773 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/types.ts @@ -0,0 +1,18 @@ +/* + * 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 interface Fields { + filterFields: string[]; + sortFields: string[]; +} + +export type SortDirection = 'asc' | 'desc'; + +export interface SortOption { + name: string; + value: string; + direction: SortDirection; +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/index.ts index 8c88fc81d3a3..7032fa1a9a06 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/index.ts @@ -9,3 +9,4 @@ export { SortingView } from './sorting_view'; export { ResultView } from './result_view'; export { ResultsPerPageView } from './results_per_page_view'; export { PagingView } from './paging_view'; +export { MultiCheckboxFacetsView } from './multi_checkbox_facets_view'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/multi_checkbox_facets_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/multi_checkbox_facets_view.test.tsx new file mode 100644 index 000000000000..7f43ca12652c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/multi_checkbox_facets_view.test.tsx @@ -0,0 +1,99 @@ +/* + * 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 { MultiCheckboxFacetsView } from './multi_checkbox_facets_view'; + +describe('MultiCheckboxFacetsView', () => { + const props = { + label: 'foo', + options: [ + { + value: 'value1', + selected: false, + }, + { + value: 'value2', + selected: false, + }, + ], + showMore: true, + onMoreClick: jest.fn(), + onRemove: jest.fn(), + onSelect: jest.fn(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders', () => { + const wrapper = shallow(); + expect(wrapper.isEmptyRender()).toBe(false); + }); + + it('calls onMoreClick when more button is clicked', () => { + const wrapper = shallow(); + wrapper.find('[data-test-subj="more"]').simulate('click'); + expect(props.onMoreClick).toHaveBeenCalled(); + }); + + it('calls onSelect when an option is selected', () => { + const wrapper = shallow(); + wrapper.find('[data-test-subj="checkbox-group"]').simulate('change', 'generated-id_1'); + expect(props.onSelect).toHaveBeenCalledWith('value2'); + }); + + it('calls onRemove if the option was already selected', () => { + const wrapper = shallow( + + ); + wrapper.find('[data-test-subj="checkbox-group"]').simulate('change', 'generated-id_1'); + expect(props.onRemove).toHaveBeenCalledWith('value2'); + }); + + it('it passes options to EuiCheckboxGroup, converting no values to the text "No Value"', () => { + const wrapper = shallow( + + ); + const options = wrapper.find('[data-test-subj="checkbox-group"]').prop('options'); + expect(options).toEqual([ + { id: 'generated-id_0', label: 'value1' }, + { id: 'generated-id_1', label: '' }, + ]); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/multi_checkbox_facets_view.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/multi_checkbox_facets_view.tsx new file mode 100644 index 000000000000..df61e6e3dcc0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/documents/search_experience/views/multi_checkbox_facets_view.tsx @@ -0,0 +1,114 @@ +/* + * 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 { + htmlIdGenerator, + EuiCheckboxGroup, + EuiFlexGroup, + EuiButtonEmpty, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +interface Option { + value: string; + selected: boolean; +} + +interface Props { + label: string; + options: Option[]; + showMore: boolean; + onMoreClick(): void; + onRemove(id: string): void; + onSelect(id: string): void; +} + +const getIndexFromId = (id: string) => parseInt(id.split('_')[1], 10); + +export const MultiCheckboxFacetsView: React.FC = ({ + label, + onMoreClick, + onRemove, + onSelect, + options, + showMore, +}) => { + const getId = htmlIdGenerator(); + + const optionToCheckBoxGroupOption = (option: Option, index: number) => ({ + id: getId(String(index)), + label: + option.value || + i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.multiCheckboxFacetsView.noValue.selectOption', + { + defaultMessage: '', + } + ), + }); + + const optionToSelectedMapReducer = ( + selectedMap: { [name: string]: boolean }, + option: Option, + index: number + ) => { + if (option.selected) { + selectedMap[getId(String(index))] = true; + } + return selectedMap; + }; + + const checkboxGroupOptions = options.map(optionToCheckBoxGroupOption); + const idToSelectedMap = options.reduce(optionToSelectedMapReducer, {}); + + const onChange = (checkboxId: string) => { + const index = getIndexFromId(checkboxId); + const option = options[index]; + if (option.selected) { + onRemove(option.value); + return; + } + onSelect(option.value); + }; + + return ( + <> + + {showMore && ( + <> + + + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.documents.search.multiCheckboxFacetsView.showMore', + { + defaultMessage: 'Show more', + } + )} + + + + )} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts index 62f444cf8f6a..48cbaeef70c1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_logic.test.ts @@ -89,7 +89,7 @@ describe('EngineLogic', () => { const mockReindexJob = { percentageComplete: 50, numDocumentsWithErrors: 2, - activeReindexJobId: 123, + activeReindexJobId: '123', }; EngineLogic.actions.setIndexingStatus(mockReindexJob); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx index 95c9beb9b866..f4ef2f5963c3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.test.tsx @@ -5,15 +5,16 @@ */ import { setMockValues, rerender } from '../../../__mocks__'; +import { mockEngineValues } from '../../__mocks__'; import React from 'react'; import { shallow } from 'enzyme'; import { EuiBadge, EuiIcon } from '@elastic/eui'; -import { EngineNav } from './'; +import { EngineNav } from './engine_nav'; describe('EngineNav', () => { - const values = { myRole: {}, engineName: 'some-engine', dataLoading: false, engine: {} }; + const values = { ...mockEngineValues, myRole: {}, dataLoading: false }; beforeEach(() => { setMockValues(values); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx index 40ae2cef0acb..0e5a7d56e906 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_nav.tsx @@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n'; import { SideNavLink, SideNavItem } from '../../../shared/layout'; import { AppLogic } from '../../app_logic'; import { - getEngineRoute, + ENGINE_PATH, ENGINE_ANALYTICS_PATH, ENGINE_DOCUMENTS_PATH, ENGINE_SCHEMA_PATH, @@ -40,7 +40,7 @@ import { RESULT_SETTINGS_TITLE } from '../result_settings'; import { SEARCH_UI_TITLE } from '../search_ui'; import { API_LOGS_TITLE } from '../api_logs'; -import { EngineLogic } from './'; +import { EngineLogic, generateEnginePath } from './'; import { EngineDetails } from './types'; import './engine_nav.scss'; @@ -75,7 +75,6 @@ export const EngineNav: React.FC = () => { if (dataLoading) return null; if (!engineName) return null; - const engineRoute = getEngineRoute(engineName); const { invalidBoosts, unsearchedUnconfirmedFields } = engine as Required; return ( @@ -99,12 +98,12 @@ export const EngineNav: React.FC = () => { )} - + {OVERVIEW_TITLE} {canViewEngineAnalytics && ( @@ -113,7 +112,7 @@ export const EngineNav: React.FC = () => { )} {canViewEngineDocuments && ( @@ -123,7 +122,7 @@ export const EngineNav: React.FC = () => { {canViewEngineSchema && ( @@ -158,7 +157,7 @@ export const EngineNav: React.FC = () => { {canViewEngineCrawler && !isMetaEngine && ( {CRAWLER_TITLE} @@ -167,7 +166,7 @@ export const EngineNav: React.FC = () => { {canViewMetaEngineSourceEngines && isMetaEngine && ( {ENGINES_TITLE} @@ -176,7 +175,7 @@ export const EngineNav: React.FC = () => { {canManageEngineRelevanceTuning && ( @@ -211,7 +210,7 @@ export const EngineNav: React.FC = () => { {canManageEngineSynonyms && ( {SYNONYMS_TITLE} @@ -220,7 +219,7 @@ export const EngineNav: React.FC = () => { {canManageEngineCurations && ( {CURATIONS_TITLE} @@ -229,7 +228,7 @@ export const EngineNav: React.FC = () => { {canManageEngineResultSettings && ( {RESULT_SETTINGS_TITLE} @@ -238,7 +237,7 @@ export const EngineNav: React.FC = () => { {canManageEngineSearchUi && ( {SEARCH_UI_TITLE} @@ -247,7 +246,7 @@ export const EngineNav: React.FC = () => { {canViewEngineApiLogs && ( {API_LOGS_TITLE} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx index 362454c31f0d..aa8b406cf777 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.test.tsx @@ -7,6 +7,7 @@ import '../../../__mocks__/react_router_history.mock'; import { unmountHandler } from '../../../__mocks__/shallow_useeffect.mock'; import { mockFlashMessageHelpers, setMockValues, setMockActions } from '../../../__mocks__'; +import { mockEngineValues } from '../../__mocks__'; import React from 'react'; import { shallow } from 'enzyme'; @@ -16,14 +17,14 @@ import { Loading } from '../../../shared/loading'; import { EngineOverview } from '../engine_overview'; import { AnalyticsRouter } from '../analytics'; -import { EngineRouter } from './'; +import { EngineRouter } from './engine_router'; describe('EngineRouter', () => { const values = { + ...mockEngineValues, dataLoading: false, engineNotFound: false, myRole: {}, - engineName: 'some-engine', }; const actions = { setEngineName: jest.fn(), initializeEngine: jest.fn(), clearEngine: jest.fn() }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx index 47fe302ac701..fd21507a427d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/engine_router.tsx @@ -17,7 +17,6 @@ import { AppLogic } from '../../app_logic'; // TODO: Uncomment and add more routes as we migrate them import { ENGINES_PATH, - ENGINE_PATH, ENGINE_ANALYTICS_PATH, ENGINE_DOCUMENTS_PATH, ENGINE_DOCUMENT_DETAIL_PATH, @@ -86,14 +85,14 @@ export const EngineRouter: React.FC = () => { return ( {canViewEngineAnalytics && ( - + )} - + - + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/index.ts index 4e7d81f73fb8..7846eb9d03b7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/index.ts @@ -7,3 +7,4 @@ export { EngineRouter } from './engine_router'; export { EngineNav } from './engine_nav'; export { EngineLogic } from './engine_logic'; +export { generateEnginePath } from './utils'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.test.ts new file mode 100644 index 000000000000..cff4065c13f5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.test.ts @@ -0,0 +1,28 @@ +/* + * 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 { mockEngineValues } from '../../__mocks__'; + +import { generateEnginePath } from './utils'; + +describe('generateEnginePath', () => { + mockEngineValues.engineName = 'hello-world'; + + it('generates paths with engineName filled from state', () => { + expect(generateEnginePath('/engines/:engineName/example')).toEqual( + '/engines/hello-world/example' + ); + }); + + it('allows overriding engineName and filling other params', () => { + expect( + generateEnginePath('/engines/:engineName/foo/:bar', { + engineName: 'override', + bar: 'baz', + }) + ).toEqual('/engines/override/foo/baz'); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.ts new file mode 100644 index 000000000000..b7efcbb6e6b2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine/utils.ts @@ -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 { generatePath } from 'react-router-dom'; + +import { EngineLogic } from './'; + +/** + * Generate a path with engineName automatically filled from EngineLogic state + */ +export const generateEnginePath = (path: string, pathParams: object = {}) => { + const { engineName } = EngineLogic.values; + return generatePath(path, { engineName, ...pathParams }); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx index fb34682e3c7e..d7d22cafee43 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { setMockValues } from '../../../../__mocks__/kea.mock'; +import '../../../__mocks__/engine_logic.mock'; import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; @@ -18,9 +18,6 @@ describe('RecentApiLogs', () => { beforeAll(() => { jest.clearAllMocks(); - setMockValues({ - engineName: 'some-engine', - }); wrapper = shallow(); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx index 3f42419252d2..207666ef6746 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/recent_api_logs.tsx @@ -5,7 +5,6 @@ */ import React from 'react'; -import { useValues } from 'kea'; import { EuiPageContent, @@ -16,17 +15,13 @@ import { } from '@elastic/eui'; import { EuiButtonTo } from '../../../../shared/react_router_helpers'; +import { ENGINE_API_LOGS_PATH } from '../../../routes'; +import { generateEnginePath } from '../../engine'; -import { ENGINE_API_LOGS_PATH, getEngineRoute } from '../../../routes'; import { RECENT_API_EVENTS } from '../../api_logs/constants'; import { VIEW_API_LOGS } from '../constants'; -import { EngineLogic } from '../../engine'; - export const RecentApiLogs: React.FC = () => { - const { engineName } = useValues(EngineLogic); - const engineRoute = getEngineRoute(engineName); - return ( @@ -36,7 +31,7 @@ export const RecentApiLogs: React.FC = () => { - + {VIEW_API_LOGS} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx index b1350b7e102e..14fb19b8ca2b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.test.tsx @@ -5,6 +5,7 @@ */ import { setMockValues } from '../../../../__mocks__/kea.mock'; +import '../../../__mocks__/engine_logic.mock'; import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; @@ -20,7 +21,6 @@ describe('TotalCharts', () => { beforeAll(() => { jest.clearAllMocks(); setMockValues({ - engineName: 'some-engine', startDate: '1970-01-01', queriesPerDay: [0, 1, 2, 3, 5, 10, 50], operationsPerDay: [0, 0, 0, 0, 0, 0, 0], diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx index 4ef4e08dee76..e8454cdc95eb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/total_charts.tsx @@ -19,20 +19,15 @@ import { } from '@elastic/eui'; import { EuiButtonTo } from '../../../../shared/react_router_helpers'; +import { ENGINE_ANALYTICS_PATH, ENGINE_API_LOGS_PATH } from '../../../routes'; +import { generateEnginePath } from '../../engine'; -import { ENGINE_ANALYTICS_PATH, ENGINE_API_LOGS_PATH, getEngineRoute } from '../../../routes'; import { TOTAL_QUERIES, TOTAL_API_OPERATIONS } from '../../analytics/constants'; import { VIEW_ANALYTICS, VIEW_API_LOGS, LAST_7_DAYS } from '../constants'; - import { AnalyticsChart, convertToChartData } from '../../analytics'; - -import { EngineLogic } from '../../engine'; import { EngineOverviewLogic } from '../'; export const TotalCharts: React.FC = () => { - const { engineName } = useValues(EngineLogic); - const engineRoute = getEngineRoute(engineName); - const { startDate, queriesPerDay, operationsPerDay } = useValues(EngineOverviewLogic); return ( @@ -49,7 +44,7 @@ export const TotalCharts: React.FC = () => { - + {VIEW_ANALYTICS} @@ -78,7 +73,7 @@ export const TotalCharts: React.FC = () => { - + {VIEW_API_LOGS} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx index 1dde4db15a42..a0f150ca4ec4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.test.tsx @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../__mocks__/kea.mock'; import '../../../__mocks__/enterprise_search_url.mock'; -import { mockTelemetryActions, mountWithIntl } from '../../../__mocks__/'; +import { mockTelemetryActions, mountWithIntl } from '../../../__mocks__'; import React from 'react'; import { EuiBasicTable, EuiPagination, EuiButtonEmpty } from '@elastic/eui'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx index e8944c37efa4..a9455b4a2306 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engines/engines_table.tsx @@ -5,6 +5,7 @@ */ import React from 'react'; +import { generatePath } from 'react-router-dom'; import { useActions } from 'kea'; import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; import { FormattedMessage, FormattedDate, FormattedNumber } from '@kbn/i18n/react'; @@ -12,7 +13,7 @@ import { i18n } from '@kbn/i18n'; import { TelemetryLogic } from '../../../shared/telemetry'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; -import { getEngineRoute } from '../../routes'; +import { ENGINE_PATH } from '../../routes'; import { ENGINES_PAGE_SIZE } from '../../../../../common/constants'; import { UNIVERSAL_LANGUAGE } from '../../constants'; @@ -39,8 +40,8 @@ export const EnginesTable: React.FC = ({ }) => { const { sendAppSearchTelemetry } = useActions(TelemetryLogic); - const engineLinkProps = (name: string) => ({ - to: getEngineRoute(name), + const engineLinkProps = (engineName: string) => ({ + to: generatePath(ENGINE_PATH, { engineName }), onClick: () => sendAppSearchTelemetry({ action: 'clicked', diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx index f25eb2a4ba09..a3935bb782f9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result/result.tsx @@ -5,6 +5,7 @@ */ import React, { useState, useMemo } from 'react'; +import { generatePath } from 'react-router-dom'; import classNames from 'classnames'; import './result.scss'; @@ -12,12 +13,13 @@ import './result.scss'; import { EuiPanel, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { ReactRouterHelper } from '../../../shared/react_router_helpers/eui_components'; +import { ENGINE_DOCUMENT_DETAIL_PATH } from '../../routes'; + +import { Schema } from '../../../shared/types'; import { FieldValue, Result as ResultType } from './types'; import { ResultField } from './result_field'; import { ResultHeader } from './result_header'; -import { getDocumentDetailRoute } from '../../routes'; -import { ReactRouterHelper } from '../../../shared/react_router_helpers/eui_components'; -import { Schema } from '../../../shared/types'; interface Props { result: ResultType; @@ -50,7 +52,10 @@ export const Result: React.FC = ({ if (schemaForTypeHighlights) return schemaForTypeHighlights[fieldName]; }; - const documentLink = getDocumentDetailRoute(resultMeta.engine, resultMeta.id); + const documentLink = generatePath(ENGINE_DOCUMENT_DETAIL_PATH, { + engineName: resultMeta.engine, + documentId: resultMeta.id, + }); const conditionallyLinkedArticle = (children: React.ReactNode) => { return shouldLinkToDetailPage ? ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts index 0f3d34cfa633..41e9bfa19e0f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/routes.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { generatePath } from 'react-router-dom'; - import { CURRENT_MAJOR_VERSION } from '../../../common/version'; export const DOCS_PREFIX = `https://www.elastic.co/guide/en/app-search/${CURRENT_MAJOR_VERSION}`; @@ -20,11 +18,10 @@ export const ROLE_MAPPINGS_PATH = '#/role-mappings'; // This page seems to 404 i export const ENGINES_PATH = '/engines'; export const CREATE_ENGINES_PATH = `${ENGINES_PATH}/new`; -export const ENGINE_PATH = '/engines/:engineName'; -export const SAMPLE_ENGINE_PATH = '/engines/national-parks-demo'; -export const getEngineRoute = (engineName: string) => generatePath(ENGINE_PATH, { engineName }); +export const ENGINE_PATH = `${ENGINES_PATH}/:engineName`; +export const SAMPLE_ENGINE_PATH = `${ENGINES_PATH}/national-parks-demo`; -export const ENGINE_ANALYTICS_PATH = '/analytics'; +export const ENGINE_ANALYTICS_PATH = `${ENGINE_PATH}/analytics`; export const ENGINE_ANALYTICS_TOP_QUERIES_PATH = `${ENGINE_ANALYTICS_PATH}/top_queries`; export const ENGINE_ANALYTICS_TOP_QUERIES_NO_CLICKS_PATH = `${ENGINE_ANALYTICS_PATH}/top_queries_no_clicks`; export const ENGINE_ANALYTICS_TOP_QUERIES_NO_RESULTS_PATH = `${ENGINE_ANALYTICS_PATH}/top_queries_no_results`; @@ -33,25 +30,22 @@ export const ENGINE_ANALYTICS_RECENT_QUERIES_PATH = `${ENGINE_ANALYTICS_PATH}/re export const ENGINE_ANALYTICS_QUERY_DETAILS_PATH = `${ENGINE_ANALYTICS_PATH}/query_detail`; export const ENGINE_ANALYTICS_QUERY_DETAIL_PATH = `${ENGINE_ANALYTICS_QUERY_DETAILS_PATH}/:query`; -export const ENGINE_DOCUMENTS_PATH = '/documents'; +export const ENGINE_DOCUMENTS_PATH = `${ENGINE_PATH}/documents`; export const ENGINE_DOCUMENT_DETAIL_PATH = `${ENGINE_DOCUMENTS_PATH}/:documentId`; -export const getDocumentDetailRoute = (engineName: string, documentId: string) => { - return generatePath(ENGINE_PATH + ENGINE_DOCUMENT_DETAIL_PATH, { engineName, documentId }); -}; -export const ENGINE_SCHEMA_PATH = '/schema/edit'; -export const ENGINE_REINDEX_JOB_PATH = '/reindex-job/:activeReindexJobId'; +export const ENGINE_SCHEMA_PATH = `${ENGINE_PATH}/schema/edit`; +export const ENGINE_REINDEX_JOB_PATH = `${ENGINE_PATH}/reindex-job/:activeReindexJobId`; -export const ENGINE_CRAWLER_PATH = '/crawler'; +export const ENGINE_CRAWLER_PATH = `${ENGINE_PATH}/crawler`; // TODO: Crawler sub-pages -export const META_ENGINE_SOURCE_ENGINES_PATH = '/engines'; +export const META_ENGINE_SOURCE_ENGINES_PATH = `${ENGINE_PATH}/engines`; -export const ENGINE_RELEVANCE_TUNING_PATH = '/search-settings'; -export const ENGINE_SYNONYMS_PATH = '/synonyms'; -export const ENGINE_CURATIONS_PATH = '/curations'; +export const ENGINE_RELEVANCE_TUNING_PATH = `${ENGINE_PATH}/search-settings`; +export const ENGINE_SYNONYMS_PATH = `${ENGINE_PATH}/synonyms`; +export const ENGINE_CURATIONS_PATH = `${ENGINE_PATH}/curations`; // TODO: Curations sub-pages -export const ENGINE_RESULT_SETTINGS_PATH = '/result-settings'; +export const ENGINE_RESULT_SETTINGS_PATH = `${ENGINE_PATH}/result-settings`; -export const ENGINE_SEARCH_UI_PATH = '/reference_application/new'; -export const ENGINE_API_LOGS_PATH = '/api-logs'; +export const ENGINE_SEARCH_UI_PATH = `${ENGINE_PATH}/reference_application/new`; +export const ENGINE_API_LOGS_PATH = `${ENGINE_PATH}/api-logs`; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts index 558271a8fbdc..0a80f8e36102 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/indexing_status/indexing_status_logic.test.ts @@ -21,7 +21,7 @@ describe('IndexingStatusLogic', () => { const mockStatusResponse = { percentageComplete: 50, numDocumentsWithErrors: 3, - activeReindexJobId: 1, + activeReindexJobId: '1', }; beforeEach(() => { diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts index f5833a0ac9f8..0ad5f292ebed 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts @@ -33,7 +33,7 @@ export interface SchemaConflicts { export interface IIndexingStatus { percentageComplete: number; numDocumentsWithErrors: number; - activeReindexJobId: number; + activeReindexJobId: string; } export interface IndexJob extends IIndexingStatus { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts index b3962a7c88cc..0e0d1fa86403 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts @@ -6,6 +6,7 @@ import { mergeServerAndStaticData } from '../views/content_sources/sources_logic'; import { staticSourceData } from '../views/content_sources/source_data'; +import { groups } from './groups.mock'; export const contentSources = [ { @@ -38,6 +39,62 @@ export const contentSources = [ }, ]; +export const fullContentSources = [ + { + ...contentSources[0], + activities: [ + { + details: ['detail'], + event: 'this is an event', + time: '2021-01-20', + status: 'syncing', + }, + ], + details: [ + { + title: 'My Thing', + description: 'This is a thing.', + }, + ], + summary: [ + { + count: 1, + type: 'summary', + }, + ], + groups, + custom: false, + accessToken: '123token', + urlField: 'myLink', + titleField: 'heading', + licenseSupportsPermissions: true, + serviceTypeSupportsPermissions: true, + indexPermissions: true, + hasPermissions: true, + urlFieldIsLinkable: true, + createdAt: '2021-01-20', + serviceName: 'myService', + }, + { + ...contentSources[1], + activities: [], + details: [], + summary: [], + groups: [], + custom: true, + accessToken: '123token', + urlField: 'url', + titleField: 'title', + licenseSupportsPermissions: false, + serviceTypeSupportsPermissions: false, + indexPermissions: false, + hasPermissions: false, + urlFieldIsLinkable: false, + createdAt: '2021-01-20', + serviceName: 'custom', + }, +]; + export const configuredSources = [ { serviceType: 'gmail', @@ -258,3 +315,11 @@ export const exampleResult = { }, ], }; + +export const mostRecentIndexJob = { + isActive: true, + hasErrors: true, + percentageComplete: 50, + activeReindexJobId: '123', + numDocumentsWithErrors: 1, +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/meta.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/meta.mock.ts new file mode 100644 index 000000000000..e596ea5d7e94 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/meta.mock.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 { DEFAULT_META } from '../../shared/constants'; + +export const mockMeta = { + ...DEFAULT_META, + page: { + current: 1, + total_results: 50, + total_pages: 5, + }, +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.test.tsx new file mode 100644 index 000000000000..826e86353307 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.test.tsx @@ -0,0 +1,130 @@ +/* + * 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 '../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues } from '../../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { EuiEmptyPrompt, EuiPanel, EuiTable } from '@elastic/eui'; + +import { fullContentSources } from '../../../__mocks__/content_sources.mock'; + +import { Loading } from '../../../../shared/loading'; +import { ComponentLoader } from '../../../components/shared/component_loader'; + +import { Overview } from './overview'; + +describe('Overview', () => { + const contentSource = fullContentSources[0]; + const dataLoading = false; + const isOrganization = true; + + const mockValues = { + contentSource, + dataLoading, + isOrganization, + }; + + beforeEach(() => { + setMockValues({ ...mockValues }); + }); + + it('renders', () => { + const wrapper = shallow(); + const documentSummary = wrapper.find('[data-test-subj="DocumentSummary"]').dive(); + + expect(documentSummary).toHaveLength(1); + expect(documentSummary.find('[data-test-subj="DocumentSummaryRow"]')).toHaveLength(1); + }); + + it('returns Loading when loading', () => { + setMockValues({ ...mockValues, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('renders ComponentLoader when loading', () => { + setMockValues({ + ...mockValues, + contentSource: { + ...fullContentSources[1], + summary: null, + }, + }); + + const wrapper = shallow(); + const documentSummary = wrapper.find('[data-test-subj="DocumentSummary"]').dive(); + + expect(documentSummary.find(ComponentLoader)).toHaveLength(1); + }); + + it('handles empty states', () => { + setMockValues({ ...mockValues, contentSource: fullContentSources[1] }); + const wrapper = shallow(); + const documentSummary = wrapper.find('[data-test-subj="DocumentSummary"]').dive(); + const activitySummary = wrapper.find('[data-test-subj="ActivitySummary"]').dive(); + + expect(documentSummary.find(EuiEmptyPrompt)).toHaveLength(1); + expect(activitySummary.find(EuiEmptyPrompt)).toHaveLength(1); + expect(wrapper.find('[data-test-subj="GroupsSummary"]')).toHaveLength(0); + }); + + it('renders activity table', () => { + const wrapper = shallow(); + const activitySummary = wrapper.find('[data-test-subj="ActivitySummary"]').dive(); + + expect(activitySummary.find(EuiTable)).toHaveLength(1); + }); + + it('renders GroupsSummary', () => { + const wrapper = shallow(); + const groupsSummary = wrapper.find('[data-test-subj="GroupsSummary"]').dive(); + + expect(groupsSummary.find('[data-test-subj="SourceGroupLink"]')).toHaveLength(1); + }); + + it('renders DocumentationCallout', () => { + setMockValues({ ...mockValues, contentSource: fullContentSources[1] }); + const wrapper = shallow(); + const documentationCallout = wrapper.find('[data-test-subj="DocumentationCallout"]').dive(); + + expect(documentationCallout.find(EuiPanel)).toHaveLength(1); + }); + + it('renders PermissionsStatus', () => { + setMockValues({ + ...mockValues, + contentSource: { + ...fullContentSources[0], + serviceTypeSupportsPermissions: true, + hasPermissions: false, + }, + }); + + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="PermissionsStatus"]')).toHaveLength(1); + }); + + it('renders DocumentPermissionsDisabled', () => { + setMockValues({ + ...mockValues, + contentSource: { + ...fullContentSources[1], + serviceTypeSupportsPermissions: true, + custom: false, + }, + }); + + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="DocumentPermissionsDisabled"]')).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx index 71d79b4b2a08..a0797305de6c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/overview.tsx @@ -55,7 +55,6 @@ export const Overview: React.FC = () => { const { id, summary, - documentCount, activities, groups, details, @@ -72,24 +71,22 @@ export const Overview: React.FC = () => { const DocumentSummary = () => { let totalDocuments = 0; - const tableContent = - summary && - summary.map((item, index) => { - totalDocuments += item.count; - return ( - item.count > 0 && ( - - {item.type} - {item.count.toLocaleString('en-US')} - - ) - ); - }); + const tableContent = summary?.map((item, index) => { + totalDocuments += item.count; + return ( + item.count > 0 && ( + + {item.type} + {item.count.toLocaleString('en-US')} + + ) + ); + }); const emptyState = ( <> - + No content yet} iconType="documents" @@ -121,14 +118,10 @@ export const Overview: React.FC = () => { {tableContent} - {summary ? Total documents : 'Documents'} + Total documents - {summary ? ( - {totalDocuments.toLocaleString('en-US')} - ) : ( - parseInt(documentCount, 10).toLocaleString('en-US') - )} + {totalDocuments.toLocaleString('en-US')} @@ -142,7 +135,7 @@ export const Overview: React.FC = () => { const emptyState = ( <> - + There is no recent activity} iconType="clock" @@ -202,31 +195,29 @@ export const Overview: React.FC = () => { ); }; - const GroupsSummary = () => { - return !groups.length ? null : ( - <> - -

Group Access

-
- - - {groups.map((group, index) => ( - - - - {group.name} - - - - ))} - - - ); - }; + const groupsSummary = ( + <> + +

Group Access

+
+ + + {groups.map((group, index) => ( + + + + {group.name} + + + + ))} + + + ); const detailsSummary = ( <> @@ -285,7 +276,7 @@ export const Overview: React.FC = () => {

Document-level permissions

- + @@ -333,7 +324,7 @@ export const Overview: React.FC = () => { ); const permissionsStatus = ( - +
Status @@ -426,20 +417,18 @@ export const Overview: React.FC = () => { - + {!isFederatedSource && ( - + )} - - - + {groups.length > 0 && groupsSummary} {details.length > 0 && {detailsSummary}} {!custom && serviceTypeSupportsPermissions && ( <> @@ -458,7 +447,10 @@ export const Overview: React.FC = () => { {sourceStatus} {credentials} - +

Learn more diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.test.tsx new file mode 100644 index 000000000000..1d7a73970bf1 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema.test.tsx @@ -0,0 +1,125 @@ +/* + * 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 '../../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { EuiEmptyPrompt, EuiFieldSearch } from '@elastic/eui'; + +import { mostRecentIndexJob } from '../../../../__mocks__/content_sources.mock'; + +import { IndexingStatus } from '../../../../../shared/indexing_status'; +import { Loading } from '../../../../../shared/loading'; +import { SchemaAddFieldModal } from '../../../../../shared/schema/schema_add_field_modal'; + +import { SchemaFieldsTable } from './schema_fields_table'; + +import { Schema } from './schema'; + +describe('Schema', () => { + const initializeSchema = jest.fn(); + const onIndexingComplete = jest.fn(); + const addNewField = jest.fn(); + const updateFields = jest.fn(); + const openAddFieldModal = jest.fn(); + const closeAddFieldModal = jest.fn(); + const setFilterValue = jest.fn(); + + const sourceId = '123'; + const activeSchema = { + foo: 'string', + }; + const filterValue = ''; + const showAddFieldModal = false; + const addFieldFormErrors = null; + const formUnchanged = true; + const dataLoading = false; + const isOrganization = true; + + const mockValues = { + sourceId, + activeSchema, + filterValue, + showAddFieldModal, + addFieldFormErrors, + mostRecentIndexJob: {}, + formUnchanged, + dataLoading, + isOrganization, + }; + + beforeEach(() => { + setMockValues({ ...mockValues }); + setMockActions({ + initializeSchema, + onIndexingComplete, + addNewField, + updateFields, + openAddFieldModal, + closeAddFieldModal, + setFilterValue, + }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(SchemaFieldsTable)).toHaveLength(1); + }); + + it('returns loading when loading', () => { + setMockValues({ ...mockValues, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('handles empty state', () => { + setMockValues({ ...mockValues, activeSchema: {} }); + const wrapper = shallow(); + + expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1); + }); + + it('renders modal', () => { + setMockValues({ ...mockValues, showAddFieldModal: true }); + const wrapper = shallow(); + + expect(wrapper.find(SchemaAddFieldModal)).toHaveLength(1); + }); + + it('gets search results when filters changed', () => { + const wrapper = shallow(); + const input = wrapper.find(EuiFieldSearch); + input.simulate('change', { target: { value: 'Query' } }); + + expect(setFilterValue).toHaveBeenCalledWith('Query'); + }); + + it('renders IndexingStatus (org)', () => { + setMockValues({ ...mockValues, mostRecentIndexJob }); + const wrapper = shallow(); + + expect(wrapper.find(IndexingStatus)).toHaveLength(1); + expect(wrapper.find(IndexingStatus).prop('statusPath')).toEqual( + '/api/workplace_search/org/sources/123/reindex_job/123/status' + ); + }); + + it('renders IndexingStatus (account)', () => { + setMockValues({ ...mockValues, mostRecentIndexJob, isOrganization: false }); + const wrapper = shallow(); + + expect(wrapper.find(IndexingStatus)).toHaveLength(1); + expect(wrapper.find(IndexingStatus).prop('statusPath')).toEqual( + '/api/workplace_search/account/sources/123/reindex_job/123/status' + ); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.test.tsx new file mode 100644 index 000000000000..35a086369f39 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_change_errors.test.tsx @@ -0,0 +1,36 @@ +/* + * 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 '../../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; +import { useParams } from 'react-router-dom'; + +import { SchemaErrorsAccordion } from '../../../../../shared/schema/schema_errors_accordion'; + +import { SchemaChangeErrors } from './schema_change_errors'; + +describe('SchemaChangeErrors', () => { + const fieldCoercionErrors = [] as any; + const serverSchema = { + foo: 'string', + }; + it('renders', () => { + setMockValues({ fieldCoercionErrors, serverSchema }); + setMockActions({ initializeSchemaFieldErrors: jest.fn() }); + + (useParams as jest.Mock).mockImplementationOnce(() => ({ + activeReindexJobId: '1', + sourceId: '123', + })); + const wrapper = shallow(); + + expect(wrapper.find(SchemaErrorsAccordion)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.test.tsx new file mode 100644 index 000000000000..cf16a04d92ec --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.test.tsx @@ -0,0 +1,41 @@ +/* + * 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 '../../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { SchemaExistingField } from '../../../../../shared/schema/schema_existing_field'; + +import { SchemaFieldsTable } from './schema_fields_table'; + +describe('SchemaFieldsTable', () => { + const filterValue = ''; + const filteredSchemaFields = { + foo: 'string', + }; + + beforeEach(() => { + setMockActions({ updateExistingFieldType: jest.fn() }); + }); + + it('renders', () => { + setMockValues({ filterValue, filteredSchemaFields }); + const wrapper = shallow(); + + expect(wrapper.find(SchemaExistingField)).toHaveLength(1); + }); + + it('handles no results', () => { + setMockValues({ filterValue, filteredSchemaFields: {} }); + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="NoResultsMessage"]')).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx index 8f697b2b5c35..1670e2128c0a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_fields_table.tsx @@ -66,7 +66,7 @@ export const SchemaFieldsTable: React.FC = () => { ) : ( -

+

{i18n.translate( 'xpack.enterpriseSearch.workplaceSearch.contentSource.schema.filter.noResults.message', { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.test.ts new file mode 100644 index 000000000000..2c3aa6114c7d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.test.ts @@ -0,0 +1,470 @@ +/* + * 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 { + LogicMounter, + mockFlashMessageHelpers, + mockHttpValues, + expectedAsyncError, +} from '../../../../../__mocks__'; + +const contentSource = { id: 'source123' }; +jest.mock('../../source_logic', () => ({ + SourceLogic: { values: { contentSource } }, +})); + +import { AppLogic } from '../../../../app_logic'; +jest.mock('../../../../app_logic', () => ({ + AppLogic: { values: { isOrganization: true } }, +})); + +const spyScrollTo = jest.fn(); +Object.defineProperty(global.window, 'scrollTo', { value: spyScrollTo }); + +import { mostRecentIndexJob } from '../../../../__mocks__/content_sources.mock'; +import { TEXT } from '../../../../../shared/constants/field_types'; +import { ADD, UPDATE } from '../../../../../shared/constants/operations'; + +import { + SCHEMA_FIELD_ERRORS_ERROR_MESSAGE, + SCHEMA_FIELD_ADDED_MESSAGE, + SCHEMA_UPDATED_MESSAGE, +} from './constants'; + +import { SchemaLogic, dataTypeOptions } from './schema_logic'; + +describe('SchemaLogic', () => { + const { http } = mockHttpValues; + const { clearFlashMessages, flashAPIErrors, setSuccessMessage } = mockFlashMessageHelpers; + const { mount } = new LogicMounter(SchemaLogic); + + const defaultValues = { + sourceId: '', + activeSchema: {}, + serverSchema: {}, + filterValue: '', + filteredSchemaFields: {}, + dataTypeOptions, + showAddFieldModal: false, + addFieldFormErrors: null, + mostRecentIndexJob: {}, + fieldCoercionErrors: {}, + newFieldType: TEXT, + rawFieldName: '', + formUnchanged: true, + dataLoading: true, + }; + + const schema = { + foo: 'text', + } as any; + + const fieldCoercionErrors = [ + { + external_id: '123', + error: 'error', + }, + ] as any; + + const errors = ['this is an error']; + + const serverResponse = { + schema, + sourceId: contentSource.id, + mostRecentIndexJob, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mount(); + }); + + it('has expected default values', () => { + expect(SchemaLogic.values).toEqual(defaultValues); + }); + + describe('actions', () => { + it('onInitializeSchema', () => { + SchemaLogic.actions.onInitializeSchema(serverResponse); + + expect(SchemaLogic.values.sourceId).toEqual(contentSource.id); + expect(SchemaLogic.values.activeSchema).toEqual(schema); + expect(SchemaLogic.values.serverSchema).toEqual(schema); + expect(SchemaLogic.values.mostRecentIndexJob).toEqual(mostRecentIndexJob); + expect(SchemaLogic.values.dataLoading).toEqual(false); + }); + + it('onInitializeSchemaFieldErrors', () => { + SchemaLogic.actions.onInitializeSchemaFieldErrors({ fieldCoercionErrors }); + + expect(SchemaLogic.values.fieldCoercionErrors).toEqual(fieldCoercionErrors); + }); + it('onSchemaSetSuccess', () => { + SchemaLogic.actions.onSchemaSetSuccess({ + schema, + mostRecentIndexJob, + }); + + expect(SchemaLogic.values.activeSchema).toEqual(schema); + expect(SchemaLogic.values.serverSchema).toEqual(schema); + expect(SchemaLogic.values.mostRecentIndexJob).toEqual(mostRecentIndexJob); + expect(SchemaLogic.values.newFieldType).toEqual(TEXT); + expect(SchemaLogic.values.addFieldFormErrors).toEqual(null); + expect(SchemaLogic.values.formUnchanged).toEqual(true); + expect(SchemaLogic.values.showAddFieldModal).toEqual(false); + expect(SchemaLogic.values.dataLoading).toEqual(false); + expect(SchemaLogic.values.rawFieldName).toEqual(''); + }); + + it('onSchemaSetFormErrors', () => { + SchemaLogic.actions.onSchemaSetFormErrors(errors); + + expect(SchemaLogic.values.addFieldFormErrors).toEqual(errors); + }); + + it('updateNewFieldType', () => { + const NUMBER = 'number'; + SchemaLogic.actions.updateNewFieldType(NUMBER); + + expect(SchemaLogic.values.newFieldType).toEqual(NUMBER); + }); + + it('onFieldUpdate', () => { + SchemaLogic.actions.onFieldUpdate({ schema, formUnchanged: false }); + + expect(SchemaLogic.values.activeSchema).toEqual(schema); + expect(SchemaLogic.values.formUnchanged).toEqual(false); + }); + + it('onIndexingComplete', () => { + SchemaLogic.actions.onIndexingComplete(1); + + expect(SchemaLogic.values.mostRecentIndexJob).toEqual({ + ...mostRecentIndexJob, + activeReindexJobId: undefined, + percentageComplete: 100, + hasErrors: true, + isActive: false, + }); + }); + + it('resetMostRecentIndexJob', () => { + SchemaLogic.actions.resetMostRecentIndexJob(mostRecentIndexJob); + + expect(SchemaLogic.values.mostRecentIndexJob).toEqual(mostRecentIndexJob); + }); + + it('setFieldName', () => { + const NAME = 'name'; + SchemaLogic.actions.setFieldName(NAME); + + expect(SchemaLogic.values.rawFieldName).toEqual(NAME); + }); + + it('setFilterValue', () => { + const VALUE = 'string'; + SchemaLogic.actions.setFilterValue(VALUE); + + expect(SchemaLogic.values.filterValue).toEqual(VALUE); + }); + + it('openAddFieldModal', () => { + SchemaLogic.actions.openAddFieldModal(); + + expect(SchemaLogic.values.showAddFieldModal).toEqual(true); + }); + + it('closeAddFieldModal', () => { + SchemaLogic.actions.onSchemaSetFormErrors(errors); + SchemaLogic.actions.openAddFieldModal(); + SchemaLogic.actions.closeAddFieldModal(); + + expect(SchemaLogic.values.showAddFieldModal).toEqual(false); + expect(SchemaLogic.values.addFieldFormErrors).toEqual(null); + }); + + it('resetSchemaState', () => { + SchemaLogic.actions.resetSchemaState(); + + expect(SchemaLogic.values.dataLoading).toEqual(true); + expect(clearFlashMessages).toHaveBeenCalled(); + }); + }); + + describe('listeners', () => { + describe('initializeSchema', () => { + it('calls API and sets values (org)', async () => { + const onInitializeSchemaSpy = jest.spyOn(SchemaLogic.actions, 'onInitializeSchema'); + const promise = Promise.resolve(serverResponse); + http.get.mockReturnValue(promise); + SchemaLogic.actions.initializeSchema(); + + expect(http.get).toHaveBeenCalledWith( + '/api/workplace_search/org/sources/source123/schemas' + ); + await promise; + expect(onInitializeSchemaSpy).toHaveBeenCalledWith(serverResponse); + }); + + it('calls API and sets values (account)', async () => { + AppLogic.values.isOrganization = false; + + const onInitializeSchemaSpy = jest.spyOn(SchemaLogic.actions, 'onInitializeSchema'); + const promise = Promise.resolve(serverResponse); + http.get.mockReturnValue(promise); + SchemaLogic.actions.initializeSchema(); + + expect(http.get).toHaveBeenCalledWith( + '/api/workplace_search/account/sources/source123/schemas' + ); + await promise; + expect(onInitializeSchemaSpy).toHaveBeenCalledWith(serverResponse); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + http.get.mockReturnValue(promise); + SchemaLogic.actions.initializeSchema(); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + }); + }); + + describe('initializeSchemaFieldErrors', () => { + it('calls API and sets values (org)', async () => { + AppLogic.values.isOrganization = true; + const onInitializeSchemaFieldErrorsSpy = jest.spyOn( + SchemaLogic.actions, + 'onInitializeSchemaFieldErrors' + ); + const initPromise = Promise.resolve(serverResponse); + const promise = Promise.resolve({ fieldCoercionErrors }); + http.get.mockReturnValue(initPromise); + http.get.mockReturnValue(promise); + SchemaLogic.actions.initializeSchemaFieldErrors( + mostRecentIndexJob.activeReindexJobId, + contentSource.id + ); + + expect(http.get).toHaveBeenCalledWith( + '/api/workplace_search/org/sources/source123/schemas' + ); + + await initPromise; + expect(http.get).toHaveBeenCalledWith( + '/api/workplace_search/org/sources/source123/reindex_job/123' + ); + + await promise; + expect(onInitializeSchemaFieldErrorsSpy).toHaveBeenCalledWith({ + fieldCoercionErrors, + }); + }); + + it('calls API and sets values (account)', async () => { + AppLogic.values.isOrganization = false; + + const onInitializeSchemaFieldErrorsSpy = jest.spyOn( + SchemaLogic.actions, + 'onInitializeSchemaFieldErrors' + ); + const initPromise = Promise.resolve(serverResponse); + const promise = Promise.resolve({ fieldCoercionErrors }); + http.get.mockReturnValue(initPromise); + http.get.mockReturnValue(promise); + SchemaLogic.actions.initializeSchemaFieldErrors( + mostRecentIndexJob.activeReindexJobId, + contentSource.id + ); + + expect(http.get).toHaveBeenCalledWith( + '/api/workplace_search/account/sources/source123/schemas' + ); + + await initPromise; + expect(http.get).toHaveBeenCalledWith( + '/api/workplace_search/account/sources/source123/reindex_job/123' + ); + + await promise; + expect(onInitializeSchemaFieldErrorsSpy).toHaveBeenCalledWith({ + fieldCoercionErrors, + }); + }); + + it('handles error', async () => { + const promise = Promise.reject({ error: 'this is an error' }); + http.get.mockReturnValue(promise); + SchemaLogic.actions.initializeSchemaFieldErrors( + mostRecentIndexJob.activeReindexJobId, + contentSource.id + ); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith({ + error: 'this is an error', + message: SCHEMA_FIELD_ERRORS_ERROR_MESSAGE, + }); + }); + }); + + it('addNewField', () => { + const setServerFieldSpy = jest.spyOn(SchemaLogic.actions, 'setServerField'); + SchemaLogic.actions.onInitializeSchema(serverResponse); + const newSchema = { + ...schema, + bar: 'number', + }; + SchemaLogic.actions.addNewField('bar', 'number'); + + expect(setServerFieldSpy).toHaveBeenCalledWith(newSchema, ADD); + }); + + it('updateExistingFieldType', () => { + const onFieldUpdateSpy = jest.spyOn(SchemaLogic.actions, 'onFieldUpdate'); + SchemaLogic.actions.onInitializeSchema(serverResponse); + const newSchema = { + foo: 'number', + }; + SchemaLogic.actions.updateExistingFieldType('foo', 'number'); + + expect(onFieldUpdateSpy).toHaveBeenCalledWith({ schema: newSchema, formUnchanged: false }); + }); + + it('updateFields', () => { + const setServerFieldSpy = jest.spyOn(SchemaLogic.actions, 'setServerField'); + SchemaLogic.actions.onInitializeSchema(serverResponse); + SchemaLogic.actions.updateFields(); + + expect(setServerFieldSpy).toHaveBeenCalledWith(schema, UPDATE); + }); + + describe('setServerField', () => { + beforeEach(() => { + SchemaLogic.actions.onInitializeSchema(serverResponse); + }); + + describe('adding a field', () => { + it('calls API and sets values (org)', async () => { + AppLogic.values.isOrganization = true; + const onSchemaSetSuccessSpy = jest.spyOn(SchemaLogic.actions, 'onSchemaSetSuccess'); + const promise = Promise.resolve(serverResponse); + http.post.mockReturnValue(promise); + SchemaLogic.actions.setServerField(schema, ADD); + + expect(http.post).toHaveBeenCalledWith( + '/api/workplace_search/org/sources/source123/schemas', + { + body: JSON.stringify({ ...schema }), + } + ); + await promise; + expect(setSuccessMessage).toHaveBeenCalledWith(SCHEMA_FIELD_ADDED_MESSAGE); + expect(onSchemaSetSuccessSpy).toHaveBeenCalledWith(serverResponse); + }); + + it('calls API and sets values (account)', async () => { + AppLogic.values.isOrganization = false; + + const onSchemaSetSuccessSpy = jest.spyOn(SchemaLogic.actions, 'onSchemaSetSuccess'); + const promise = Promise.resolve(serverResponse); + http.post.mockReturnValue(promise); + SchemaLogic.actions.setServerField(schema, ADD); + + expect(http.post).toHaveBeenCalledWith( + '/api/workplace_search/account/sources/source123/schemas', + { + body: JSON.stringify({ ...schema }), + } + ); + await promise; + expect(onSchemaSetSuccessSpy).toHaveBeenCalledWith(serverResponse); + }); + + it('handles error', async () => { + const onSchemaSetFormErrorsSpy = jest.spyOn(SchemaLogic.actions, 'onSchemaSetFormErrors'); + const promise = Promise.reject({ message: 'this is an error' }); + http.post.mockReturnValue(promise); + SchemaLogic.actions.setServerField(schema, ADD); + await expectedAsyncError(promise); + + expect(onSchemaSetFormErrorsSpy).toHaveBeenCalledWith('this is an error'); + }); + }); + + describe('updating a field', () => { + it('calls API and sets values (org)', async () => { + AppLogic.values.isOrganization = true; + const onSchemaSetSuccessSpy = jest.spyOn(SchemaLogic.actions, 'onSchemaSetSuccess'); + const promise = Promise.resolve(serverResponse); + http.post.mockReturnValue(promise); + SchemaLogic.actions.setServerField(schema, UPDATE); + + expect(http.post).toHaveBeenCalledWith( + '/api/workplace_search/org/sources/source123/schemas', + { + body: JSON.stringify({ ...schema }), + } + ); + await promise; + expect(setSuccessMessage).toHaveBeenCalledWith(SCHEMA_UPDATED_MESSAGE); + expect(onSchemaSetSuccessSpy).toHaveBeenCalledWith(serverResponse); + }); + + it('calls API and sets values (account)', async () => { + AppLogic.values.isOrganization = false; + + const onSchemaSetSuccessSpy = jest.spyOn(SchemaLogic.actions, 'onSchemaSetSuccess'); + const promise = Promise.resolve(serverResponse); + http.post.mockReturnValue(promise); + SchemaLogic.actions.setServerField(schema, UPDATE); + + expect(http.post).toHaveBeenCalledWith( + '/api/workplace_search/account/sources/source123/schemas', + { + body: JSON.stringify({ ...schema }), + } + ); + await promise; + expect(onSchemaSetSuccessSpy).toHaveBeenCalledWith(serverResponse); + }); + + it('handles error', async () => { + const promise = Promise.reject('this is an error'); + http.post.mockReturnValue(promise); + SchemaLogic.actions.setServerField(schema, UPDATE); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith('this is an error'); + }); + }); + }); + }); + + describe('selectors', () => { + describe('filteredSchemaFields', () => { + it('handles empty response', () => { + SchemaLogic.actions.onInitializeSchema(serverResponse); + SchemaLogic.actions.setFilterValue('baz'); + + expect(SchemaLogic.values.filteredSchemaFields).toEqual({}); + }); + + it('handles filtered response', () => { + const newSchema = { + ...schema, + bar: 'number', + }; + SchemaLogic.actions.onInitializeSchema(serverResponse); + SchemaLogic.actions.onFieldUpdate({ schema: newSchema, formUnchanged: false }); + SchemaLogic.actions.setFilterValue('foo'); + + expect(SchemaLogic.values.filteredSchemaFields).toEqual(schema); + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts index 36eb3fc67b2c..9a9435a07e24 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/schema/schema_logic.ts @@ -17,7 +17,7 @@ import { OptionValue } from '../../../../types'; import { flashAPIErrors, setSuccessMessage, - FlashMessagesLogic, + clearFlashMessages, } from '../../../../../shared/flash_messages'; import { AppLogic } from '../../../../app_logic'; @@ -46,7 +46,6 @@ interface SchemaActions { }): { schema: Schema; formUnchanged: boolean }; onIndexingComplete(numDocumentsWithErrors: number): number; resetMostRecentIndexJob(emptyReindexJob: IndexJob): IndexJob; - showFieldSuccess(successMessage: string): string; setFieldName(rawFieldName: string): string; setFilterValue(filterValue: string): string; addNewField( @@ -111,7 +110,7 @@ interface SchemaChangeErrorsProps { fieldCoercionErrors: FieldCoercionErrors; } -const dataTypeOptions = [ +export const dataTypeOptions = [ { value: 'text', text: 'Text' }, { value: 'date', text: 'Date' }, { value: 'number', text: 'Number' }, @@ -132,7 +131,6 @@ export const SchemaLogic = kea>({ }), onIndexingComplete: (numDocumentsWithErrors: number) => numDocumentsWithErrors, resetMostRecentIndexJob: (emptyReindexJob: IndexJob) => emptyReindexJob, - showFieldSuccess: (successMessage: string) => successMessage, setFieldName: (rawFieldName: string) => rawFieldName, setFilterValue: (filterValue: string) => filterValue, openAddFieldModal: () => true, @@ -326,7 +324,7 @@ export const SchemaLogic = kea>({ const emptyReindexJob = { percentageComplete: 100, numDocumentsWithErrors: 0, - activeReindexJobId: 0, + activeReindexJobId: '', isActive: false, }; @@ -348,10 +346,10 @@ export const SchemaLogic = kea>({ } }, resetMostRecentIndexJob: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, resetSchemaState: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_added.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_added.test.tsx new file mode 100644 index 000000000000..d29995484540 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_added.test.tsx @@ -0,0 +1,54 @@ +/* + * 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 '../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions, mockFlashMessageHelpers } from '../../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { Redirect, useLocation } from 'react-router-dom'; + +import { SourceAdded } from './source_added'; + +describe('SourceAdded', () => { + const { setErrorMessage } = mockFlashMessageHelpers; + const setAddedSource = jest.fn(); + + beforeEach(() => { + setMockActions({ setAddedSource }); + setMockValues({ isOrganization: true }); + }); + + it('renders', () => { + const search = '?name=foo&serviceType=custom&indexPermissions=false'; + (useLocation as jest.Mock).mockImplementationOnce(() => ({ search })); + const wrapper = shallow(); + + expect(wrapper.find(Redirect)).toHaveLength(1); + expect(setAddedSource).toHaveBeenCalled(); + }); + + describe('hasError', () => { + it('passes default error to server', () => { + const search = '?name=foo&hasError=true&serviceType=custom&indexPermissions=false'; + (useLocation as jest.Mock).mockImplementationOnce(() => ({ search })); + shallow(); + + expect(setErrorMessage).toHaveBeenCalledWith('foo failed to connect.'); + }); + + it('passes custom error to server', () => { + const search = + '?name=foo&hasError=true&serviceType=custom&indexPermissions=false&errorMessages[]=custom error'; + (useLocation as jest.Mock).mockImplementationOnce(() => ({ search })); + shallow(); + + expect(setErrorMessage).toHaveBeenCalledWith('custom error'); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.test.tsx new file mode 100644 index 000000000000..c445a7aec04f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.test.tsx @@ -0,0 +1,186 @@ +/* + * 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 '../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { + EuiTable, + EuiButton, + EuiButtonEmpty, + EuiEmptyPrompt, + EuiFieldSearch, + EuiLink, +} from '@elastic/eui'; + +import { mockMeta } from '../../../__mocks__/meta.mock'; +import { fullContentSources } from '../../../__mocks__/content_sources.mock'; + +import { DEFAULT_META } from '../../../../shared/constants'; +import { ComponentLoader } from '../../../components/shared/component_loader'; +import { Loading } from '../../../../../applications/shared/loading'; +import { TablePaginationBar } from '../../../components/shared/table_pagination_bar'; + +import { SourceContent } from './source_content'; + +describe('SourceContent', () => { + const setActivePage = jest.fn(); + const searchContentSourceDocuments = jest.fn(); + const resetSourceState = jest.fn(); + const setContentFilterValue = jest.fn(); + + const mockValues = { + contentSource: fullContentSources[0], + contentMeta: mockMeta, + contentItems: [ + { + id: '1234', + last_updated: '2021-01-21', + }, + { + id: '1235', + last_updated: '2021-01-20', + }, + ], + contentFilterValue: '', + dataLoading: false, + sectionLoading: false, + isOrganization: true, + }; + + beforeEach(() => { + setMockActions({ + setActivePage, + searchContentSourceDocuments, + resetSourceState, + setContentFilterValue, + }); + setMockValues({ ...mockValues }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiTable)).toHaveLength(1); + }); + + it('returns Loading when loading', () => { + setMockValues({ ...mockValues, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('returns ComponentLoader when section loading', () => { + setMockValues({ ...mockValues, sectionLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(ComponentLoader)).toHaveLength(1); + }); + + describe('empty states', () => { + beforeEach(() => { + setMockValues({ ...mockValues, contentMeta: DEFAULT_META }); + }); + it('renders', () => { + setMockValues({ ...mockValues, contentMeta: DEFAULT_META }); + const wrapper = shallow(); + + expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1); + expect(wrapper.find(EuiEmptyPrompt).prop('body')).toBeTruthy(); + expect(wrapper.find(EuiEmptyPrompt).prop('title')).toEqual( +

This source doesn't have any content yet

+ ); + }); + + it('shows custom source docs link', () => { + setMockValues({ + ...mockValues, + contentMeta: DEFAULT_META, + contentSource: { + ...fullContentSources[0], + serviceType: 'google', + }, + }); + const wrapper = shallow(); + + expect(wrapper.find(EuiEmptyPrompt).prop('body')).toBeNull(); + }); + + it('shows correct message when filter value set', () => { + setMockValues({ ...mockValues, contentMeta: DEFAULT_META, contentFilterValue: 'Elastic' }); + const wrapper = shallow(); + + expect(wrapper.find(EuiEmptyPrompt).prop('title')).toEqual( +

No results for 'Elastic'

+ ); + }); + }); + + it('handles page change', () => { + const wrapper = shallow(); + const tablePager = wrapper.find(TablePaginationBar).first(); + tablePager.prop('onChangePage')(3); + + expect(setActivePage).toHaveBeenCalledWith(4); + }); + + it('clears filter value when reset', () => { + setMockValues({ + ...mockValues, + contentSource: { + ...fullContentSources[0], + isFederatedSource: true, + }, + }); + const wrapper = shallow(); + const button = wrapper.find(EuiButtonEmpty); + button.simulate('click'); + + expect(setContentFilterValue).toHaveBeenCalledWith(''); + }); + + it('sets filter value', () => { + setMockValues({ + ...mockValues, + contentSource: { + ...fullContentSources[0], + isFederatedSource: true, + }, + }); + const wrapper = shallow(); + const input = wrapper.find(EuiFieldSearch); + input.simulate('change', { target: { value: 'Query' } }); + const button = wrapper.find(EuiButton); + button.simulate('click'); + + expect(setContentFilterValue).toHaveBeenCalledWith(''); + }); + + describe('URL field link', () => { + it('does not render link when not linkable', () => { + setMockValues({ + ...mockValues, + contentSource: fullContentSources[1], + }); + const wrapper = shallow(); + const fieldCell = wrapper.find('[data-test-subj="URLFieldCell"]'); + + expect(fieldCell.find(EuiLink)).toHaveLength(0); + }); + + it('renders links when linkable', () => { + const wrapper = shallow(); + const fieldCell = wrapper.find('[data-test-subj="URLFieldCell"]'); + + expect(fieldCell.find(EuiLink)).toHaveLength(2); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx index 8d9636ec38e1..728d21eb1530 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.tsx @@ -122,7 +122,7 @@ export const SourceContent: React.FC = () => { - + {!urlFieldIsLinkable && ( )} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_info_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_info_card.test.tsx new file mode 100644 index 000000000000..0a01fecfc91b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_info_card.test.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 React from 'react'; +import { shallow } from 'enzyme'; + +import { EuiBadge, EuiHealth, EuiText, EuiTitle } from '@elastic/eui'; + +import { SourceIcon } from '../../../components/shared/source_icon'; + +import { SourceInfoCard } from './source_info_card'; + +describe('SourceInfoCard', () => { + const props = { + sourceName: 'source', + sourceType: 'custom', + dateCreated: '2021-01-20', + isFederatedSource: true, + }; + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(SourceIcon)).toHaveLength(1); + expect(wrapper.find(EuiBadge)).toHaveLength(1); + expect(wrapper.find(EuiHealth)).toHaveLength(1); + expect(wrapper.find(EuiText)).toHaveLength(3); + expect(wrapper.find(EuiTitle)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.test.tsx new file mode 100644 index 000000000000..11e74d8246a4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.test.tsx @@ -0,0 +1,108 @@ +/* + * 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 '../../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { EuiConfirmModal } from '@elastic/eui'; + +import { fullContentSources, sourceConfigData } from '../../../__mocks__/content_sources.mock'; + +import { SourceConfigFields } from '../../../components/shared/source_config_fields'; + +import { SourceSettings } from './source_settings'; + +describe('SourceSettings', () => { + const updateContentSource = jest.fn(); + const removeContentSource = jest.fn(); + const resetSourceState = jest.fn(); + const getSourceConfigData = jest.fn(); + const contentSource = fullContentSources[0]; + const buttonLoading = false; + const isOrganization = true; + + const mockValues = { + contentSource, + buttonLoading, + sourceConfigData, + isOrganization, + }; + + beforeEach(() => { + setMockValues({ ...mockValues }); + setMockActions({ + updateContentSource, + removeContentSource, + resetSourceState, + getSourceConfigData, + }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find('form')).toHaveLength(1); + }); + + it('handles form submission', () => { + const wrapper = shallow(); + + const TEXT = 'name'; + const input = wrapper.find('[data-test-subj="SourceNameInput"]'); + input.simulate('change', { target: { value: TEXT } }); + + const preventDefault = jest.fn(); + wrapper.find('form').simulate('submit', { preventDefault }); + + expect(preventDefault).toHaveBeenCalled(); + expect(updateContentSource).toHaveBeenCalledWith(fullContentSources[0].id, { name: TEXT }); + }); + + it('handles confirmModal submission', () => { + const wrapper = shallow(); + wrapper.find('[data-test-subj="DeleteSourceButton"]').simulate('click'); + + const modal = wrapper.find(EuiConfirmModal); + modal.prop('onConfirm')!({} as any); + modal.prop('onCancel')!({} as any); + + expect(removeContentSource).toHaveBeenCalled(); + }); + + it('falls back when no configured fields sent', () => { + setMockValues({ ...mockValues, sourceConfigData: {} }); + const wrapper = shallow(); + + expect(wrapper.find('form')).toHaveLength(1); + }); + + it('falls back when no consumerKey field sent', () => { + setMockValues({ ...mockValues, sourceConfigData: { configuredFields: { clientId: '123' } } }); + const wrapper = shallow(); + + expect(wrapper.find(SourceConfigFields).prop('consumerKey')).toBeUndefined(); + }); + + it('handles public key use case', () => { + setMockValues({ + ...mockValues, + contentSource: { + ...fullContentSources[0], + serviceType: 'confluence_server', + }, + }); + + const wrapper = shallow(); + + expect(wrapper.find(SourceConfigFields).prop('publicKey')).toEqual( + sourceConfigData.configuredFields.publicKey + ); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx index 8ca31d184501..8d3219be9b02 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_settings.tsx @@ -6,10 +6,9 @@ import React, { useEffect, useState, ChangeEvent, FormEvent } from 'react'; -import { History } from 'history'; import { useActions, useValues } from 'kea'; import { isEmpty } from 'lodash'; -import { Link, useHistory } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import { EuiButton, @@ -22,8 +21,6 @@ import { EuiFormRow, } from '@elastic/eui'; -import { SOURCES_PATH, getSourcesPath } from '../../../routes'; - import { ContentSection } from '../../../components/shared/content_section'; import { SourceConfigFields } from '../../../components/shared/source_config_fields'; import { ViewContentHeader } from '../../../components/shared/view_content_header'; @@ -35,7 +32,6 @@ import { staticSourceData } from '../source_data'; import { SourceLogic } from '../source_logic'; export const SourceSettings: React.FC = () => { - const history = useHistory() as History; const { updateContentSource, removeContentSource, @@ -83,8 +79,7 @@ export const SourceSettings: React.FC = () => { * modal here and set the button that was clicked to delete to a loading state. */ setModalVisibility(false); - const onSourceRemoved = () => history.push(getSourcesPath(SOURCES_PATH, isOrganization)); - removeContentSource(id, onSourceRemoved); + removeContentSource(id); }; const confirmModal = ( diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.test.tsx new file mode 100644 index 000000000000..a90002e5d553 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_sub_nav.test.tsx @@ -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 { setMockValues } from '../../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { CUSTOM_SERVICE_TYPE } from '../../../constants'; +import { SourceSubNav } from './source_sub_nav'; + +import { SideNavLink } from '../../../../shared/layout'; + +describe('SourceSubNav', () => { + it('renders empty when no group id present', () => { + setMockValues({ contentSource: {} }); + const wrapper = shallow(); + + expect(wrapper.find(SideNavLink)).toHaveLength(0); + }); + + it('renders nav items', () => { + setMockValues({ contentSource: { id: '1' } }); + const wrapper = shallow(); + + expect(wrapper.find(SideNavLink)).toHaveLength(3); + }); + + it('renders custom source nav items', () => { + setMockValues({ contentSource: { id: '1', serviceType: CUSTOM_SERVICE_TYPE } }); + const wrapper = shallow(); + + expect(wrapper.find(SideNavLink)).toHaveLength(5); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.test.tsx new file mode 100644 index 000000000000..1050150028ae --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.test.tsx @@ -0,0 +1,63 @@ +/* + * 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 '../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../__mocks__'; + +import { shallow } from 'enzyme'; + +import React from 'react'; +import { Redirect } from 'react-router-dom'; + +import { contentSources } from '../../__mocks__/content_sources.mock'; + +import { Loading } from '../../../shared/loading'; +import { SourcesTable } from '../../components/shared/sources_table'; +import { ViewContentHeader } from '../../components/shared/view_content_header'; + +import { ADD_SOURCE_PATH, getSourcesPath } from '../../routes'; + +import { OrganizationSources } from './organization_sources'; + +describe('OrganizationSources', () => { + const initializeSources = jest.fn(); + const setSourceSearchability = jest.fn(); + + const mockValues = { + contentSources, + dataLoading: false, + }; + + beforeEach(() => { + setMockActions({ + initializeSources, + setSourceSearchability, + }); + setMockValues({ ...mockValues }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(SourcesTable)).toHaveLength(1); + expect(wrapper.find(ViewContentHeader)).toHaveLength(1); + }); + + it('returns loading when loading', () => { + setMockValues({ ...mockValues, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('returns redirect when no sources', () => { + setMockValues({ ...mockValues, contentSources: [] }); + const wrapper = shallow(); + + expect(wrapper.find(Redirect).prop('to')).toEqual(getSourcesPath(ADD_SOURCE_PATH, true)); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx index 880df3d086cc..fdb536dd7977 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/organization_sources.tsx @@ -27,10 +27,11 @@ const ORG_HEADER_DESCRIPTION = 'Organization sources are available to the entire organization and can be assigned to specific user groups.'; export const OrganizationSources: React.FC = () => { - const { initializeSources, setSourceSearchability } = useActions(SourcesLogic); + const { initializeSources, setSourceSearchability, resetSourcesState } = useActions(SourcesLogic); useEffect(() => { initializeSources(); + return resetSourcesState; }, []); const { dataLoading, contentSources } = useValues(SourcesLogic); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts index 0891687d425f..2de70009c56a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts @@ -15,12 +15,12 @@ import { flashAPIErrors, setSuccessMessage, setQueuedSuccessMessage, - FlashMessagesLogic, + clearFlashMessages, } from '../../../shared/flash_messages'; import { DEFAULT_META } from '../../../shared/constants'; import { AppLogic } from '../../app_logic'; -import { NOT_FOUND_PATH } from '../../routes'; +import { NOT_FOUND_PATH, SOURCES_PATH, getSourcesPath } from '../../routes'; import { ContentSourceFullData, Meta, DocumentSummaryItem, SourceContentItem } from '../../types'; export interface SourceActions { @@ -38,11 +38,8 @@ export interface SourceActions { source: { name: string } ): { sourceId: string; source: { name: string } }; resetSourceState(): void; - removeContentSource( - sourceId: string, - successCallback: () => void - ): { sourceId: string; successCallback(): void }; - initializeSource(sourceId: string, history: object): { sourceId: string; history: object }; + removeContentSource(sourceId: string): { sourceId: string }; + initializeSource(sourceId: string): { sourceId: string }; getSourceConfigData(serviceType: string): { serviceType: string }; setButtonNotLoading(): void; } @@ -91,13 +88,12 @@ export const SourceLogic = kea>({ setSearchResults: (searchResultsResponse: SearchResultsResponse) => searchResultsResponse, setContentFilterValue: (contentFilterValue: string) => contentFilterValue, setActivePage: (activePage: number) => activePage, - initializeSource: (sourceId: string, history: object) => ({ sourceId, history }), + initializeSource: (sourceId: string) => ({ sourceId }), initializeFederatedSummary: (sourceId: string) => ({ sourceId }), searchContentSourceDocuments: (sourceId: string) => ({ sourceId }), updateContentSource: (sourceId: string, source: { name: string }) => ({ sourceId, source }), - removeContentSource: (sourceId: string, successCallback: () => void) => ({ + removeContentSource: (sourceId: string) => ({ sourceId, - successCallback, }), getSourceConfigData: (serviceType: string) => ({ serviceType }), resetSourceState: () => true, @@ -245,8 +241,8 @@ export const SourceLogic = kea>({ flashAPIErrors(e); } }, - removeContentSource: async ({ sourceId, successCallback }) => { - FlashMessagesLogic.actions.clearFlashMessages(); + removeContentSource: async ({ sourceId }) => { + clearFlashMessages(); const { isOrganization } = AppLogic.values; const route = isOrganization ? `/api/workplace_search/org/sources/${sourceId}` @@ -263,7 +259,7 @@ export const SourceLogic = kea>({ } ) ); - successCallback(); + KibanaLogic.values.navigateToUrl(getSourcesPath(SOURCES_PATH, isOrganization)); } catch (e) { flashAPIErrors(e); } finally { @@ -292,7 +288,7 @@ export const SourceLogic = kea>({ ); }, resetSourceState: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.test.tsx new file mode 100644 index 000000000000..ac542f57b8fd --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.test.tsx @@ -0,0 +1,120 @@ +/* + * 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 '../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; +import { useParams } from 'react-router-dom'; + +import { Route, Switch } from 'react-router-dom'; + +import { contentSources } from '../../__mocks__/content_sources.mock'; + +import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; + +import { NAV } from '../../constants'; + +import { Loading } from '../../../shared/loading'; + +import { DisplaySettingsRouter } from './components/display_settings'; +import { Overview } from './components/overview'; +import { Schema } from './components/schema'; +import { SchemaChangeErrors } from './components/schema/schema_change_errors'; +import { SourceContent } from './components/source_content'; +import { SourceSettings } from './components/source_settings'; + +import { SourceRouter } from './source_router'; + +describe('SourceRouter', () => { + const initializeSource = jest.fn(); + const contentSource = contentSources[1]; + const customSource = contentSources[0]; + const mockValues = { + contentSource, + dataLoading: false, + }; + + beforeEach(() => { + setMockActions({ + initializeSource, + }); + setMockValues({ ...mockValues }); + (useParams as jest.Mock).mockImplementationOnce(() => ({ + sourceId: '1', + })); + }); + + it('returns Loading when loading', () => { + setMockValues({ ...mockValues, dataLoading: true }); + const wrapper = shallow(); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('renders source routes (standard)', () => { + const wrapper = shallow(); + + expect(wrapper.find(Overview)).toHaveLength(1); + expect(wrapper.find(SourceSettings)).toHaveLength(1); + expect(wrapper.find(SourceContent)).toHaveLength(1); + expect(wrapper.find(Switch)).toHaveLength(1); + expect(wrapper.find(Route)).toHaveLength(3); + }); + + it('renders source routes (custom)', () => { + setMockValues({ ...mockValues, contentSource: customSource }); + const wrapper = shallow(); + + expect(wrapper.find(DisplaySettingsRouter)).toHaveLength(1); + expect(wrapper.find(Schema)).toHaveLength(1); + expect(wrapper.find(SchemaChangeErrors)).toHaveLength(1); + expect(wrapper.find(Route)).toHaveLength(6); + }); + + it('handles breadcrumbs while loading (standard)', () => { + setMockValues({ + ...mockValues, + contentSource: {}, + }); + + const loadingBreadcrumbs = ['Sources', '...']; + + const wrapper = shallow(); + + const overviewBreadCrumb = wrapper.find(SetPageChrome).at(0); + const contentBreadCrumb = wrapper.find(SetPageChrome).at(1); + const settingsBreadCrumb = wrapper.find(SetPageChrome).at(2); + + expect(overviewBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.OVERVIEW]); + expect(contentBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.CONTENT]); + expect(settingsBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SETTINGS]); + }); + + it('handles breadcrumbs while loading (custom)', () => { + setMockValues({ + ...mockValues, + contentSource: { serviceType: 'custom' }, + }); + + const loadingBreadcrumbs = ['Sources', '...']; + + const wrapper = shallow(); + + const schemaBreadCrumb = wrapper.find(SetPageChrome).at(2); + const schemaErrorsBreadCrumb = wrapper.find(SetPageChrome).at(3); + const displaySettingsBreadCrumb = wrapper.find(SetPageChrome).at(4); + + expect(schemaBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SCHEMA]); + expect(schemaErrorsBreadCrumb.prop('trail')).toEqual([...loadingBreadcrumbs, NAV.SCHEMA]); + expect(displaySettingsBreadCrumb.prop('trail')).toEqual([ + ...loadingBreadcrumbs, + NAV.DISPLAY_SETTINGS, + ]); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx index 089ef0cd46a0..f46743778a16 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_router.tsx @@ -6,10 +6,9 @@ import React, { useEffect } from 'react'; -import { History } from 'history'; import { useActions, useValues } from 'kea'; import moment from 'moment'; -import { Route, Switch, useHistory, useParams } from 'react-router-dom'; +import { Route, Switch, useParams } from 'react-router-dom'; import { EuiButton, EuiCallOut, EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; @@ -46,14 +45,13 @@ import { SourceInfoCard } from './components/source_info_card'; import { SourceSettings } from './components/source_settings'; export const SourceRouter: React.FC = () => { - const history = useHistory() as History; const { sourceId } = useParams() as { sourceId: string }; const { initializeSource } = useActions(SourceLogic); const { contentSource, dataLoading } = useValues(SourceLogic); const { isOrganization } = useValues(AppLogic); useEffect(() => { - initializeSource(sourceId, history); + initializeSource(sourceId); }, []); if (dataLoading) return ; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts index cb8df1d31219..0a3d047796f4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts @@ -15,7 +15,7 @@ import { HttpLogic } from '../../../shared/http'; import { flashAPIErrors, setQueuedSuccessMessage, - FlashMessagesLogic, + clearFlashMessages, } from '../../../shared/flash_messages'; import { Connector, ContentSourceDetails, ContentSourceStatus, SourceDataItem } from '../../types'; @@ -77,6 +77,9 @@ interface ISourcesServerResponse { serviceTypes: Connector[]; } +let pollingInterval: number; +const POLLING_INTERVAL = 10000; + export const SourcesLogic = kea>({ path: ['enterprise_search', 'workplace_search', 'sources_logic'], actions: { @@ -169,6 +172,7 @@ export const SourcesLogic = kea>( try { const response = await HttpLogic.values.http.get(route); + actions.pollForSourceStatusChanges(); actions.onInitializeSources(response); } catch (e) { flashAPIErrors(e); @@ -181,18 +185,20 @@ export const SourcesLogic = kea>( } }, // We poll the server and if the status update, we trigger a new fetch of the sources. - pollForSourceStatusChanges: async () => { + pollForSourceStatusChanges: () => { const { isOrganization } = AppLogic.values; if (!isOrganization) return; const serverStatuses = values.serverStatuses; - const sourceStatuses = await fetchSourceStatuses(isOrganization); + pollingInterval = window.setInterval(async () => { + const sourceStatuses = await fetchSourceStatuses(isOrganization); - sourceStatuses.some((source: ContentSourceStatus) => { - if (serverStatuses && serverStatuses[source.id] !== source.status.status) { - return actions.initializeSources(); - } - }); + sourceStatuses.some((source: ContentSourceStatus) => { + if (serverStatuses && serverStatuses[source.id] !== source.status.status) { + return actions.initializeSources(); + } + }); + }, POLLING_INTERVAL); }, setSourceSearchability: async ({ sourceId, searchable }) => { const { isOrganization } = AppLogic.values; @@ -233,7 +239,15 @@ export const SourcesLogic = kea>( ); }, resetFlashMessages: () => { - FlashMessagesLogic.actions.clearFlashMessages(); + clearFlashMessages(); + }, + resetSourcesState: () => { + clearInterval(pollingInterval); + }, + }), + events: () => ({ + beforeUnmount() { + clearInterval(pollingInterval); }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.test.tsx new file mode 100644 index 000000000000..7580203e759a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_router.test.tsx @@ -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 '../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../__mocks__'; + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { Route, Switch, Redirect } from 'react-router-dom'; + +import { ADD_SOURCE_PATH, PERSONAL_SOURCES_PATH, SOURCES_PATH, getSourcesPath } from '../../routes'; + +import { SourcesRouter } from './sources_router'; + +describe('SourcesRouter', () => { + const resetSourcesState = jest.fn(); + const mockValues = { + account: { canCreatePersonalSources: true }, + isOrganization: true, + hasPlatinumLicense: true, + }; + + beforeEach(() => { + setMockActions({ + resetSourcesState, + }); + setMockValues({ ...mockValues }); + }); + + it('renders sources routes', () => { + const TOTAL_ROUTES = 62; + const wrapper = shallow(); + + expect(wrapper.find(Switch)).toHaveLength(1); + expect(wrapper.find(Route)).toHaveLength(TOTAL_ROUTES); + }); + + it('redirects when nonplatinum license and accountOnly context', () => { + setMockValues({ ...mockValues, hasPlatinumLicense: false }); + const wrapper = shallow(); + + expect(wrapper.find(Redirect).first().prop('from')).toEqual(ADD_SOURCE_PATH); + expect(wrapper.find(Redirect).first().prop('to')).toEqual(SOURCES_PATH); + }); + + it('redirects when cannot create sources', () => { + setMockValues({ ...mockValues, account: { canCreatePersonalSources: false } }); + const wrapper = shallow(); + + expect(wrapper.find(Redirect).last().prop('from')).toEqual( + getSourcesPath(ADD_SOURCE_PATH, false) + ); + expect(wrapper.find(Redirect).last().prop('to')).toEqual(PERSONAL_SOURCES_PATH); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.test.tsx new file mode 100644 index 000000000000..7deb87f4311a --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.test.tsx @@ -0,0 +1,64 @@ +/* + * 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 '../../../__mocks__/shallow_useeffect.mock'; + +import { setMockValues, setMockActions } from '../../../__mocks__'; + +import { shallow } from 'enzyme'; + +import React from 'react'; + +import { EuiModal } from '@elastic/eui'; + +import { Loading } from '../../../shared/loading'; + +import { SourcesView } from './sources_view'; + +describe('SourcesView', () => { + const resetPermissionsModal = jest.fn(); + const permissionsModal = { + addedSourceName: 'mySource', + serviceType: 'jira', + additionalConfiguration: true, + }; + + const mockValues = { + permissionsModal, + dataLoading: false, + }; + + const children =

test

; + + beforeEach(() => { + setMockActions({ + resetPermissionsModal, + }); + setMockValues({ ...mockValues }); + }); + + it('renders', () => { + const wrapper = shallow({children}); + + expect(wrapper.find('PermissionsModal')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="TestChildren"]')).toHaveLength(1); + }); + + it('returns loading when loading', () => { + setMockValues({ ...mockValues, dataLoading: true }); + const wrapper = shallow({children}); + + expect(wrapper.find(Loading)).toHaveLength(1); + }); + + it('calls function on modal close', () => { + const wrapper = shallow({children}); + const modal = wrapper.find('PermissionsModal').dive().find(EuiModal); + modal.prop('onClose')(); + + expect(resetPermissionsModal).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.tsx index 7485f986076d..9e6c8f5b7319 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_view.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { useActions, useValues } from 'kea'; @@ -22,8 +22,6 @@ import { EuiText, } from '@elastic/eui'; -import { FlashMessagesLogic } from '../../../shared/flash_messages'; - import { Loading } from '../../../shared/loading'; import { SourceIcon } from '../../components/shared/source_icon'; @@ -31,29 +29,14 @@ import { EXTERNAL_IDENTITIES_DOCS_URL, DOCUMENT_PERMISSIONS_DOCS_URL } from '../ import { SourcesLogic } from './sources_logic'; -const POLLING_INTERVAL = 10000; - interface SourcesViewProps { children: React.ReactNode; } export const SourcesView: React.FC = ({ children }) => { - const { initializeSources, pollForSourceStatusChanges, resetPermissionsModal } = useActions( - SourcesLogic - ); - + const { resetPermissionsModal } = useActions(SourcesLogic); const { dataLoading, permissionsModal } = useValues(SourcesLogic); - useEffect(() => { - initializeSources(); - const pollingInterval = window.setInterval(pollForSourceStatusChanges, POLLING_INTERVAL); - - return () => { - FlashMessagesLogic.actions.clearFlashMessages(); - clearInterval(pollingInterval); - }; - }, []); - if (dataLoading) return ; const PermissionsModal = ({ @@ -113,7 +96,7 @@ export const SourcesView: React.FC = ({ children }) => { return ( <> - {!!permissionsModal && permissionsModal.additionalConfiguration && ( + {permissionsModal?.additionalConfiguration && ( { ); }); - it('correctly encodes paths and query string parameters', async () => { + it('correctly encodes query string parameters', async () => { const requestHandler = enterpriseSearchRequestHandler.createRequest({ - path: '/api/some example', + path: '/api/example', }); await makeAPICall(requestHandler, { query: { 'page[current]': 1 } }); EnterpriseSearchAPI.shouldHaveBeenCalledWith( - 'http://localhost:3002/api/some%20example?page%5Bcurrent%5D=1' + 'http://localhost:3002/api/example?page%5Bcurrent%5D=1' ); }); + + describe('encodePathParams', () => { + it('correctly replaces :pathVariables with request.params', async () => { + const requestHandler = enterpriseSearchRequestHandler.createRequest({ + path: '/api/examples/:example/some/:id', + }); + await makeAPICall(requestHandler, { params: { example: 'hello', id: 'world' } }); + + EnterpriseSearchAPI.shouldHaveBeenCalledWith( + 'http://localhost:3002/api/examples/hello/some/world' + ); + }); + + it('correctly encodes path params as URI components', async () => { + const requestHandler = enterpriseSearchRequestHandler.createRequest({ + path: '/api/examples/:example', + }); + await makeAPICall(requestHandler, { params: { example: 'hello#@/$%^/&[]{}/";world' } }); + + EnterpriseSearchAPI.shouldHaveBeenCalledWith( + 'http://localhost:3002/api/examples/hello%23%40%2F%24%25%5E%2F%26%5B%5D%7B%7D%2F%22%3Bworld' + ); + }); + }); }); describe('response passing', () => { diff --git a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts index 8e4a817a8255..d337854ffc2c 100644 --- a/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts +++ b/x-pack/plugins/enterprise_search/server/lib/enterprise_search_request_handler.ts @@ -20,10 +20,10 @@ interface ConstructorDependencies { config: ConfigType; log: Logger; } -interface RequestParams { +interface RequestParams { path: string; params?: object; - hasValidData?: (body?: ResponseBody) => boolean; + hasValidData?: Function; } interface ErrorResponse { message: string; @@ -32,7 +32,7 @@ interface ErrorResponse { }; } export interface IEnterpriseSearchRequestHandler { - createRequest(requestParams?: object): RequestHandler; + createRequest(requestParams?: RequestParams): RequestHandler; } /** @@ -53,11 +53,7 @@ export class EnterpriseSearchRequestHandler { this.enterpriseSearchUrl = config.host as string; } - createRequest({ - path, - params = {}, - hasValidData = () => true, - }: RequestParams) { + createRequest({ path, params = {}, hasValidData = () => true }: RequestParams) { return async ( _context: RequestHandlerContext, request: KibanaRequest, @@ -65,11 +61,12 @@ export class EnterpriseSearchRequestHandler { ) => { try { // Set up API URL + const encodedPath = this.encodePathParams(path, request.params as Record); const queryParams = { ...(request.query as object), ...params }; const queryString = !this.isEmptyObj(queryParams) ? `?${querystring.stringify(queryParams)}` : ''; - const url = encodeURI(this.enterpriseSearchUrl + path) + queryString; + const url = encodeURI(this.enterpriseSearchUrl) + encodedPath + queryString; // Set up API options const { method } = request.route; @@ -126,6 +123,36 @@ export class EnterpriseSearchRequestHandler { }; } + /** + * This path helper is similar to React Router's generatePath, but much simpler & + * does not use regexes. It enables us to pass a static '/foo/:bar/baz' string to + * createRequest({ path }) and have :bar be automatically replaced by the value of + * request.params.bar. + * It also (very importantly) wraps all URL request params with encodeURIComponent(), + * which is an extra layer of encoding required by the Enterprise Search server in + * order to correctly & safely parse user-generated IDs with special characters in + * their names - just encodeURI alone won't work. + */ + encodePathParams(path: string, params: Record) { + const hasParams = path.includes(':'); + if (!hasParams) { + return path; + } else { + return path + .split('/') + .map((pathPart) => { + const isParam = pathPart.startsWith(':'); + if (!isParam) { + return pathPart; + } else { + const pathParam = pathPart.replace(':', ''); + return encodeURIComponent(params[pathParam]); + } + }) + .join('/'); + } + } + /** * Attempt to grab a usable error body from Enterprise Search - this isn't * always possible because some of our internal endpoints send back blank diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/analytics.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/analytics.test.ts index 9ede6989052b..f93b205059b2 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/analytics.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/analytics.test.ts @@ -27,12 +27,8 @@ describe('analytics routes', () => { }); it('creates a request handler', () => { - mockRouter.callRoute({ - params: { engineName: 'some-engine' }, - }); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/as/engines/some-engine/analytics/queries', + path: '/as/engines/:engineName/analytics/queries', }); }); @@ -84,12 +80,8 @@ describe('analytics routes', () => { }); it('creates a request handler', () => { - mockRouter.callRoute({ - params: { engineName: 'some-engine', query: 'some-query' }, - }); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/as/engines/some-engine/analytics/query/some-query', + path: '/as/engines/:engineName/analytics/query/:query', }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/analytics.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/analytics.ts index f7d0786b27fd..9807ca9ad791 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/analytics.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/analytics.ts @@ -32,13 +32,9 @@ export function registerAnalyticsRoutes({ query: schema.object(queriesSchema), }, }, - async (context, request, response) => { - const { engineName } = request.params; - - return enterpriseSearchRequestHandler.createRequest({ - path: `/as/engines/${engineName}/analytics/queries`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/as/engines/:engineName/analytics/queries', + }) ); router.get( @@ -52,12 +48,8 @@ export function registerAnalyticsRoutes({ query: schema.object(querySchema), }, }, - async (context, request, response) => { - const { engineName, query } = request.params; - - return enterpriseSearchRequestHandler.createRequest({ - path: `/as/engines/${engineName}/analytics/query/${query}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/as/engines/:engineName/analytics/query/:query', + }) ); } diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts index af498e346529..f6bcd4adda1f 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.test.ts @@ -200,16 +200,8 @@ describe('credentials routes', () => { }); it('creates a request to enterprise search', () => { - const mockRequest = { - params: { - name: 'abc123', - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/as/credentials/abc123', + path: '/as/credentials/:name', }); }); @@ -311,7 +303,6 @@ describe('credentials routes', () => { mockRouter = new MockRouter({ method: 'delete', path: '/api/app_search/credentials/{name}', - payload: 'params', }); registerCredentialsRoutes({ @@ -321,16 +312,8 @@ describe('credentials routes', () => { }); it('creates a request to enterprise search', () => { - const mockRequest = { - params: { - name: 'abc123', - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/as/credentials/abc123', + path: '/as/credentials/:name', }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts index a5611af9bba7..29425eedef69 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/credentials.ts @@ -81,11 +81,9 @@ export function registerCredentialsRoutes({ body: tokenSchema, }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/as/credentials/${request.params.name}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/as/credentials/:name', + }) ); router.delete( { @@ -96,10 +94,8 @@ export function registerCredentialsRoutes({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/as/credentials/${request.params.name}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/as/credentials/:name', + }) ); } diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/documents.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/documents.test.ts index 5f57db40cd7e..c12a2e69057d 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/documents.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/documents.test.ts @@ -27,13 +27,8 @@ describe('documents routes', () => { }); it('creates a request to enterprise search', () => { - mockRouter.callRoute({ - params: { engineName: 'some-engine' }, - body: { documents: [{ foo: 'bar' }] }, - }); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/as/engines/some-engine/documents/new', + path: '/as/engines/:engineName/documents/new', }); }); @@ -79,10 +74,8 @@ describe('document routes', () => { }); it('creates a request to enterprise search', () => { - mockRouter.callRoute({ params: { engineName: 'some-engine', documentId: '1' } }); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/as/engines/some-engine/documents/1', + path: '/as/engines/:engineName/documents/:documentId', }); }); }); @@ -104,10 +97,8 @@ describe('document routes', () => { }); it('creates a request to enterprise search', () => { - mockRouter.callRoute({ params: { engineName: 'some-engine', documentId: '1' } }); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/as/engines/some-engine/documents/1', + path: '/as/engines/:engineName/documents/:documentId', }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/documents.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/documents.ts index 60cd64b32479..665691c3a9ea 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/documents.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/documents.ts @@ -24,11 +24,9 @@ export function registerDocumentsRoutes({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/as/engines/${request.params.engineName}/documents/new`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: `/as/engines/:engineName/documents/new`, + }) ); } @@ -46,11 +44,9 @@ export function registerDocumentRoutes({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/as/engines/${request.params.engineName}/documents/${request.params.documentId}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: `/as/engines/:engineName/documents/:documentId`, + }) ); router.delete( { @@ -62,10 +58,8 @@ export function registerDocumentRoutes({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/as/engines/${request.params.engineName}/documents/${request.params.documentId}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: `/as/engines/:engineName/documents/:documentId`, + }) ); } diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts index ed6847a02910..9755fff02f73 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts @@ -59,6 +59,7 @@ describe('engine routes', () => { describe('hasValidData', () => { it('should correctly validate that the response has data', () => { + mockRequestHandler.createRequest.mockClear(); const response = { meta: { page: { @@ -73,6 +74,7 @@ describe('engine routes', () => { }); it('should correctly validate that a response does not have data', () => { + mockRequestHandler.createRequest.mockClear(); const response = {}; mockRouter.callRoute(mockRequest); @@ -125,10 +127,8 @@ describe('engine routes', () => { }); it('creates a request to enterprise search', () => { - mockRouter.callRoute({ params: { name: 'some-engine' } }); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/as/engines/some-engine/details', + path: '/as/engines/:name/details', }); }); }); @@ -150,10 +150,8 @@ describe('engine routes', () => { }); it('creates a request to enterprise search', () => { - mockRouter.callRoute({ params: { name: 'some-engine' } }); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/as/engines/some-engine/overview_metrics', + path: '/as/engines/:name/overview_metrics', }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts index f9169d8795f4..c0bbc40ff8d2 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts @@ -54,11 +54,9 @@ export function registerEnginesRoutes({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/as/engines/${request.params.name}/details`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: `/as/engines/:name/details`, + }) ); router.get( { @@ -69,10 +67,8 @@ export function registerEnginesRoutes({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/as/engines/${request.params.name}/overview_metrics`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: `/as/engines/:name/overview_metrics`, + }) ); } diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/settings.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/settings.test.ts index be3b2632eb67..613ecee90d98 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/settings.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/settings.test.ts @@ -26,8 +26,6 @@ describe('log settings routes', () => { }); it('creates a request to enterprise search', () => { - mockRouter.callRoute({}); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/as/log_settings', }); @@ -52,7 +50,6 @@ describe('log settings routes', () => { }); it('creates a request to enterprise search', () => { - mockRouter.callRoute({}); expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/as/log_settings', }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/settings.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/settings.ts index f9684cdbc060..bec56c9e3de0 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/settings.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/settings.ts @@ -40,10 +40,8 @@ export function registerSettingsRoutes({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/as/log_settings', - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/as/log_settings', + }) ); } diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.test.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.test.ts index 2f244022be03..d7938e6eb738 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.test.ts @@ -22,9 +22,6 @@ describe('groups routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/groups', @@ -35,7 +32,9 @@ describe('groups routes', () => { ...mockDependencies, router: mockRouter.router, }); + }); + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/org/groups', }); @@ -60,16 +59,19 @@ describe('groups routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - body: { - group_name: 'group', - }, - }; - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/org/groups', - ...mockRequest, + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + group_name: 'group', + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -92,24 +94,8 @@ describe('groups routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - body: { - page: { - current: 1, - size: 1, - }, - search: { - query: 'foo', - content_source_ids: ['123', '234'], - user_ids: ['345', '456'], - }, - }, - }; - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/org/groups/search', - ...mockRequest, }); }); @@ -150,30 +136,20 @@ describe('groups routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/groups/{id}', - payload: 'params', }); registerGroupRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/groups/123', + path: '/ws/org/groups/:id', }); }); }); @@ -183,15 +159,6 @@ describe('groups routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - const mockPayload = { - group: { - name: 'group', - }, - }; - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'put', path: '/api/workplace_search/groups/{id}', @@ -202,19 +169,24 @@ describe('groups routes', () => { ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - body: mockPayload, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/groups/123', - body: mockPayload, + path: '/ws/org/groups/:id', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + group: { + name: 'group', + }, + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -227,7 +199,6 @@ describe('groups routes', () => { mockRouter = new MockRouter({ method: 'delete', path: '/api/workplace_search/groups/{id}', - payload: 'params', }); registerGroupRoute({ @@ -237,16 +208,8 @@ describe('groups routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/groups/123', + path: '/ws/org/groups/:id', }); }); }); @@ -256,30 +219,20 @@ describe('groups routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/groups/{id}/group_users', - payload: 'params', }); registerGroupUsersRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/groups/123/group_users', + path: '/ws/org/groups/:id/group_users', }); }); }); @@ -302,18 +255,20 @@ describe('groups routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { id: '123' }, - body: { - content_source_ids: ['123', '234'], - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/groups/123/share', - body: mockRequest.body, + path: '/ws/org/groups/:id/share', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + params: { id: '123' }, + body: { + content_source_ids: ['123', '234'], + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -336,18 +291,20 @@ describe('groups routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { id: '123' }, - body: { - user_ids: ['123', '234'], - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/groups/123/assign', - body: mockRequest.body, + path: '/ws/org/groups/:id/assign', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + params: { id: '123' }, + body: { + user_ids: ['123', '234'], + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -357,15 +314,6 @@ describe('groups routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - const mockPayload = { - group: { - content_source_boosts: [['boost'], ['boost2', 'boost3']], - }, - }; - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'put', path: '/api/workplace_search/groups/{id}/boosts', @@ -376,19 +324,22 @@ describe('groups routes', () => { ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - body: mockPayload, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/groups/123/update_source_boosts', - body: mockPayload, + path: '/ws/org/groups/:id/update_source_boosts', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + content_source_boosts: [['boost'], ['boost2', 'boost3']], + }, + }; + mockRouter.shouldValidate(request); }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.ts index ed75b0d6a91c..4c3e8fa87fe2 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/groups.ts @@ -28,12 +28,9 @@ export function registerGroupsRoute({ router, enterpriseSearchRequestHandler }: }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/org/groups', - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/groups', + }) ); } @@ -58,12 +55,9 @@ export function registerSearchGroupsRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/org/groups/search', - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/groups/search', + }) ); } @@ -77,11 +71,9 @@ export function registerGroupRoute({ router, enterpriseSearchRequestHandler }: R }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/groups/${request.params.id}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/groups/:id', + }) ); router.put( @@ -98,12 +90,9 @@ export function registerGroupRoute({ router, enterpriseSearchRequestHandler }: R }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/groups/${request.params.id}`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/groups/:id', + }) ); router.delete( @@ -115,11 +104,9 @@ export function registerGroupRoute({ router, enterpriseSearchRequestHandler }: R }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/groups/${request.params.id}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/groups/:id', + }) ); } @@ -136,11 +123,9 @@ export function registerGroupUsersRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/groups/${request.params.id}/group_users`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/groups/:id/group_users', + }) ); } @@ -160,12 +145,9 @@ export function registerShareGroupRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/groups/${request.params.id}/share`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/groups/:id/share', + }) ); } @@ -185,12 +167,9 @@ export function registerAssignGroupRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/groups/${request.params.id}/assign`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/groups/:id/assign', + }) ); } @@ -212,12 +191,9 @@ export function registerBoostsGroupRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/groups/${request.params.id}/update_source_boosts`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/groups/:id/update_source_boosts', + }) ); } diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/settings.test.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/settings.test.ts index 932bf5e3685e..db21d9ae7824 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/settings.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/settings.test.ts @@ -18,22 +18,18 @@ describe('settings routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/org/settings', - payload: 'params', }); registerOrgSettingsRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - mockRouter.callRoute({}); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/org/settings', }); @@ -45,9 +41,6 @@ describe('settings routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'put', path: '/api/workplace_search/org/settings/customize', @@ -58,18 +51,18 @@ describe('settings routes', () => { ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - body: { - name: 'foo', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/org/settings/customize', - body: mockRequest.body, + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { body: { name: 'foo' } }; + mockRouter.shouldValidate(request); }); }); }); @@ -79,9 +72,6 @@ describe('settings routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'put', path: '/api/workplace_search/org/settings/oauth_application', @@ -92,22 +82,26 @@ describe('settings routes', () => { ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - body: { - oauth_application: { - name: 'foo', - confidential: true, - redirect_uri: 'http://foo.bar', - }, - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/org/settings/oauth_application', - body: mockRequest.body, + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + oauth_application: { + name: 'foo', + confidential: true, + redirect_uri: 'http://foo.bar', + }, + }, + }; + mockRouter.shouldValidate(request); }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/settings.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/settings.ts index cdba6609eb87..c05acff45040 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/settings.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/settings.ts @@ -17,11 +17,9 @@ export function registerOrgSettingsRoute({ path: '/api/workplace_search/org/settings', validate: false, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/org/settings', - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/settings', + }) ); } @@ -38,12 +36,9 @@ export function registerOrgSettingsCustomizeRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/org/settings/customize', - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/settings/customize', + }) ); } @@ -64,12 +59,9 @@ export function registerOrgSettingsOauthApplicationRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/org/settings/oauth_application', - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/settings/oauth_application', + }) ); } diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.test.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.test.ts index d97a587e57ff..9625d20d6a3c 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.test.ts @@ -57,22 +57,18 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/account/sources', - payload: 'params', }); registerAccountSourcesRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - mockRouter.callRoute({}); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/sources', }); @@ -84,22 +80,18 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/account/sources/status', - payload: 'params', }); registerAccountSourcesStatusRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - mockRouter.callRoute({}); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/sources/status', }); @@ -111,30 +103,20 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/account/sources/{id}', - payload: 'params', }); registerAccountSourceRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123', + path: '/ws/sources/:id', }); }); }); @@ -147,7 +129,6 @@ describe('sources routes', () => { mockRouter = new MockRouter({ method: 'delete', path: '/api/workplace_search/account/sources/{id}', - payload: 'params', }); registerAccountSourceRoute({ @@ -157,16 +138,8 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123', + path: '/ws/sources/:id', }); }); }); @@ -189,22 +162,24 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - body: { - service_type: 'google', - name: 'Google', - login: 'user', - password: 'changeme', - organizations: 'swiftype', - indexPermissions: true, - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/sources/form_create', - body: mockRequest.body, + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + service_type: 'google', + name: 'Google', + login: 'user', + password: 'changeme', + organizations: ['swiftype'], + indexPermissions: true, + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -227,24 +202,25 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { id: '123' }, - body: { - query: 'foo', - page: { - current: 1, - size: 10, - total_pages: 1, - total_results: 10, - }, - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123/documents', - body: mockRequest.body, + path: '/ws/sources/:id/documents', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + query: 'foo', + page: { + current: 1, + size: 10, + total_pages: 1, + total_results: 10, + }, + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -254,30 +230,20 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/account/sources/{id}/federated_summary', - payload: 'params', }); registerAccountSourceFederatedSummaryRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123/federated_summary', + path: '/ws/sources/:id/federated_summary', }); }); }); @@ -287,30 +253,20 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/account/sources/{id}/reauth_prepare', - payload: 'params', }); registerAccountSourceReauthPrepareRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123/reauth_prepare', + path: '/ws/sources/:id/reauth_prepare', }); }); }); @@ -333,20 +289,21 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { id: '123' }, - body: { - content_source: { - name: 'foo', - }, - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123/settings', - body: mockRequest.body, + path: '/ws/sources/:id/settings', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + content_source: { + name: 'foo', + }, + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -356,63 +313,43 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/account/pre_sources/{id}', - payload: 'params', }); registerAccountPreSourceRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/pre_content_sources/123', + path: '/ws/pre_content_sources/:id', }); }); }); - describe('GET /api/workplace_search/account/sources/{service_type}/prepare', () => { + describe('GET /api/workplace_search/account/sources/{serviceType}/prepare', () => { let mockRouter: MockRouter; beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', - path: '/api/workplace_search/account/sources/{service_type}/prepare', - payload: 'params', + path: '/api/workplace_search/account/sources/{serviceType}/prepare', }); registerAccountPrepareSourcesRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - service_type: 'zendesk', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/zendesk/prepare', + path: '/ws/sources/:serviceType/prepare', }); }); }); @@ -422,9 +359,6 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'put', path: '/api/workplace_search/account/sources/{id}/searchable', @@ -435,21 +369,22 @@ describe('sources routes', () => { ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - body: { - searchable: true, - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123/searchable', - body: mockRequest.body, + path: '/ws/sources/:id/searchable', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + searchable: true, + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -459,30 +394,20 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/account/sources/{id}/display_settings/config', - payload: 'params', }); registerAccountSourceDisplaySettingsConfig({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123/display_settings/config', + path: '/ws/sources/:id/display_settings/config', }); }); }); @@ -505,26 +430,28 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { id: '123' }, - body: { - titleField: 'foo', - subtitleField: 'bar', - descriptionField: 'this is a thing', - urlField: 'http://youknowfor.search', - color: '#aaa', - detailFields: { - fieldName: 'myField', - label: 'My Field', - }, - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123/display_settings/config', - body: mockRequest.body, + path: '/ws/sources/:id/display_settings/config', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + titleField: 'foo', + subtitleField: 'bar', + descriptionField: 'this is a thing', + urlField: 'http://youknowfor.search', + urlFieldIsLinkable: true, + color: '#aaa', + detailFields: { + fieldName: 'myField', + label: 'My Field', + }, + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -534,30 +461,20 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/account/sources/{id}/schemas', - payload: 'params', }); registerAccountSourceSchemasRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123/schemas', + path: '/ws/sources/:id/schemas', }); }); }); @@ -580,84 +497,70 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { id: '123' }, - body: {}, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123/schemas', - body: mockRequest.body, + path: '/ws/sources/:id/schemas', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { body: { someSchemaKey: 'text' } }; + mockRouter.shouldValidate(request); }); }); }); - describe('GET /api/workplace_search/account/sources/{source_id}/reindex_job/{job_id}', () => { + describe('GET /api/workplace_search/account/sources/{sourceId}/reindex_job/{jobId}', () => { let mockRouter: MockRouter; beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', - path: '/api/workplace_search/account/sources/{source_id}/reindex_job/{job_id}', - payload: 'params', + path: '/api/workplace_search/account/sources/{sourceId}/reindex_job/{jobId}', }); registerAccountSourceReindexJobRoute({ ...mockDependencies, router: mockRouter.router, }); + }); + it('creates a request handler', () => { const mockRequest = { params: { - source_id: '123', - job_id: '345', + sourceId: '123', + jobId: '345', }, }; mockRouter.callRoute(mockRequest); expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123/reindex_job/345', + path: '/ws/sources/:sourceId/reindex_job/:jobId', }); }); }); - describe('GET /api/workplace_search/account/sources/{source_id}/reindex_job/{job_id}/status', () => { + describe('GET /api/workplace_search/account/sources/{sourceId}/reindex_job/{jobId}/status', () => { let mockRouter: MockRouter; beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', - path: '/api/workplace_search/account/sources/{source_id}/reindex_job/{job_id}/status', - payload: 'params', + path: '/api/workplace_search/account/sources/{sourceId}/reindex_job/{jobId}/status', }); registerAccountSourceReindexJobStatusRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - source_id: '123', - job_id: '345', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/sources/123/reindex_job/345/status', + path: '/ws/sources/:sourceId/reindex_job/:jobId/status', }); }); }); @@ -667,22 +570,18 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/org/sources', - payload: 'params', }); registerOrgSourcesRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - mockRouter.callRoute({}); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/org/sources', }); @@ -694,22 +593,18 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/org/sources/status', - payload: 'params', }); registerOrgSourcesStatusRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - mockRouter.callRoute({}); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/org/sources/status', }); @@ -721,30 +616,20 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/org/sources/{id}', - payload: 'params', }); registerOrgSourceRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123', + path: '/ws/org/sources/:id', }); }); }); @@ -757,7 +642,6 @@ describe('sources routes', () => { mockRouter = new MockRouter({ method: 'delete', path: '/api/workplace_search/org/sources/{id}', - payload: 'params', }); registerOrgSourceRoute({ @@ -767,16 +651,8 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123', + path: '/ws/org/sources/:id', }); }); }); @@ -799,22 +675,24 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - body: { - service_type: 'google', - name: 'Google', - login: 'user', - password: 'changeme', - organizations: 'swiftype', - indexPermissions: true, - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/org/sources/form_create', - body: mockRequest.body, + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + service_type: 'google', + name: 'Google', + login: 'user', + password: 'changeme', + organizations: ['swiftype'], + indexPermissions: true, + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -837,24 +715,25 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { id: '123' }, - body: { - query: 'foo', - page: { - current: 1, - size: 10, - total_pages: 1, - total_results: 10, - }, - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123/documents', - body: mockRequest.body, + path: '/ws/org/sources/:id/documents', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + query: 'foo', + page: { + current: 1, + size: 10, + total_pages: 1, + total_results: 10, + }, + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -864,30 +743,20 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/org/sources/{id}/federated_summary', - payload: 'params', }); registerOrgSourceFederatedSummaryRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123/federated_summary', + path: '/ws/org/sources/:id/federated_summary', }); }); }); @@ -897,30 +766,20 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/org/sources/{id}/reauth_prepare', - payload: 'params', }); registerOrgSourceReauthPrepareRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123/reauth_prepare', + path: '/ws/org/sources/:id/reauth_prepare', }); }); }); @@ -943,20 +802,21 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { id: '123' }, - body: { - content_source: { - name: 'foo', - }, - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123/settings', - body: mockRequest.body, + path: '/ws/org/sources/:id/settings', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + content_source: { + name: 'foo', + }, + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -966,63 +826,43 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/org/pre_sources/{id}', - payload: 'params', }); registerOrgPreSourceRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/pre_content_sources/123', + path: '/ws/org/pre_content_sources/:id', }); }); }); - describe('GET /api/workplace_search/org/sources/{service_type}/prepare', () => { + describe('GET /api/workplace_search/org/sources/{serviceType}/prepare', () => { let mockRouter: MockRouter; beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', - path: '/api/workplace_search/org/sources/{service_type}/prepare', - payload: 'params', + path: '/api/workplace_search/org/sources/{serviceType}/prepare', }); registerOrgPrepareSourcesRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - service_type: 'zendesk', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/zendesk/prepare', + path: '/ws/org/sources/:serviceType/prepare', }); }); }); @@ -1032,9 +872,6 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'put', path: '/api/workplace_search/org/sources/{id}/searchable', @@ -1045,21 +882,22 @@ describe('sources routes', () => { ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - body: { - searchable: true, - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123/searchable', - body: mockRequest.body, + path: '/ws/org/sources/:id/searchable', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + searchable: true, + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -1069,30 +907,20 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/org/sources/{id}/display_settings/config', - payload: 'params', }); registerOrgSourceDisplaySettingsConfig({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123/display_settings/config', + path: '/ws/org/sources/:id/display_settings/config', }); }); }); @@ -1115,26 +943,28 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { id: '123' }, - body: { - titleField: 'foo', - subtitleField: 'bar', - descriptionField: 'this is a thing', - urlField: 'http://youknowfor.search', - color: '#aaa', - detailFields: { - fieldName: 'myField', - label: 'My Field', - }, - }, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123/display_settings/config', - body: mockRequest.body, + path: '/ws/org/sources/:id/display_settings/config', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { + body: { + titleField: 'foo', + subtitleField: 'bar', + descriptionField: 'this is a thing', + urlField: 'http://youknowfor.search', + urlFieldIsLinkable: true, + color: '#aaa', + detailFields: { + fieldName: 'myField', + label: 'My Field', + }, + }, + }; + mockRouter.shouldValidate(request); }); }); }); @@ -1144,30 +974,20 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/org/sources/{id}/schemas', - payload: 'params', }); registerOrgSourceSchemasRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - id: '123', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123/schemas', + path: '/ws/org/sources/:id/schemas', }); }); }); @@ -1190,84 +1010,61 @@ describe('sources routes', () => { }); it('creates a request handler', () => { - const mockRequest = { - params: { id: '123' }, - body: {}, - }; - - mockRouter.callRoute(mockRequest); - expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123/schemas', - body: mockRequest.body, + path: '/ws/org/sources/:id/schemas', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { body: { someSchemaKey: 'number' } }; + mockRouter.shouldValidate(request); }); }); }); - describe('GET /api/workplace_search/org/sources/{source_id}/reindex_job/{job_id}', () => { + describe('GET /api/workplace_search/org/sources/{sourceId}/reindex_job/{jobId}', () => { let mockRouter: MockRouter; beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', - path: '/api/workplace_search/org/sources/{source_id}/reindex_job/{job_id}', - payload: 'params', + path: '/api/workplace_search/org/sources/{sourceId}/reindex_job/{jobId}', }); registerOrgSourceReindexJobRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - source_id: '123', - job_id: '345', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123/reindex_job/345', + path: '/ws/org/sources/:sourceId/reindex_job/:jobId', }); }); }); - describe('GET /api/workplace_search/org/sources/{source_id}/reindex_job/{job_id}/status', () => { + describe('GET /api/workplace_search/org/sources/{sourceId}/reindex_job/{jobId}/status', () => { let mockRouter: MockRouter; beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', - path: '/api/workplace_search/org/sources/{source_id}/reindex_job/{job_id}/status', - payload: 'params', + path: '/api/workplace_search/org/sources/{sourceId}/reindex_job/{jobId}/status', }); registerOrgSourceReindexJobStatusRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - source_id: '123', - job_id: '345', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/sources/123/reindex_job/345/status', + path: '/ws/org/sources/:sourceId/reindex_job/:jobId/status', }); }); }); @@ -1277,9 +1074,6 @@ describe('sources routes', () => { beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', path: '/api/workplace_search/org/settings/connectors', @@ -1289,59 +1083,46 @@ describe('sources routes', () => { ...mockDependencies, router: mockRouter.router, }); + }); - mockRouter.callRoute({}); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ path: '/ws/org/settings/connectors', }); }); }); - describe('GET /api/workplace_search/org/settings/connectors/{service_type}', () => { + describe('GET /api/workplace_search/org/settings/connectors/{serviceType}', () => { let mockRouter: MockRouter; beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'get', - path: '/api/workplace_search/org/settings/connectors/{service_type}', - payload: 'params', + path: '/api/workplace_search/org/settings/connectors/{serviceType}', }); registerOrgSourceOauthConfigurationRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - service_type: 'zendesk', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/settings/connectors/zendesk', + path: '/ws/org/settings/connectors/:serviceType', }); }); }); - describe('POST /api/workplace_search/org/settings/connectors/{service_type}', () => { + describe('POST /api/workplace_search/org/settings/connectors/{serviceType}', () => { let mockRouter: MockRouter; beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'post', - path: '/api/workplace_search/org/settings/connectors/{service_type}', + path: '/api/workplace_search/org/settings/connectors/{serviceType}', payload: 'body', }); @@ -1349,34 +1130,30 @@ describe('sources routes', () => { ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - service_type: 'zendesk', - }, - body: mockConfig, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/settings/connectors/zendesk', - body: mockRequest.body, + path: '/ws/org/settings/connectors/:serviceType', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { body: mockConfig }; + mockRouter.shouldValidate(request); }); }); }); - describe('PUT /api/workplace_search/org/settings/connectors/{service_type}', () => { + describe('PUT /api/workplace_search/org/settings/connectors/{serviceType}', () => { let mockRouter: MockRouter; beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'put', - path: '/api/workplace_search/org/settings/connectors/{service_type}', + path: '/api/workplace_search/org/settings/connectors/{serviceType}', payload: 'body', }); @@ -1384,52 +1161,41 @@ describe('sources routes', () => { ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - service_type: 'zendesk', - }, - body: mockConfig, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/settings/connectors/zendesk', - body: mockRequest.body, + path: '/ws/org/settings/connectors/:serviceType', + }); + }); + + describe('validates', () => { + it('correctly', () => { + const request = { body: mockConfig }; + mockRouter.shouldValidate(request); }); }); }); - describe('DELETE /api/workplace_search/org/settings/connectors/{service_type}', () => { + describe('DELETE /api/workplace_search/org/settings/connectors/{serviceType}', () => { let mockRouter: MockRouter; beforeEach(() => { jest.clearAllMocks(); - }); - - it('creates a request handler', () => { mockRouter = new MockRouter({ method: 'delete', - path: '/api/workplace_search/org/settings/connectors/{service_type}', - payload: 'params', + path: '/api/workplace_search/org/settings/connectors/{serviceType}', }); registerOrgSourceOauthConfigurationRoute({ ...mockDependencies, router: mockRouter.router, }); + }); - const mockRequest = { - params: { - service_type: 'zendesk', - }, - }; - - mockRouter.callRoute(mockRequest); - + it('creates a request handler', () => { expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/ws/org/settings/connectors/zendesk', + path: '/ws/org/settings/connectors/:serviceType', }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts index 04db6bbc2912..a2f950a54471 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/sources.ts @@ -59,11 +59,9 @@ export function registerAccountSourcesRoute({ path: '/api/workplace_search/account/sources', validate: false, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/sources', - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources', + }) ); } @@ -76,11 +74,9 @@ export function registerAccountSourcesStatusRoute({ path: '/api/workplace_search/account/sources/status', validate: false, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/sources/status', - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/status', + }) ); } @@ -97,11 +93,9 @@ export function registerAccountSourceRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.id}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:id', + }) ); router.delete( @@ -113,11 +107,9 @@ export function registerAccountSourceRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.id}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:id', + }) ); } @@ -139,12 +131,9 @@ export function registerAccountCreateSourceRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/sources/form_create', - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/form_create', + }) ); } @@ -165,12 +154,9 @@ export function registerAccountSourceDocumentsRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.id}/documents`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:id/documents', + }) ); } @@ -187,11 +173,9 @@ export function registerAccountSourceFederatedSummaryRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.id}/federated_summary`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:id/federated_summary', + }) ); } @@ -208,11 +192,9 @@ export function registerAccountSourceReauthPrepareRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.id}/reauth_prepare`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:id/reauth_prepare', + }) ); } @@ -234,12 +216,9 @@ export function registerAccountSourceSettingsRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.id}/settings`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:id/settings', + }) ); } @@ -256,11 +235,9 @@ export function registerAccountPreSourceRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/pre_content_sources/${request.params.id}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/pre_content_sources/:id', + }) ); } @@ -270,18 +247,16 @@ export function registerAccountPrepareSourcesRoute({ }: RouteDependencies) { router.get( { - path: '/api/workplace_search/account/sources/{service_type}/prepare', + path: '/api/workplace_search/account/sources/{serviceType}/prepare', validate: { params: schema.object({ - service_type: schema.string(), + serviceType: schema.string(), }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.service_type}/prepare`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:serviceType/prepare', + }) ); } @@ -301,12 +276,9 @@ export function registerAccountSourceSearchableRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.id}/searchable`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:id/searchable', + }) ); } @@ -323,11 +295,9 @@ export function registerAccountSourceDisplaySettingsConfig({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.id}/display_settings/config`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:id/display_settings/config', + }) ); router.post( @@ -340,12 +310,9 @@ export function registerAccountSourceDisplaySettingsConfig({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.id}/display_settings/config`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:id/display_settings/config', + }) ); } @@ -362,11 +329,9 @@ export function registerAccountSourceSchemasRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.id}/schemas`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:id/schemas', + }) ); router.post( @@ -379,12 +344,9 @@ export function registerAccountSourceSchemasRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.id}/schemas`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:id/schemas', + }) ); } @@ -394,19 +356,17 @@ export function registerAccountSourceReindexJobRoute({ }: RouteDependencies) { router.get( { - path: '/api/workplace_search/account/sources/{source_id}/reindex_job/{job_id}', + path: '/api/workplace_search/account/sources/{sourceId}/reindex_job/{jobId}', validate: { params: schema.object({ - source_id: schema.string(), - job_id: schema.string(), + sourceId: schema.string(), + jobId: schema.string(), }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.source_id}/reindex_job/${request.params.job_id}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:sourceId/reindex_job/:jobId', + }) ); } @@ -416,19 +376,17 @@ export function registerAccountSourceReindexJobStatusRoute({ }: RouteDependencies) { router.get( { - path: '/api/workplace_search/account/sources/{source_id}/reindex_job/{job_id}/status', + path: '/api/workplace_search/account/sources/{sourceId}/reindex_job/{jobId}/status', validate: { params: schema.object({ - source_id: schema.string(), - job_id: schema.string(), + sourceId: schema.string(), + jobId: schema.string(), }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/sources/${request.params.source_id}/reindex_job/${request.params.job_id}/status`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/sources/:sourceId/reindex_job/:jobId/status', + }) ); } @@ -441,11 +399,9 @@ export function registerOrgSourcesRoute({ path: '/api/workplace_search/org/sources', validate: false, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/org/sources', - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources', + }) ); } @@ -458,11 +414,9 @@ export function registerOrgSourcesStatusRoute({ path: '/api/workplace_search/org/sources/status', validate: false, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/org/sources/status', - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/status', + }) ); } @@ -479,11 +433,9 @@ export function registerOrgSourceRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.id}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:id', + }) ); router.delete( @@ -495,11 +447,9 @@ export function registerOrgSourceRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.id}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:id', + }) ); } @@ -521,12 +471,9 @@ export function registerOrgCreateSourceRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/org/sources/form_create', - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/form_create', + }) ); } @@ -547,12 +494,9 @@ export function registerOrgSourceDocumentsRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.id}/documents`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:id/documents', + }) ); } @@ -569,11 +513,9 @@ export function registerOrgSourceFederatedSummaryRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.id}/federated_summary`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:id/federated_summary', + }) ); } @@ -590,11 +532,9 @@ export function registerOrgSourceReauthPrepareRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.id}/reauth_prepare`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:id/reauth_prepare', + }) ); } @@ -616,12 +556,9 @@ export function registerOrgSourceSettingsRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.id}/settings`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:id/settings', + }) ); } @@ -638,11 +575,9 @@ export function registerOrgPreSourceRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/pre_content_sources/${request.params.id}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/pre_content_sources/:id', + }) ); } @@ -652,18 +587,16 @@ export function registerOrgPrepareSourcesRoute({ }: RouteDependencies) { router.get( { - path: '/api/workplace_search/org/sources/{service_type}/prepare', + path: '/api/workplace_search/org/sources/{serviceType}/prepare', validate: { params: schema.object({ - service_type: schema.string(), + serviceType: schema.string(), }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.service_type}/prepare`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:serviceType/prepare', + }) ); } @@ -683,12 +616,9 @@ export function registerOrgSourceSearchableRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.id}/searchable`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:id/searchable', + }) ); } @@ -705,11 +635,9 @@ export function registerOrgSourceDisplaySettingsConfig({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.id}/display_settings/config`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:id/display_settings/config', + }) ); router.post( @@ -722,12 +650,9 @@ export function registerOrgSourceDisplaySettingsConfig({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.id}/display_settings/config`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:id/display_settings/config', + }) ); } @@ -744,11 +669,9 @@ export function registerOrgSourceSchemasRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.id}/schemas`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:id/schemas', + }) ); router.post( @@ -761,12 +684,9 @@ export function registerOrgSourceSchemasRoute({ }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.id}/schemas`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:id/schemas', + }) ); } @@ -776,19 +696,17 @@ export function registerOrgSourceReindexJobRoute({ }: RouteDependencies) { router.get( { - path: '/api/workplace_search/org/sources/{source_id}/reindex_job/{job_id}', + path: '/api/workplace_search/org/sources/{sourceId}/reindex_job/{jobId}', validate: { params: schema.object({ - source_id: schema.string(), - job_id: schema.string(), + sourceId: schema.string(), + jobId: schema.string(), }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.source_id}/reindex_job/${request.params.job_id}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:sourceId/reindex_job/:jobId', + }) ); } @@ -798,19 +716,17 @@ export function registerOrgSourceReindexJobStatusRoute({ }: RouteDependencies) { router.get( { - path: '/api/workplace_search/org/sources/{source_id}/reindex_job/{job_id}/status', + path: '/api/workplace_search/org/sources/{sourceId}/reindex_job/{jobId}/status', validate: { params: schema.object({ - source_id: schema.string(), - job_id: schema.string(), + sourceId: schema.string(), + jobId: schema.string(), }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/sources/${request.params.source_id}/reindex_job/${request.params.job_id}/status`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/sources/:sourceId/reindex_job/:jobId/status', + }) ); } @@ -823,11 +739,9 @@ export function registerOrgSourceOauthConfigurationsRoute({ path: '/api/workplace_search/org/settings/connectors', validate: false, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: '/ws/org/settings/connectors', - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/settings/connectors', + }) ); } @@ -837,70 +751,60 @@ export function registerOrgSourceOauthConfigurationRoute({ }: RouteDependencies) { router.get( { - path: '/api/workplace_search/org/settings/connectors/{service_type}', + path: '/api/workplace_search/org/settings/connectors/{serviceType}', validate: { params: schema.object({ - service_type: schema.string(), + serviceType: schema.string(), }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/settings/connectors/${request.params.service_type}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/settings/connectors/:serviceType', + }) ); router.post( { - path: '/api/workplace_search/org/settings/connectors/{service_type}', + path: '/api/workplace_search/org/settings/connectors/{serviceType}', validate: { params: schema.object({ - service_type: schema.string(), + serviceType: schema.string(), }), body: oAuthConfigSchema, }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/settings/connectors/${request.params.service_type}`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/settings/connectors/:serviceType', + }) ); router.put( { - path: '/api/workplace_search/org/settings/connectors/{service_type}', + path: '/api/workplace_search/org/settings/connectors/{serviceType}', validate: { params: schema.object({ - service_type: schema.string(), + serviceType: schema.string(), }), body: oAuthConfigSchema, }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/settings/connectors/${request.params.service_type}`, - body: request.body, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/settings/connectors/:serviceType', + }) ); router.delete( { - path: '/api/workplace_search/org/settings/connectors/{service_type}', + path: '/api/workplace_search/org/settings/connectors/{serviceType}', validate: { params: schema.object({ - service_type: schema.string(), + serviceType: schema.string(), }), }, }, - async (context, request, response) => { - return enterpriseSearchRequestHandler.createRequest({ - path: `/ws/org/settings/connectors/${request.params.service_type}`, - })(context, request, response); - } + enterpriseSearchRequestHandler.createRequest({ + path: '/ws/org/settings/connectors/:serviceType', + }) ); } diff --git a/x-pack/plugins/event_log/server/plugin.ts b/x-pack/plugins/event_log/server/plugin.ts index 03125f3005c3..3bf726de7185 100644 --- a/x-pack/plugins/event_log/server/plugin.ts +++ b/x-pack/plugins/event_log/server/plugin.ts @@ -15,11 +15,11 @@ import { LegacyClusterClient, SharedGlobalConfig, IContextProvider, - RequestHandler, } from 'src/core/server'; import { SpacesPluginStart } from '../../spaces/server'; -import { +import type { + EventLogRequestHandlerContext, IEventLogConfig, IEventLogService, IEventLogger, @@ -97,10 +97,13 @@ export class Plugin implements CorePlugin( + 'eventLog', + this.createRouteHandlerContext() + ); // Routes - const router = core.http.createRouter(); + const router = core.http.createRouter(); // Register routes findRoute(router, this.systemLogger); findByIdsRoute(router, this.systemLogger); @@ -169,7 +172,7 @@ export class Plugin implements CorePlugin, + EventLogRequestHandlerContext, 'eventLog' > => { return async (context, request) => { diff --git a/x-pack/plugins/event_log/server/routes/find.ts b/x-pack/plugins/event_log/server/routes/find.ts index 50785de72cfc..aa882fb00275 100644 --- a/x-pack/plugins/event_log/server/routes/find.ts +++ b/x-pack/plugins/event_log/server/routes/find.ts @@ -5,15 +5,13 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, +import type { KibanaRequest, IKibanaResponse, KibanaResponseFactory, Logger, } from 'src/core/server'; - +import type { EventLogRouter, EventLogRequestHandlerContext } from '../types'; import { BASE_EVENT_LOG_API_PATH } from '../../common'; import { findOptionsSchema, FindOptionsType } from '../event_log_client'; @@ -22,7 +20,7 @@ const paramSchema = schema.object({ id: schema.string(), }); -export const findRoute = (router: IRouter, systemLogger: Logger) => { +export const findRoute = (router: EventLogRouter, systemLogger: Logger) => { router.get( { path: `${BASE_EVENT_LOG_API_PATH}/{type}/{id}/_find`, @@ -32,7 +30,7 @@ export const findRoute = (router: IRouter, systemLogger: Logger) => { }, }, router.handleLegacyErrors(async function ( - context: RequestHandlerContext, + context: EventLogRequestHandlerContext, req: KibanaRequest, FindOptionsType, unknown>, res: KibanaResponseFactory ): Promise { diff --git a/x-pack/plugins/event_log/server/routes/find_by_ids.ts b/x-pack/plugins/event_log/server/routes/find_by_ids.ts index a7ee0f35ac59..a846c93eb95e 100644 --- a/x-pack/plugins/event_log/server/routes/find_by_ids.ts +++ b/x-pack/plugins/event_log/server/routes/find_by_ids.ts @@ -5,14 +5,13 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, +import type { KibanaRequest, IKibanaResponse, KibanaResponseFactory, Logger, } from 'src/core/server'; +import type { EventLogRouter, EventLogRequestHandlerContext } from '../types'; import { BASE_EVENT_LOG_API_PATH } from '../../common'; import { findOptionsSchema, FindOptionsType } from '../event_log_client'; @@ -25,7 +24,7 @@ const bodySchema = schema.object({ ids: schema.arrayOf(schema.string(), { defaultValue: [] }), }); -export const findByIdsRoute = (router: IRouter, systemLogger: Logger) => { +export const findByIdsRoute = (router: EventLogRouter, systemLogger: Logger) => { router.post( { path: `${BASE_EVENT_LOG_API_PATH}/{type}/_find`, @@ -36,7 +35,7 @@ export const findByIdsRoute = (router: IRouter, systemLogger: Logger) => { }, }, router.handleLegacyErrors(async function ( - context: RequestHandlerContext, + context: EventLogRequestHandlerContext, req: KibanaRequest, FindOptionsType, TypeOf>, res: KibanaResponseFactory ): Promise { diff --git a/x-pack/plugins/event_log/server/types.ts b/x-pack/plugins/event_log/server/types.ts index ff2ae8163292..e995e979a080 100644 --- a/x-pack/plugins/event_log/server/types.ts +++ b/x-pack/plugins/event_log/server/types.ts @@ -6,7 +6,7 @@ import { Observable } from 'rxjs'; import { schema, TypeOf } from '@kbn/config-schema'; -import { KibanaRequest } from 'src/core/server'; +import type { IRouter, KibanaRequest, RequestHandlerContext } from 'src/core/server'; export { IEvent, IValidatedEvent, EventSchema, ECS_VERSION } from '../generated/schemas'; import { IEvent } from '../generated/schemas'; @@ -26,14 +26,6 @@ export const ConfigSchema = schema.object({ export type IEventLogConfig = TypeOf; export type IEventLogConfig$ = Observable>; -declare module 'src/core/server' { - interface RequestHandlerContext { - eventLog?: { - getEventLogClient: () => IEventLogClient; - }; - } -} - // the object exposed by plugin.setup() export interface IEventLogService { isEnabled(): boolean; @@ -63,3 +55,22 @@ export interface IEventLogger { startTiming(event: IEvent): void; stopTiming(event: IEvent): void; } + +/** + * @internal + */ +export interface EventLogApiRequestHandlerContext { + getEventLogClient(): IEventLogClient; +} + +/** + * @internal + */ +export interface EventLogRequestHandlerContext extends RequestHandlerContext { + eventLog: EventLogApiRequestHandlerContext; +} + +/** + * @internal + */ +export type EventLogRouter = IRouter; diff --git a/x-pack/plugins/features/server/routes/index.ts b/x-pack/plugins/features/server/routes/index.ts index b2bfa8b0296b..cd6d22096183 100644 --- a/x-pack/plugins/features/server/routes/index.ts +++ b/x-pack/plugins/features/server/routes/index.ts @@ -5,14 +5,14 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from '../../../../../src/core/server'; +import type { FeaturesPluginRouter } from '../types'; import { FeatureRegistry } from '../feature_registry'; /** * Describes parameters used to define HTTP routes. */ export interface RouteDefinitionParams { - router: IRouter; + router: FeaturesPluginRouter; featureRegistry: FeatureRegistry; } diff --git a/x-pack/plugins/features/server/types.ts b/x-pack/plugins/features/server/types.ts new file mode 100644 index 000000000000..d42fa1c498d4 --- /dev/null +++ b/x-pack/plugins/features/server/types.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. + */ +import type { RequestHandlerContext, IRouter } from 'src/core/server'; +import type { LicensingApiRequestHandlerContext } from '../../licensing/server'; + +/** + * @internal + */ +export interface FeaturesRequestHandlerContext extends RequestHandlerContext { + licensing: LicensingApiRequestHandlerContext; +} + +/** + * @internal + */ +export type FeaturesPluginRouter = IRouter; diff --git a/x-pack/plugins/fleet/common/constants/agent.ts b/x-pack/plugins/fleet/common/constants/agent.ts index 30b8a6b74060..8bfb32b5ed2b 100644 --- a/x-pack/plugins/fleet/common/constants/agent.ts +++ b/x-pack/plugins/fleet/common/constants/agent.ts @@ -22,3 +22,5 @@ export const AGENT_UPDATE_ACTIONS_INTERVAL_MS = 5000; export const AGENT_POLICY_ROLLOUT_RATE_LIMIT_INTERVAL_MS = 1000; export const AGENT_POLICY_ROLLOUT_RATE_LIMIT_REQUEST_PER_INTERVAL = 5; + +export const AGENTS_INDEX = '.fleet-agents'; diff --git a/x-pack/plugins/fleet/common/constants/agent_policy.ts b/x-pack/plugins/fleet/common/constants/agent_policy.ts index 5445fbcacf2e..2dd21fb41b66 100644 --- a/x-pack/plugins/fleet/common/constants/agent_policy.ts +++ b/x-pack/plugins/fleet/common/constants/agent_policy.ts @@ -6,7 +6,7 @@ import { defaultPackages } from './epm'; import { AgentPolicy } from '../types'; export const AGENT_POLICY_SAVED_OBJECT_TYPE = 'ingest-agent-policies'; - +export const AGENT_POLICY_INDEX = '.fleet-policies'; export const agentPolicyStatuses = { Active: 'active', Inactive: 'inactive', diff --git a/x-pack/plugins/fleet/common/constants/enrollment_api_key.ts b/x-pack/plugins/fleet/common/constants/enrollment_api_key.ts index fd28b6632b15..ce774f221246 100644 --- a/x-pack/plugins/fleet/common/constants/enrollment_api_key.ts +++ b/x-pack/plugins/fleet/common/constants/enrollment_api_key.ts @@ -5,3 +5,5 @@ */ export const ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE = 'fleet-enrollment-api-keys'; + +export const ENROLLMENT_API_KEYS_INDEX = '.fleet-enrollment-api-keys'; diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index e9b11a2f5ac8..55c32802c333 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -32,7 +32,7 @@ "items": { "type": "array", "items": { - "$ref": "#/components/schemas/agent_policy" + "$ref": "#/paths/~1agent_policies/post/responses/200/content/application~1json/schema/properties/item" } }, "total": { @@ -59,13 +59,31 @@ "operationId": "agent-policy-list", "parameters": [ { - "$ref": "#/components/parameters/page_size" + "name": "perPage", + "in": "query", + "description": "The number of items to return", + "required": false, + "schema": { + "type": "integer", + "default": 50 + } }, { - "$ref": "#/components/parameters/page_index" + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } }, { - "$ref": "#/components/parameters/kuery" + "name": "kuery", + "in": "query", + "required": false, + "schema": { + "type": "string" + } } ], "description": "" @@ -82,7 +100,58 @@ "type": "object", "properties": { "item": { - "$ref": "#/components/schemas/agent_policy" + "allOf": [ + { + "$ref": "#/paths/~1agent_policies/post/requestBody/content/application~1json/schema" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "packagePolicies": { + "oneOf": [ + { + "items": { + "type": "string" + } + }, + { + "items": { + "$ref": "#/paths/~1package_policies~1%7BpackagePolicyId%7D/get/responses/200/content/application~1json/schema/properties/item" + } + } + ], + "type": "array" + }, + "updated_on": { + "type": "string", + "format": "date-time" + }, + "updated_by": { + "type": "string" + }, + "revision": { + "type": "number" + }, + "agents": { + "type": "number" + } + }, + "required": [ + "id", + "status" + ] + } + ] } } } @@ -95,7 +164,19 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/new_agent_policy" + "title": "NewAgentPolicy", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "description": { + "type": "string" + } + } } } } @@ -103,7 +184,7 @@ "security": [], "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] } @@ -131,7 +212,7 @@ "type": "object", "properties": { "item": { - "$ref": "#/components/schemas/agent_policy" + "$ref": "#/paths/~1agent_policies/post/responses/200/content/application~1json/schema/properties/item" } }, "required": [ @@ -158,7 +239,7 @@ "type": "object", "properties": { "item": { - "$ref": "#/components/schemas/agent_policy" + "$ref": "#/paths/~1agent_policies/post/responses/200/content/application~1json/schema/properties/item" } }, "required": [ @@ -174,14 +255,14 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/new_agent_policy" + "$ref": "#/paths/~1agent_policies/post/requestBody/content/application~1json/schema" } } } }, "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] } @@ -209,7 +290,7 @@ "type": "object", "properties": { "item": { - "$ref": "#/components/schemas/agent_policy" + "$ref": "#/paths/~1agent_policies/post/responses/200/content/application~1json/schema/properties/item" } }, "required": [ @@ -294,7 +375,7 @@ }, "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] }, @@ -405,7 +486,7 @@ "operationId": "post-fleet-agents-agentId-acks", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ], "requestBody": { @@ -488,7 +569,7 @@ "operationId": "post-fleet-agents-agentId-checkin", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ], "security": [ @@ -503,12 +584,69 @@ "type": "object", "properties": { "local_metadata": { - "$ref": "#/components/schemas/agent_metadata" + "title": "AgentMetadata", + "type": "object" }, "events": { "type": "array", "items": { - "$ref": "#/components/schemas/new_agent_event" + "title": "NewAgentEvent", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "STATE", + "ERROR", + "ACTION_RESULT", + "ACTION" + ] + }, + "subtype": { + "type": "string", + "enum": [ + "RUNNING", + "STARTING", + "IN_PROGRESS", + "CONFIG", + "FAILED", + "STOPPING", + "STOPPED", + "DEGRADED", + "DATA_DUMP", + "ACKNOWLEDGED", + "UNKNOWN" + ] + }, + "timestamp": { + "type": "string" + }, + "message": { + "type": "string" + }, + "payload": { + "type": "string" + }, + "agent_id": { + "type": "string" + }, + "policy_id": { + "type": "string" + }, + "stream_id": { + "type": "string" + }, + "action_id": { + "type": "string" + } + }, + "required": [ + "type", + "subtype", + "timestamp", + "message", + "agent_id" + ] } } } @@ -554,7 +692,7 @@ "operationId": "post-fleet-agents-unenroll", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ], "requestBody": { @@ -593,7 +731,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/upgrade_agent" + "$ref": "#/paths/~1agents~1%7BagentId%7D~1upgrade/post/requestBody/content/application~1json/schema" } } } @@ -603,7 +741,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/upgrade_agent" + "$ref": "#/paths/~1agents~1%7BagentId%7D~1upgrade/post/requestBody/content/application~1json/schema" } } } @@ -612,7 +750,7 @@ "operationId": "post-fleet-agents-upgrade", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ], "requestBody": { @@ -620,7 +758,34 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/upgrade_agent" + "title": "UpgradeAgent", + "oneOf": [ + { + "type": "object", + "properties": { + "version": { + "type": "string" + } + }, + "required": [ + "version" + ] + }, + { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "source_uri": { + "type": "string" + } + }, + "required": [ + "version" + ] + } + ] } } } @@ -637,7 +802,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/bulk_upgrade_agents" + "$ref": "#/paths/~1agents~1bulk_upgrade/post/requestBody/content/application~1json/schema" } } } @@ -647,7 +812,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/upgrade_agent" + "$ref": "#/paths/~1agents~1%7BagentId%7D~1upgrade/post/requestBody/content/application~1json/schema" } } } @@ -656,7 +821,7 @@ "operationId": "post-fleet-agents-bulk-upgrade", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ], "requestBody": { @@ -664,7 +829,66 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/bulk_upgrade_agents" + "title": "BulkUpgradeAgents", + "oneOf": [ + { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "agents": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "version", + "agents" + ] + }, + { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "source_uri": { + "type": "string" + }, + "agents": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "version", + "agents" + ] + }, + { + "type": "object", + "properties": { + "version": { + "type": "string" + }, + "source_uri": { + "type": "string" + }, + "agents": { + "type": "string" + } + }, + "required": [ + "version", + "agents" + ] + } + ] } } } @@ -687,7 +911,106 @@ "type": "string" }, "item": { - "$ref": "#/components/schemas/agent" + "title": "Agent", + "type": "object", + "properties": { + "type": { + "type": "string", + "title": "AgentType", + "enum": [ + "PERMANENT", + "EPHEMERAL", + "TEMPORARY" + ] + }, + "active": { + "type": "boolean" + }, + "enrolled_at": { + "type": "string" + }, + "unenrolled_at": { + "type": "string" + }, + "unenrollment_started_at": { + "type": "string" + }, + "shared_id": { + "type": "string", + "deprecated": true + }, + "access_api_key_id": { + "type": "string" + }, + "default_api_key_id": { + "type": "string" + }, + "policy_id": { + "type": "string" + }, + "policy_revision": { + "type": "number" + }, + "last_checkin": { + "type": "string" + }, + "user_provided_metadata": { + "$ref": "#/paths/~1agents~1%7BagentId%7D~1checkin/post/requestBody/content/application~1json/schema/properties/local_metadata" + }, + "local_metadata": { + "$ref": "#/paths/~1agents~1%7BagentId%7D~1checkin/post/requestBody/content/application~1json/schema/properties/local_metadata" + }, + "id": { + "type": "string" + }, + "current_error_events": { + "type": "array", + "items": { + "title": "AgentEvent", + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + { + "$ref": "#/paths/~1agents~1%7BagentId%7D~1checkin/post/requestBody/content/application~1json/schema/properties/events/items" + } + ] + } + }, + "access_api_key": { + "type": "string" + }, + "status": { + "type": "string", + "title": "AgentStatus", + "enum": [ + "offline", + "error", + "online", + "inactive", + "warning" + ] + }, + "default_api_key": { + "type": "string" + } + }, + "required": [ + "type", + "active", + "enrolled_at", + "id", + "current_error_events", + "status" + ] } } } @@ -698,7 +1021,7 @@ "operationId": "post-fleet-agents-enroll", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ], "requestBody": { @@ -716,7 +1039,8 @@ ] }, "shared_id": { - "type": "string" + "type": "string", + "deprecated": true }, "metadata": { "type": "object", @@ -726,10 +1050,10 @@ ], "properties": { "local": { - "$ref": "#/components/schemas/agent_metadata" + "$ref": "#/paths/~1agents~1%7BagentId%7D~1checkin/post/requestBody/content/application~1json/schema/properties/local_metadata" }, "user_provided": { - "$ref": "#/components/schemas/agent_metadata" + "$ref": "#/paths/~1agents~1%7BagentId%7D~1checkin/post/requestBody/content/application~1json/schema/properties/local_metadata" } } } @@ -826,7 +1150,7 @@ }, "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] } @@ -846,7 +1170,7 @@ "operationId": "post-fleet-enrollment-api-keys", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] } @@ -875,7 +1199,7 @@ "operationId": "delete-fleet-enrollment-api-keys-keyId", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] } @@ -930,7 +1254,51 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/search_result" + "title": "SearchResult", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "download": { + "type": "string" + }, + "icons": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string" + }, + "version": { + "type": "string" + }, + "status": { + "type": "string" + }, + "savedObject": { + "type": "object" + } + }, + "required": [ + "description", + "download", + "icons", + "name", + "path", + "title", + "type", + "version", + "status" + ] } } } @@ -956,7 +1324,182 @@ { "properties": { "response": { - "$ref": "#/components/schemas/package_info" + "title": "PackageInfo", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "title": { + "type": "string" + }, + "version": { + "type": "string" + }, + "readme": { + "type": "string" + }, + "description": { + "type": "string" + }, + "type": { + "type": "string" + }, + "categories": { + "type": "array", + "items": { + "type": "string" + } + }, + "requirement": { + "oneOf": [ + { + "properties": { + "kibana": { + "type": "object", + "properties": { + "versions": { + "type": "string" + } + } + } + } + }, + { + "properties": { + "elasticsearch": { + "type": "object", + "properties": { + "versions": { + "type": "string" + } + } + } + } + } + ], + "type": "object" + }, + "screenshots": { + "type": "array", + "items": { + "type": "object", + "properties": { + "src": { + "type": "string" + }, + "path": { + "type": "string" + }, + "title": { + "type": "string" + }, + "size": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "src", + "path" + ] + } + }, + "icons": { + "type": "array", + "items": { + "type": "string" + } + }, + "assets": { + "type": "array", + "items": { + "type": "string" + } + }, + "internal": { + "type": "boolean" + }, + "format_version": { + "type": "string" + }, + "data_streams": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "name": { + "type": "string" + }, + "release": { + "type": "string" + }, + "ingeset_pipeline": { + "type": "string" + }, + "vars": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "default": { + "type": "string" + } + }, + "required": [ + "name", + "default" + ] + } + }, + "type": { + "type": "string" + }, + "package": { + "type": "string" + } + }, + "required": [ + "title", + "name", + "release", + "ingeset_pipeline", + "type", + "package" + ] + } + }, + "download": { + "type": "string" + }, + "path": { + "type": "string" + }, + "removable": { + "type": "boolean" + } + }, + "required": [ + "name", + "title", + "version", + "description", + "type", + "categories", + "requirement", + "assets", + "format_version", + "download", + "path" + ] } } }, @@ -1043,7 +1586,7 @@ "description": "", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] }, @@ -1088,7 +1631,7 @@ "operationId": "post-epm-delete-pkgkey", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] } @@ -1136,7 +1679,7 @@ "operationId": "put-fleet-agents-agentId", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] }, @@ -1147,7 +1690,7 @@ "operationId": "delete-fleet-agents-agentId", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] } @@ -1185,7 +1728,7 @@ "items": { "type": "array", "items": { - "$ref": "#/components/schemas/package_policy" + "$ref": "#/paths/~1package_policies~1%7BpackagePolicyId%7D/get/responses/200/content/application~1json/schema/properties/item" } }, "total": { @@ -1223,14 +1766,96 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/new_package_policy" + "title": "NewPackagePolicy", + "type": "object", + "description": "", + "properties": { + "enabled": { + "type": "boolean" + }, + "package": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "name", + "version", + "title" + ] + }, + "namespace": { + "type": "string" + }, + "output_id": { + "type": "string" + }, + "inputs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "processors": { + "type": "array", + "items": { + "type": "string" + } + }, + "streams": { + "type": "array", + "items": {} + }, + "config": { + "type": "object" + }, + "vars": { + "type": "object" + } + }, + "required": [ + "type", + "enabled", + "streams" + ] + } + }, + "policy_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "output_id", + "inputs", + "policy_id", + "name" + ] } } } }, "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] } @@ -1248,7 +1873,31 @@ "type": "object", "properties": { "item": { - "$ref": "#/components/schemas/package_policy" + "title": "PackagePolicy", + "allOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "revision": { + "type": "number" + }, + "inputs": { + "type": "array", + "items": {} + } + }, + "required": [ + "id", + "revision" + ] + }, + { + "$ref": "#/paths/~1package_policies/post/requestBody/content/application~1json/schema" + } + ] } }, "required": [ @@ -1278,7 +1927,20 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/update_package_policy" + "title": "UpdatePackagePolicy", + "allOf": [ + { + "type": "object", + "properties": { + "version": { + "type": "string" + } + } + }, + { + "$ref": "#/paths/~1package_policies/post/requestBody/content/application~1json/schema" + } + ] } } } @@ -1292,7 +1954,7 @@ "type": "object", "properties": { "item": { - "$ref": "#/components/schemas/package_policy" + "$ref": "#/paths/~1package_policies~1%7BpackagePolicyId%7D/get/responses/200/content/application~1json/schema/properties/item" }, "sucess": { "type": "boolean" @@ -1309,7 +1971,7 @@ }, "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "$ref": "#/paths/~1setup/post/parameters/0" } ] } @@ -1353,7 +2015,12 @@ "operationId": "post-setup", "parameters": [ { - "$ref": "#/components/parameters/kbn_xsrf" + "schema": { + "type": "string" + }, + "in": "header", + "name": "kbn-xsrf", + "required": true } ] } @@ -1377,732 +2044,6 @@ "in": "header", "description": "e.g. Authorization: ApiKey base64AccessApiKey" } - }, - "parameters": { - "page_size": { - "name": "perPage", - "in": "query", - "description": "The number of items to return", - "required": false, - "schema": { - "type": "integer", - "default": 50 - } - }, - "page_index": { - "name": "page", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "default": 1 - } - }, - "kuery": { - "name": "kuery", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - "kbn_xsrf": { - "schema": { - "type": "string" - }, - "in": "header", - "name": "kbn-xsrf", - "required": true - } - }, - "schemas": { - "new_agent_policy": { - "title": "NewAgentPolicy", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "namespace": { - "type": "string" - }, - "description": { - "type": "string" - } - } - }, - "new_package_policy": { - "title": "NewPackagePolicy", - "type": "object", - "description": "", - "properties": { - "enabled": { - "type": "boolean" - }, - "package": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "version": { - "type": "string" - }, - "title": { - "type": "string" - } - }, - "required": [ - "name", - "version", - "title" - ] - }, - "namespace": { - "type": "string" - }, - "output_id": { - "type": "string" - }, - "inputs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "enabled": { - "type": "boolean" - }, - "processors": { - "type": "array", - "items": { - "type": "string" - } - }, - "streams": { - "type": "array", - "items": {} - }, - "config": { - "type": "object" - }, - "vars": { - "type": "object" - } - }, - "required": [ - "type", - "enabled", - "streams" - ] - } - }, - "policy_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - } - }, - "required": [ - "output_id", - "inputs", - "policy_id", - "name" - ] - }, - "package_policy": { - "title": "PackagePolicy", - "allOf": [ - { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "revision": { - "type": "number" - }, - "inputs": { - "type": "array", - "items": {} - } - }, - "required": [ - "id", - "revision" - ] - }, - { - "$ref": "#/components/schemas/new_package_policy" - } - ] - }, - "agent_policy": { - "allOf": [ - { - "$ref": "#/components/schemas/new_agent_policy" - }, - { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - }, - "packagePolicies": { - "oneOf": [ - { - "items": { - "type": "string" - } - }, - { - "items": { - "$ref": "#/components/schemas/package_policy" - } - } - ], - "type": "array" - }, - "updated_on": { - "type": "string", - "format": "date-time" - }, - "updated_by": { - "type": "string" - }, - "revision": { - "type": "number" - }, - "agents": { - "type": "number" - } - }, - "required": [ - "id", - "status" - ] - } - ] - }, - "agent_metadata": { - "title": "AgentMetadata", - "type": "object" - }, - "new_agent_event": { - "title": "NewAgentEvent", - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "STATE", - "ERROR", - "ACTION_RESULT", - "ACTION" - ] - }, - "subtype": { - "type": "string", - "enum": [ - "RUNNING", - "STARTING", - "IN_PROGRESS", - "CONFIG", - "FAILED", - "STOPPING", - "STOPPED", - "DEGRADED", - "DATA_DUMP", - "ACKNOWLEDGED", - "UNKNOWN" - ] - }, - "timestamp": { - "type": "string" - }, - "message": { - "type": "string" - }, - "payload": { - "type": "string" - }, - "agent_id": { - "type": "string" - }, - "policy_id": { - "type": "string" - }, - "stream_id": { - "type": "string" - }, - "action_id": { - "type": "string" - } - }, - "required": [ - "type", - "subtype", - "timestamp", - "message", - "agent_id" - ] - }, - "upgrade_agent": { - "title": "UpgradeAgent", - "oneOf": [ - { - "type": "object", - "properties": { - "version": { - "type": "string" - } - }, - "required": [ - "version" - ] - }, - { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "source_uri": { - "type": "string" - } - }, - "required": [ - "version" - ] - } - ] - }, - "bulk_upgrade_agents": { - "title": "BulkUpgradeAgents", - "oneOf": [ - { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "agents": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "version", - "agents" - ] - }, - { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "source_uri": { - "type": "string" - }, - "agents": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "version", - "agents" - ] - }, - { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "source_uri": { - "type": "string" - }, - "agents": { - "type": "string" - } - }, - "required": [ - "version", - "agents" - ] - } - ] - }, - "agent_type": { - "type": "string", - "title": "AgentType", - "enum": [ - "PERMANENT", - "EPHEMERAL", - "TEMPORARY" - ] - }, - "agent_event": { - "title": "AgentEvent", - "allOf": [ - { - "type": "object", - "properties": { - "id": { - "type": "string" - } - }, - "required": [ - "id" - ] - }, - { - "$ref": "#/components/schemas/new_agent_event" - } - ] - }, - "agent_status": { - "type": "string", - "title": "AgentStatus", - "enum": [ - "offline", - "error", - "online", - "inactive", - "warning" - ] - }, - "agent": { - "title": "Agent", - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/agent_type" - }, - "active": { - "type": "boolean" - }, - "enrolled_at": { - "type": "string" - }, - "unenrolled_at": { - "type": "string" - }, - "unenrollment_started_at": { - "type": "string" - }, - "shared_id": { - "type": "string" - }, - "access_api_key_id": { - "type": "string" - }, - "default_api_key_id": { - "type": "string" - }, - "policy_id": { - "type": "string" - }, - "policy_revision": { - "type": "number" - }, - "last_checkin": { - "type": "string" - }, - "user_provided_metadata": { - "$ref": "#/components/schemas/agent_metadata" - }, - "local_metadata": { - "$ref": "#/components/schemas/agent_metadata" - }, - "id": { - "type": "string" - }, - "current_error_events": { - "type": "array", - "items": { - "$ref": "#/components/schemas/agent_event" - } - }, - "access_api_key": { - "type": "string" - }, - "status": { - "$ref": "#/components/schemas/agent_status" - }, - "default_api_key": { - "type": "string" - } - }, - "required": [ - "type", - "active", - "enrolled_at", - "id", - "current_error_events", - "status" - ] - }, - "search_result": { - "title": "SearchResult", - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "download": { - "type": "string" - }, - "icons": { - "type": "string" - }, - "name": { - "type": "string" - }, - "path": { - "type": "string" - }, - "title": { - "type": "string" - }, - "type": { - "type": "string" - }, - "version": { - "type": "string" - }, - "status": { - "type": "string" - }, - "savedObject": { - "type": "object" - } - }, - "required": [ - "description", - "download", - "icons", - "name", - "path", - "title", - "type", - "version", - "status" - ] - }, - "package_info": { - "title": "PackageInfo", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "title": { - "type": "string" - }, - "version": { - "type": "string" - }, - "readme": { - "type": "string" - }, - "description": { - "type": "string" - }, - "type": { - "type": "string" - }, - "categories": { - "type": "array", - "items": { - "type": "string" - } - }, - "requirement": { - "oneOf": [ - { - "properties": { - "kibana": { - "type": "object", - "properties": { - "versions": { - "type": "string" - } - } - } - } - }, - { - "properties": { - "elasticsearch": { - "type": "object", - "properties": { - "versions": { - "type": "string" - } - } - } - } - } - ], - "type": "object" - }, - "screenshots": { - "type": "array", - "items": { - "type": "object", - "properties": { - "src": { - "type": "string" - }, - "path": { - "type": "string" - }, - "title": { - "type": "string" - }, - "size": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "required": [ - "src", - "path" - ] - } - }, - "icons": { - "type": "array", - "items": { - "type": "string" - } - }, - "assets": { - "type": "array", - "items": { - "type": "string" - } - }, - "internal": { - "type": "boolean" - }, - "format_version": { - "type": "string" - }, - "data_streams": { - "type": "array", - "items": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "name": { - "type": "string" - }, - "release": { - "type": "string" - }, - "ingeset_pipeline": { - "type": "string" - }, - "vars": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "default": { - "type": "string" - } - }, - "required": [ - "name", - "default" - ] - } - }, - "type": { - "type": "string" - }, - "package": { - "type": "string" - } - }, - "required": [ - "title", - "name", - "release", - "ingeset_pipeline", - "type", - "package" - ] - } - }, - "download": { - "type": "string" - }, - "path": { - "type": "string" - }, - "removable": { - "type": "boolean" - } - }, - "required": [ - "name", - "title", - "version", - "description", - "type", - "categories", - "requirement", - "assets", - "format_version", - "download", - "path" - ] - }, - "update_package_policy": { - "title": "UpdatePackagePolicy", - "allOf": [ - { - "type": "object", - "properties": { - "version": { - "type": "string" - } - } - }, - { - "$ref": "#/components/schemas/new_package_policy" - } - ] - } } }, "security": [ @@ -2110,4 +2051,4 @@ "basicAuth": [] } ] -} \ No newline at end of file +} diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index 05b5b239dc98..9461927bb09b 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -25,7 +25,7 @@ paths: items: type: array items: - $ref: '#/components/schemas/agent_policy' + $ref: '#/paths/~1agent_policies/post/responses/200/content/application~1json/schema/properties/item' total: type: number page: @@ -39,9 +39,24 @@ paths: - perPage operationId: agent-policy-list parameters: - - $ref: '#/components/parameters/page_size' - - $ref: '#/components/parameters/page_index' - - $ref: '#/components/parameters/kuery' + - name: perPage + in: query + description: The number of items to return + required: false + schema: + type: integer + default: 50 + - name: page + in: query + required: false + schema: + type: integer + default: 1 + - name: kuery + in: query + required: false + schema: + type: string description: '' post: summary: Agent policy - Create @@ -55,16 +70,53 @@ paths: type: object properties: item: - $ref: '#/components/schemas/agent_policy' + allOf: + - $ref: '#/paths/~1agent_policies/post/requestBody/content/application~1json/schema' + - type: object + properties: + id: + type: string + status: + type: string + enum: + - active + - inactive + packagePolicies: + oneOf: + - items: + type: string + - items: + $ref: '#/paths/~1package_policies~1%7BpackagePolicyId%7D/get/responses/200/content/application~1json/schema/properties/item' + type: array + updated_on: + type: string + format: date-time + updated_by: + type: string + revision: + type: number + agents: + type: number + required: + - id + - status operationId: post-agent-policy requestBody: content: application/json: schema: - $ref: '#/components/schemas/new_agent_policy' + title: NewAgentPolicy + type: object + properties: + name: + type: string + namespace: + type: string + description: + type: string security: [] parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' '/agent_policies/{agentPolicyId}': parameters: - schema: @@ -84,7 +136,7 @@ paths: type: object properties: item: - $ref: '#/components/schemas/agent_policy' + $ref: '#/paths/~1agent_policies/post/responses/200/content/application~1json/schema/properties/item' required: - item operationId: agent-policy-info @@ -102,7 +154,7 @@ paths: type: object properties: item: - $ref: '#/components/schemas/agent_policy' + $ref: '#/paths/~1agent_policies/post/responses/200/content/application~1json/schema/properties/item' required: - item operationId: put-agent-policy-agentPolicyId @@ -110,9 +162,9 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/new_agent_policy' + $ref: '#/paths/~1agent_policies/post/requestBody/content/application~1json/schema' parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' '/agent_policies/{agentPolicyId}/copy': parameters: - schema: @@ -132,7 +184,7 @@ paths: type: object properties: item: - $ref: '#/components/schemas/agent_policy' + $ref: '#/paths/~1agent_policies/post/responses/200/content/application~1json/schema/properties/item' required: - item requestBody: @@ -181,7 +233,7 @@ paths: items: type: string parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' parameters: [] /agent-status: get: @@ -251,7 +303,7 @@ paths: - action operationId: post-fleet-agents-agentId-acks parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' requestBody: content: application/json: @@ -304,7 +356,7 @@ paths: - type operationId: post-fleet-agents-agentId-checkin parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' security: - Access API Key: [] requestBody: @@ -314,11 +366,55 @@ paths: type: object properties: local_metadata: - $ref: '#/components/schemas/agent_metadata' + title: AgentMetadata + type: object events: type: array items: - $ref: '#/components/schemas/new_agent_event' + title: NewAgentEvent + type: object + properties: + type: + type: string + enum: + - STATE + - ERROR + - ACTION_RESULT + - ACTION + subtype: + type: string + enum: + - RUNNING + - STARTING + - IN_PROGRESS + - CONFIG + - FAILED + - STOPPING + - STOPPED + - DEGRADED + - DATA_DUMP + - ACKNOWLEDGED + - UNKNOWN + timestamp: + type: string + message: + type: string + payload: + type: string + agent_id: + type: string + policy_id: + type: string + stream_id: + type: string + action_id: + type: string + required: + - type + - subtype + - timestamp + - message + - agent_id '/agents/{agentId}/events': parameters: - schema: @@ -344,7 +440,7 @@ paths: responses: {} operationId: post-fleet-agents-unenroll parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' requestBody: content: application/json: @@ -369,22 +465,37 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/upgrade_agent' + $ref: '#/paths/~1agents~1%7BagentId%7D~1upgrade/post/requestBody/content/application~1json/schema' '400': description: BAD REQUEST content: application/json: schema: - $ref: '#/components/schemas/upgrade_agent' + $ref: '#/paths/~1agents~1%7BagentId%7D~1upgrade/post/requestBody/content/application~1json/schema' operationId: post-fleet-agents-upgrade parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/upgrade_agent' + title: UpgradeAgent + oneOf: + - type: object + properties: + version: + type: string + required: + - version + - type: object + properties: + version: + type: string + source_uri: + type: string + required: + - version /agents/bulk_upgrade: post: summary: Fleet - Agent - Bulk Upgrade @@ -395,22 +506,58 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/bulk_upgrade_agents' + $ref: '#/paths/~1agents~1bulk_upgrade/post/requestBody/content/application~1json/schema' '400': description: BAD REQUEST content: application/json: schema: - $ref: '#/components/schemas/upgrade_agent' + $ref: '#/paths/~1agents~1%7BagentId%7D~1upgrade/post/requestBody/content/application~1json/schema' operationId: post-fleet-agents-bulk-upgrade parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' requestBody: required: true content: application/json: schema: - $ref: '#/components/schemas/bulk_upgrade_agents' + title: BulkUpgradeAgents + oneOf: + - type: object + properties: + version: + type: string + agents: + type: array + items: + type: string + required: + - version + - agents + - type: object + properties: + version: + type: string + source_uri: + type: string + agents: + type: array + items: + type: string + required: + - version + - agents + - type: object + properties: + version: + type: string + source_uri: + type: string + agents: + type: string + required: + - version + - agents /agents/enroll: post: summary: Fleet - Agent - Enroll @@ -426,10 +573,78 @@ paths: action: type: string item: - $ref: '#/components/schemas/agent' + title: Agent + type: object + properties: + type: + type: string + title: AgentType + enum: + - PERMANENT + - EPHEMERAL + - TEMPORARY + active: + type: boolean + enrolled_at: + type: string + unenrolled_at: + type: string + unenrollment_started_at: + type: string + shared_id: + type: string + deprecated: true + access_api_key_id: + type: string + default_api_key_id: + type: string + policy_id: + type: string + policy_revision: + type: number + last_checkin: + type: string + user_provided_metadata: + $ref: '#/paths/~1agents~1%7BagentId%7D~1checkin/post/requestBody/content/application~1json/schema/properties/local_metadata' + local_metadata: + $ref: '#/paths/~1agents~1%7BagentId%7D~1checkin/post/requestBody/content/application~1json/schema/properties/local_metadata' + id: + type: string + current_error_events: + type: array + items: + title: AgentEvent + allOf: + - type: object + properties: + id: + type: string + required: + - id + - $ref: '#/paths/~1agents~1%7BagentId%7D~1checkin/post/requestBody/content/application~1json/schema/properties/events/items' + access_api_key: + type: string + status: + type: string + title: AgentStatus + enum: + - offline + - error + - online + - inactive + - warning + default_api_key: + type: string + required: + - type + - active + - enrolled_at + - id + - current_error_events + - status operationId: post-fleet-agents-enroll parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' requestBody: content: application/json: @@ -444,6 +659,7 @@ paths: - TEMPORARY shared_id: type: string + deprecated: true metadata: type: object required: @@ -451,9 +667,9 @@ paths: - user_provided properties: local: - $ref: '#/components/schemas/agent_metadata' + $ref: '#/paths/~1agents~1%7BagentId%7D~1checkin/post/requestBody/content/application~1json/schema/properties/local_metadata' user_provided: - $ref: '#/components/schemas/agent_metadata' + $ref: '#/paths/~1agents~1%7BagentId%7D~1checkin/post/requestBody/content/application~1json/schema/properties/local_metadata' required: - type - metadata @@ -507,7 +723,7 @@ paths: - admin_username - admin_password parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' /enrollment-api-keys: get: summary: Enrollment - List @@ -521,7 +737,7 @@ paths: responses: {} operationId: post-fleet-enrollment-api-keys parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' '/enrollment-api-keys/{keyId}': parameters: - schema: @@ -540,7 +756,7 @@ paths: responses: {} operationId: delete-fleet-enrollment-api-keys-keyId parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' /epm/categories: get: summary: EPM - Categories @@ -578,7 +794,39 @@ paths: schema: type: array items: - $ref: '#/components/schemas/search_result' + title: SearchResult + type: object + properties: + description: + type: string + download: + type: string + icons: + type: string + name: + type: string + path: + type: string + title: + type: string + type: + type: string + version: + type: string + status: + type: string + savedObject: + type: object + required: + - description + - download + - icons + - name + - path + - title + - type + - version + - status operationId: get-epm-list parameters: [] '/epm/packages/{pkgkey}': @@ -595,7 +843,124 @@ paths: allOf: - properties: response: - $ref: '#/components/schemas/package_info' + title: PackageInfo + type: object + properties: + name: + type: string + title: + type: string + version: + type: string + readme: + type: string + description: + type: string + type: + type: string + categories: + type: array + items: + type: string + requirement: + oneOf: + - properties: + kibana: + type: object + properties: + versions: + type: string + - properties: + elasticsearch: + type: object + properties: + versions: + type: string + type: object + screenshots: + type: array + items: + type: object + properties: + src: + type: string + path: + type: string + title: + type: string + size: + type: string + type: + type: string + required: + - src + - path + icons: + type: array + items: + type: string + assets: + type: array + items: + type: string + internal: + type: boolean + format_version: + type: string + data_streams: + type: array + items: + type: object + properties: + title: + type: string + name: + type: string + release: + type: string + ingeset_pipeline: + type: string + vars: + type: array + items: + type: object + properties: + name: + type: string + default: + type: string + required: + - name + - default + type: + type: string + package: + type: string + required: + - title + - name + - release + - ingeset_pipeline + - type + - package + download: + type: string + path: + type: string + removable: + type: boolean + required: + - name + - title + - version + - description + - type + - categories + - requirement + - assets + - format_version + - download + - path - properties: status: type: string @@ -644,7 +1009,7 @@ paths: operationId: post-epm-install-pkgkey description: '' parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' delete: summary: EPM - Packages - Delete tags: [] @@ -672,7 +1037,7 @@ paths: - response operationId: post-epm-delete-pkgkey parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' '/agents/{agentId}': parameters: - schema: @@ -702,14 +1067,14 @@ paths: responses: {} operationId: put-fleet-agents-agentId parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' delete: summary: Fleet - Agent - Delete tags: [] responses: {} operationId: delete-fleet-agents-agentId parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' '/install/{osType}': parameters: - schema: @@ -737,7 +1102,7 @@ paths: items: type: array items: - $ref: '#/components/schemas/package_policy' + $ref: '#/paths/~1package_policies~1%7BpackagePolicyId%7D/get/responses/200/content/application~1json/schema/properties/item' total: type: number page: @@ -760,9 +1125,66 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/new_package_policy' + title: NewPackagePolicy + type: object + description: '' + properties: + enabled: + type: boolean + package: + type: object + properties: + name: + type: string + version: + type: string + title: + type: string + required: + - name + - version + - title + namespace: + type: string + output_id: + type: string + inputs: + type: array + items: + type: object + properties: + type: + type: string + enabled: + type: boolean + processors: + type: array + items: + type: string + streams: + type: array + items: {} + config: + type: object + vars: + type: object + required: + - type + - enabled + - streams + policy_id: + type: string + name: + type: string + description: + type: string + required: + - output_id + - inputs + - policy_id + - name parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' '/package_policies/{packagePolicyId}': get: summary: PackagePolicies - Info @@ -776,7 +1198,21 @@ paths: type: object properties: item: - $ref: '#/components/schemas/package_policy' + title: PackagePolicy + allOf: + - type: object + properties: + id: + type: string + revision: + type: number + inputs: + type: array + items: {} + required: + - id + - revision + - $ref: '#/paths/~1package_policies/post/requestBody/content/application~1json/schema' required: - item operationId: get-packagePolicies-packagePolicyId @@ -793,7 +1229,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/update_package_policy' + title: UpdatePackagePolicy + allOf: + - type: object + properties: + version: + type: string + - $ref: '#/paths/~1package_policies/post/requestBody/content/application~1json/schema' responses: '200': description: OK @@ -803,14 +1245,14 @@ paths: type: object properties: item: - $ref: '#/components/schemas/package_policy' + $ref: '#/paths/~1package_policies~1%7BpackagePolicyId%7D/get/responses/200/content/application~1json/schema/properties/item' sucess: type: boolean required: - item - sucess parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/paths/~1setup/post/parameters/0' /setup: post: summary: Ingest Manager - Setup @@ -836,7 +1278,11 @@ paths: type: string operationId: post-setup parameters: - - $ref: '#/components/parameters/kbn_xsrf' + - schema: + type: string + in: header + name: kbn-xsrf + required: true components: securitySchemes: basicAuth: @@ -852,489 +1298,5 @@ components: type: apiKey in: header description: 'e.g. Authorization: ApiKey base64AccessApiKey' - parameters: - page_size: - name: perPage - in: query - description: The number of items to return - required: false - schema: - type: integer - default: 50 - page_index: - name: page - in: query - required: false - schema: - type: integer - default: 1 - kuery: - name: kuery - in: query - required: false - schema: - type: string - kbn_xsrf: - schema: - type: string - in: header - name: kbn-xsrf - required: true - schemas: - new_agent_policy: - title: NewAgentPolicy - type: object - properties: - name: - type: string - namespace: - type: string - description: - type: string - new_package_policy: - title: NewPackagePolicy - type: object - description: '' - properties: - enabled: - type: boolean - package: - type: object - properties: - name: - type: string - version: - type: string - title: - type: string - required: - - name - - version - - title - namespace: - type: string - output_id: - type: string - inputs: - type: array - items: - type: object - properties: - type: - type: string - enabled: - type: boolean - processors: - type: array - items: - type: string - streams: - type: array - items: {} - config: - type: object - vars: - type: object - required: - - type - - enabled - - streams - policy_id: - type: string - name: - type: string - description: - type: string - required: - - output_id - - inputs - - policy_id - - name - package_policy: - title: PackagePolicy - allOf: - - type: object - properties: - id: - type: string - revision: - type: number - inputs: - type: array - items: {} - required: - - id - - revision - - $ref: '#/components/schemas/new_package_policy' - agent_policy: - allOf: - - $ref: '#/components/schemas/new_agent_policy' - - type: object - properties: - id: - type: string - status: - type: string - enum: - - active - - inactive - packagePolicies: - oneOf: - - items: - type: string - - items: - $ref: '#/components/schemas/package_policy' - type: array - updated_on: - type: string - format: date-time - updated_by: - type: string - revision: - type: number - agents: - type: number - required: - - id - - status - agent_metadata: - title: AgentMetadata - type: object - new_agent_event: - title: NewAgentEvent - type: object - properties: - type: - type: string - enum: - - STATE - - ERROR - - ACTION_RESULT - - ACTION - subtype: - type: string - enum: - - RUNNING - - STARTING - - IN_PROGRESS - - CONFIG - - FAILED - - STOPPING - - STOPPED - - DEGRADED - - DATA_DUMP - - ACKNOWLEDGED - - UNKNOWN - timestamp: - type: string - message: - type: string - payload: - type: string - agent_id: - type: string - policy_id: - type: string - stream_id: - type: string - action_id: - type: string - required: - - type - - subtype - - timestamp - - message - - agent_id - upgrade_agent: - title: UpgradeAgent - oneOf: - - type: object - properties: - version: - type: string - required: - - version - - type: object - properties: - version: - type: string - source_uri: - type: string - required: - - version - bulk_upgrade_agents: - title: BulkUpgradeAgents - oneOf: - - type: object - properties: - version: - type: string - agents: - type: array - items: - type: string - required: - - version - - agents - - type: object - properties: - version: - type: string - source_uri: - type: string - agents: - type: array - items: - type: string - required: - - version - - agents - - type: object - properties: - version: - type: string - source_uri: - type: string - agents: - type: string - required: - - version - - agents - agent_type: - type: string - title: AgentType - enum: - - PERMANENT - - EPHEMERAL - - TEMPORARY - agent_event: - title: AgentEvent - allOf: - - type: object - properties: - id: - type: string - required: - - id - - $ref: '#/components/schemas/new_agent_event' - agent_status: - type: string - title: AgentStatus - enum: - - offline - - error - - online - - inactive - - warning - agent: - title: Agent - type: object - properties: - type: - $ref: '#/components/schemas/agent_type' - active: - type: boolean - enrolled_at: - type: string - unenrolled_at: - type: string - unenrollment_started_at: - type: string - shared_id: - type: string - access_api_key_id: - type: string - default_api_key_id: - type: string - policy_id: - type: string - policy_revision: - type: number - last_checkin: - type: string - user_provided_metadata: - $ref: '#/components/schemas/agent_metadata' - local_metadata: - $ref: '#/components/schemas/agent_metadata' - id: - type: string - current_error_events: - type: array - items: - $ref: '#/components/schemas/agent_event' - access_api_key: - type: string - status: - $ref: '#/components/schemas/agent_status' - default_api_key: - type: string - required: - - type - - active - - enrolled_at - - id - - current_error_events - - status - search_result: - title: SearchResult - type: object - properties: - description: - type: string - download: - type: string - icons: - type: string - name: - type: string - path: - type: string - title: - type: string - type: - type: string - version: - type: string - status: - type: string - savedObject: - type: object - required: - - description - - download - - icons - - name - - path - - title - - type - - version - - status - package_info: - title: PackageInfo - type: object - properties: - name: - type: string - title: - type: string - version: - type: string - readme: - type: string - description: - type: string - type: - type: string - categories: - type: array - items: - type: string - requirement: - oneOf: - - properties: - kibana: - type: object - properties: - versions: - type: string - - properties: - elasticsearch: - type: object - properties: - versions: - type: string - type: object - screenshots: - type: array - items: - type: object - properties: - src: - type: string - path: - type: string - title: - type: string - size: - type: string - type: - type: string - required: - - src - - path - icons: - type: array - items: - type: string - assets: - type: array - items: - type: string - internal: - type: boolean - format_version: - type: string - data_streams: - type: array - items: - type: object - properties: - title: - type: string - name: - type: string - release: - type: string - ingeset_pipeline: - type: string - vars: - type: array - items: - type: object - properties: - name: - type: string - default: - type: string - required: - - name - - default - type: - type: string - package: - type: string - required: - - title - - name - - release - - ingeset_pipeline - - type - - package - download: - type: string - path: - type: string - removable: - type: boolean - required: - - name - - title - - version - - description - - type - - categories - - requirement - - assets - - format_version - - download - - path - update_package_policy: - title: UpdatePackagePolicy - allOf: - - type: object - properties: - version: - type: string - - $ref: '#/components/schemas/new_package_policy' security: - basicAuth: [] diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/agent.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/agent.yaml index df106093a8d8..a2647b71c70c 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/agent.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/agent.yaml @@ -13,6 +13,7 @@ properties: type: string shared_id: type: string + deprecated: true access_api_key_id: type: string default_api_key_id: diff --git a/x-pack/plugins/fleet/common/openapi/paths/agents@enroll.yaml b/x-pack/plugins/fleet/common/openapi/paths/agents@enroll.yaml index a0c1c8c28e72..1946a65e33fd 100644 --- a/x-pack/plugins/fleet/common/openapi/paths/agents@enroll.yaml +++ b/x-pack/plugins/fleet/common/openapi/paths/agents@enroll.yaml @@ -30,6 +30,7 @@ post: - TEMPORARY shared_id: type: string + deprecated: true metadata: type: object required: diff --git a/x-pack/plugins/fleet/common/services/agent_status.ts b/x-pack/plugins/fleet/common/services/agent_status.ts index 4cf35398bab2..c99cfd11b763 100644 --- a/x-pack/plugins/fleet/common/services/agent_status.ts +++ b/x-pack/plugins/fleet/common/services/agent_status.ts @@ -41,7 +41,7 @@ export function getAgentStatus(agent: Agent, now: number = Date.now()): AgentSta } export function buildKueryForEnrollingAgents() { - return `not ${AGENT_SAVED_OBJECT_TYPE}.last_checkin:*`; + return `not (${AGENT_SAVED_OBJECT_TYPE}.last_checkin:*)`; } export function buildKueryForUnenrollingAgents() { @@ -53,7 +53,7 @@ export function buildKueryForOnlineAgents() { } export function buildKueryForErrorAgents() { - return `( ${AGENT_SAVED_OBJECT_TYPE}.last_checkin_status:error or ${AGENT_SAVED_OBJECT_TYPE}.last_checkin_status:degraded )`; + return `${AGENT_SAVED_OBJECT_TYPE}.last_checkin_status:error or ${AGENT_SAVED_OBJECT_TYPE}.last_checkin_status:degraded`; } export function buildKueryForOfflineAgents() { diff --git a/x-pack/plugins/fleet/common/types/index.ts b/x-pack/plugins/fleet/common/types/index.ts index e0827ef7cf40..b023052b1328 100644 --- a/x-pack/plugins/fleet/common/types/index.ts +++ b/x-pack/plugins/fleet/common/types/index.ts @@ -11,6 +11,7 @@ export interface FleetConfigType { registryUrl?: string; registryProxyUrl?: string; agents: { + fleetServerEnabled: boolean; enabled: boolean; tlsCheckDisabled: boolean; pollingRequestTimeout: number; diff --git a/x-pack/plugins/fleet/common/types/models/agent.ts b/x-pack/plugins/fleet/common/types/models/agent.ts index 59fab14f90e6..b59249da2dd3 100644 --- a/x-pack/plugins/fleet/common/types/models/agent.ts +++ b/x-pack/plugins/fleet/common/types/models/agent.ts @@ -130,7 +130,6 @@ interface AgentBase { unenrollment_started_at?: string; upgraded_at?: string; upgrade_started_at?: string; - shared_id?: string; access_api_key_id?: string; default_api_key?: string; default_api_key_id?: string; diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index 75bb2998f2d9..2e29fe148b35 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -80,3 +80,37 @@ export interface FullAgentPolicyKibanaConfig { protocol: string; path?: string; } + +// Generated from Fleet Server schema.json + +/** + * A policy that an Elastic Agent is attached to + */ +export interface FleetServerPolicy { + /** + * Date/time the policy revision was created + */ + '@timestamp'?: string; + /** + * The ID of the policy + */ + policy_id: string; + /** + * The revision index of the policy + */ + revision_idx: number; + /** + * The coordinator index of the policy + */ + coordinator_idx: number; + /** + * The opaque payload. + */ + data: { + [k: string]: unknown; + }; + /** + * True when this policy is the default policy to start Fleet Server + */ + default_fleet_server: boolean; +} diff --git a/x-pack/plugins/fleet/common/types/models/enrollment_api_key.ts b/x-pack/plugins/fleet/common/types/models/enrollment_api_key.ts index f39076ce1027..81dc6889f994 100644 --- a/x-pack/plugins/fleet/common/types/models/enrollment_api_key.ts +++ b/x-pack/plugins/fleet/common/types/models/enrollment_api_key.ts @@ -15,3 +15,31 @@ export interface EnrollmentAPIKey { } export type EnrollmentAPIKeySOAttributes = Omit; + +// Generated + +/** + * An Elastic Agent enrollment API key + */ +export interface FleetServerEnrollmentAPIKey { + /** + * True when the key is active + */ + active?: boolean; + /** + * The unique identifier for the enrollment key, currently xid + */ + api_key_id: string; + /** + * Api key + */ + api_key: string; + /** + * Enrollment key name + */ + name?: string; + policy_id?: string; + expire_at?: string; + created_at?: string; + updated_at?: string; +} diff --git a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts index f758ca0921a0..925ed4b8b163 100644 --- a/x-pack/plugins/fleet/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/common/types/rest_spec/agent.ts @@ -62,7 +62,6 @@ export interface PostAgentCheckinResponse { export interface PostAgentEnrollRequest { body: { type: AgentType; - shared_id?: string; metadata: { local: Record; user_provided: Record; diff --git a/x-pack/plugins/fleet/dev_docs/api/agents_enroll.md b/x-pack/plugins/fleet/dev_docs/api/agents_enroll.md index 977b3029371b..7dd56338b31f 100644 --- a/x-pack/plugins/fleet/dev_docs/api/agents_enroll.md +++ b/x-pack/plugins/fleet/dev_docs/api/agents_enroll.md @@ -13,7 +13,6 @@ Enroll agent ## Request body - `type` (Required, string) Agent type should be one of `EPHEMERAL`, `TEMPORARY`, `PERMANENT` -- `shared_id` (Optional, string) An ID for the agent. - `metadata` (Optional, object) Objects with `local` and `user_provided` properties that contain the metadata for an agent. The metadata is a dictionary of strings (example: `"local": { "os": "macos" }`). ## Response code @@ -68,12 +67,3 @@ The API will return a response with a `401` status code and an error if the enro } ``` -The API will return a response with a `400` status code and an error if you enroll an agent with the same `shared_id` than an already active agent: - -```js -{ - "statusCode": 400, - "error": "BadRequest", - "message": "Impossible to enroll an already active agent" -} -``` diff --git a/x-pack/plugins/fleet/public/applications/fleet/mock/plugin_configuration.ts b/x-pack/plugins/fleet/public/applications/fleet/mock/plugin_configuration.ts index 735c1d11a983..62896289af51 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/mock/plugin_configuration.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/mock/plugin_configuration.ts @@ -13,6 +13,7 @@ export const createConfigurationMock = (): FleetConfigType => { registryProxyUrl: '', agents: { enabled: true, + fleetServerEnabled: false, tlsCheckDisabled: true, pollingRequestTimeout: 1000, maxConcurrentConnections: 100, diff --git a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts index fe5e5fa735b2..8925f3386dfb 100644 --- a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts +++ b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClient } from 'kibana/server'; +import { ElasticsearchClient, SavedObjectsClient } from 'kibana/server'; import * as AgentService from '../services/agents'; export interface AgentUsage { total: number; @@ -13,9 +13,12 @@ export interface AgentUsage { offline: number; } -export const getAgentUsage = async (soClient?: SavedObjectsClient): Promise => { +export const getAgentUsage = async ( + soClient?: SavedObjectsClient, + esClient?: ElasticsearchClient +): Promise => { // TODO: unsure if this case is possible at all. - if (!soClient) { + if (!soClient || !esClient) { return { total: 0, online: 0, @@ -24,7 +27,8 @@ export const getAgentUsage = async (soClient?: SavedObjectsClient): Promise { return core.getStartServices().then(async ([coreStart]) => { const savedObjectsRepo = coreStart.savedObjects.createInternalRepository(); - return new SavedObjectsClient(savedObjectsRepo); + const esClient = coreStart.elasticsearch.client.asInternalUser; + return [new SavedObjectsClient(savedObjectsRepo), esClient]; }); } diff --git a/x-pack/plugins/fleet/server/collectors/register.ts b/x-pack/plugins/fleet/server/collectors/register.ts index 35517e6a7a70..7ec04ca6fee4 100644 --- a/x-pack/plugins/fleet/server/collectors/register.ts +++ b/x-pack/plugins/fleet/server/collectors/register.ts @@ -8,7 +8,7 @@ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { CoreSetup } from 'kibana/server'; import { getIsAgentsEnabled } from './config_collectors'; import { AgentUsage, getAgentUsage } from './agent_collectors'; -import { getInternalSavedObjectsClient } from './helpers'; +import { getInternalClients } from './helpers'; import { PackageUsage, getPackageUsage } from './package_collectors'; import { FleetConfigType } from '..'; @@ -34,10 +34,10 @@ export function registerFleetUsageCollector( type: 'fleet', isReady: () => true, fetch: async () => { - const soClient = await getInternalSavedObjectsClient(core); + const [soClient, esClient] = await getInternalClients(core); return { agents_enabled: getIsAgentsEnabled(config), - agents: await getAgentUsage(soClient), + agents: await getAgentUsage(soClient, esClient), packages: await getPackageUsage(soClient), }; }, diff --git a/x-pack/plugins/fleet/server/constants/index.ts b/x-pack/plugins/fleet/server/constants/index.ts index dbf2fbc362a4..37f8ab041e5a 100644 --- a/x-pack/plugins/fleet/server/constants/index.ts +++ b/x-pack/plugins/fleet/server/constants/index.ts @@ -47,4 +47,7 @@ export { // Defaults DEFAULT_AGENT_POLICY, DEFAULT_OUTPUT, + // Fleet Server index + ENROLLMENT_API_KEYS_INDEX, + AGENTS_INDEX, } from '../../common'; diff --git a/x-pack/plugins/fleet/server/index.ts b/x-pack/plugins/fleet/server/index.ts index 1fe7013944fd..672911ccf6fe 100644 --- a/x-pack/plugins/fleet/server/index.ts +++ b/x-pack/plugins/fleet/server/index.ts @@ -37,6 +37,7 @@ export const config: PluginConfigDescriptor = { registryProxyUrl: schema.maybe(schema.uri({ scheme: ['http', 'https'] })), agents: schema.object({ enabled: schema.boolean({ defaultValue: true }), + fleetServerEnabled: schema.boolean({ defaultValue: false }), tlsCheckDisabled: schema.boolean({ defaultValue: false }), pollingRequestTimeout: schema.number({ defaultValue: AGENT_POLLING_REQUEST_TIMEOUT_MS, diff --git a/x-pack/plugins/fleet/server/mocks.ts b/x-pack/plugins/fleet/server/mocks.ts index 4a897d80acd6..bf659294f514 100644 --- a/x-pack/plugins/fleet/server/mocks.ts +++ b/x-pack/plugins/fleet/server/mocks.ts @@ -4,7 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { loggingSystemMock, savedObjectsServiceMock } from 'src/core/server/mocks'; +import { + elasticsearchServiceMock, + loggingSystemMock, + savedObjectsServiceMock, +} from 'src/core/server/mocks'; import { FleetAppContext } from './plugin'; import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks'; import { securityMock } from '../../security/server/mocks'; @@ -13,6 +17,7 @@ import { AgentPolicyServiceInterface, AgentService } from './services'; export const createAppContextStartContractMock = (): FleetAppContext => { return { + elasticsearch: elasticsearchServiceMock.createStart(), encryptedSavedObjectsStart: encryptedSavedObjectsMock.createStart(), savedObjects: savedObjectsServiceMock.createStartContract(), security: securityMock.createStart(), diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index 8ce17a00acf3..253b614dc228 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -8,6 +8,7 @@ import { first } from 'rxjs/operators'; import { CoreSetup, CoreStart, + ElasticsearchServiceStart, Logger, Plugin, PluginInitializerContext, @@ -80,6 +81,7 @@ import { agentCheckinState } from './services/agents/checkin/state'; import { registerFleetUsageCollector } from './collectors/register'; import { getInstallation } from './services/epm/packages'; import { makeRouterEnforcingSuperuser } from './routes/security'; +import { runFleetServerMigration } from './services/fleet_server_migration'; export interface FleetSetupDeps { licensing: LicensingPluginSetup; @@ -96,6 +98,7 @@ export interface FleetStartDeps { } export interface FleetAppContext { + elasticsearch: ElasticsearchServiceStart; encryptedSavedObjectsStart?: EncryptedSavedObjectsPluginStart; encryptedSavedObjectsSetup?: EncryptedSavedObjectsPluginSetup; security?: SecurityPluginStart; @@ -276,6 +279,7 @@ export class FleetPlugin public async start(core: CoreStart, plugins: FleetStartDeps): Promise { await appContextService.start({ + elasticsearch: core.elasticsearch, encryptedSavedObjectsStart: plugins.encryptedSavedObjects, encryptedSavedObjectsSetup: this.encryptedSavedObjectsSetup, security: plugins.security, @@ -291,6 +295,13 @@ export class FleetPlugin licenseService.start(this.licensing$); agentCheckinState.start(); + const fleetServerEnabled = appContextService.getConfig()?.agents?.fleetServerEnabled; + if (fleetServerEnabled) { + // We need licence to be initialized before using the SO service. + await this.licensing$.pipe(first()).toPromise(); + await runFleetServerMigration(); + } + return { esIndexPatternService: new ESIndexPatternSavedObjectService(), packageService: { diff --git a/x-pack/plugins/fleet/server/routes/agent/acks_handlers.test.ts b/x-pack/plugins/fleet/server/routes/agent/acks_handlers.test.ts index 3d7f5c4a17ad..d775979527af 100644 --- a/x-pack/plugins/fleet/server/routes/agent/acks_handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/agent/acks_handlers.test.ts @@ -6,11 +6,16 @@ import { postAgentAcksHandlerBuilder } from './acks_handlers'; import { + ElasticsearchClient, KibanaResponseFactory, RequestHandlerContext, SavedObjectsClientContract, } from 'kibana/server'; -import { httpServerMock, savedObjectsClientMock } from '../../../../../../src/core/server/mocks'; +import { + elasticsearchServiceMock, + httpServerMock, + savedObjectsClientMock, +} from '../../../../../../src/core/server/mocks'; import { PostAgentAcksResponse } from '../../../common/types/rest_spec'; import { AckEventSchema } from '../../types/models'; import { AcksService } from '../../services/agents'; @@ -45,9 +50,11 @@ describe('test acks schema', () => { describe('test acks handlers', () => { let mockResponse: jest.Mocked; let mockSavedObjectsClient: jest.Mocked; + let mockElasticsearchClient: jest.Mocked; beforeEach(() => { mockSavedObjectsClient = savedObjectsClientMock.create(); + mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockResponse = httpServerMock.createResponseFactory(); }); @@ -81,6 +88,7 @@ describe('test acks handlers', () => { id: 'agent', }), getSavedObjectsClientContract: jest.fn().mockReturnValueOnce(mockSavedObjectsClient), + getElasticsearchClientContract: jest.fn().mockReturnValueOnce(mockElasticsearchClient), saveAgentEvents: jest.fn(), } as jest.Mocked; diff --git a/x-pack/plugins/fleet/server/routes/agent/acks_handlers.ts b/x-pack/plugins/fleet/server/routes/agent/acks_handlers.ts index fb320b01dea9..28cd7e57d653 100644 --- a/x-pack/plugins/fleet/server/routes/agent/acks_handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/acks_handlers.ts @@ -18,6 +18,7 @@ export const postAgentAcksHandlerBuilder = function ( return async (context, request, response) => { try { const soClient = ackService.getSavedObjectsClientContract(request); + const esClient = ackService.getElasticsearchClientContract(); const agent = await ackService.authenticateAgentWithAccessToken(soClient, request); const agentEvents = request.body.events as AgentEvent[]; @@ -33,7 +34,12 @@ export const postAgentAcksHandlerBuilder = function ( }); } - const agentActions = await ackService.acknowledgeAgentActions(soClient, agent, agentEvents); + const agentActions = await ackService.acknowledgeAgentActions( + soClient, + esClient, + agent, + agentEvents + ); if (agentActions.length > 0) { await ackService.saveAgentEvents(soClient, agentEvents); diff --git a/x-pack/plugins/fleet/server/routes/agent/actions_handlers.test.ts b/x-pack/plugins/fleet/server/routes/agent/actions_handlers.test.ts index 2f0884664298..2674e8c5cedd 100644 --- a/x-pack/plugins/fleet/server/routes/agent/actions_handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/agent/actions_handlers.test.ts @@ -6,11 +6,16 @@ import { NewAgentActionSchema } from '../../types/models'; import { + ElasticsearchClient, KibanaResponseFactory, RequestHandlerContext, SavedObjectsClientContract, } from 'kibana/server'; -import { savedObjectsClientMock, httpServerMock } from 'src/core/server/mocks'; +import { + elasticsearchServiceMock, + savedObjectsClientMock, + httpServerMock, +} from 'src/core/server/mocks'; import { ActionsService } from '../../services/agents'; import { AgentAction } from '../../../common/types/models'; import { postNewAgentActionHandlerBuilder } from './actions_handlers'; @@ -41,9 +46,11 @@ describe('test actions handlers schema', () => { describe('test actions handlers', () => { let mockResponse: jest.Mocked; let mockSavedObjectsClient: jest.Mocked; + let mockElasticsearchClient: jest.Mocked; beforeEach(() => { mockSavedObjectsClient = savedObjectsClientMock.create(); + mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockResponse = httpServerMock.createResponseFactory(); }); @@ -84,6 +91,11 @@ describe('test actions handlers', () => { savedObjects: { client: mockSavedObjectsClient, }, + elasticsearch: { + client: { + asInternalUser: mockElasticsearchClient, + }, + }, }, } as unknown) as RequestHandlerContext, mockRequest, diff --git a/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts b/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts index 64a7795cc9da..04b92296439c 100644 --- a/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/actions_handlers.ts @@ -23,8 +23,9 @@ export const postNewAgentActionHandlerBuilder = function ( return async (context, request, response) => { try { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asInternalUser; - const agent = await actionsService.getAgent(soClient, request.params.agentId); + const agent = await actionsService.getAgent(soClient, esClient, request.params.agentId); const newAgentAction = request.body.action; diff --git a/x-pack/plugins/fleet/server/routes/agent/handlers.ts b/x-pack/plugins/fleet/server/routes/agent/handlers.ts index a867196f9762..ace18e10115d 100644 --- a/x-pack/plugins/fleet/server/routes/agent/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent/handlers.ts @@ -39,8 +39,10 @@ export const getAgentHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; + try { - const agent = await AgentService.getAgent(soClient, request.params.agentId); + const agent = await AgentService.getAgent(soClient, esClient, request.params.agentId); const body: GetOneAgentResponse = { item: { @@ -98,8 +100,10 @@ export const deleteAgentHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; + try { - await AgentService.deleteAgent(soClient, request.params.agentId); + await AgentService.deleteAgent(soClient, esClient, request.params.agentId); const body = { action: 'deleted', @@ -124,11 +128,13 @@ export const updateAgentHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; + try { await AgentService.updateAgent(soClient, request.params.agentId, { userProvidedMetatada: request.body.user_provided_metadata, }); - const agent = await AgentService.getAgent(soClient, request.params.agentId); + const agent = await AgentService.getAgent(soClient, esClient, request.params.agentId); const body = { item: { @@ -156,6 +162,7 @@ export const postAgentCheckinHandler: RequestHandler< > = async (context, request, response) => { try { const soClient = appContextService.getInternalUserSOClient(request); + const esClient = appContextService.getInternalUserESClient(); const agent = await AgentService.authenticateAgentWithAccessToken(soClient, request); const abortController = new AbortController(); request.events.aborted$.subscribe(() => { @@ -164,6 +171,7 @@ export const postAgentCheckinHandler: RequestHandler< const signal = abortController.signal; const { actions } = await AgentService.agentCheckin( soClient, + esClient, agent, { events: request.body.events || [], @@ -212,8 +220,7 @@ export const postAgentEnrollHandler: RequestHandler< { userProvided: request.body.metadata.user_provided, local: request.body.metadata.local, - }, - request.body.shared_id + } ); const body: PostAgentEnrollResponse = { action: 'created', @@ -234,8 +241,10 @@ export const getAgentsHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; + try { - const { agents, total, page, perPage } = await AgentService.listAgents(soClient, { + const { agents, total, page, perPage } = await AgentService.listAgents(soClient, esClient, { page: request.query.page, perPage: request.query.perPage, showInactive: request.query.showInactive, @@ -243,7 +252,7 @@ export const getAgentsHandler: RequestHandler< kuery: request.query.kuery, }); const totalInactive = request.query.showInactive - ? await AgentService.countInactiveAgents(soClient, { + ? await AgentService.countInactiveAgents(soClient, esClient, { kuery: request.query.kuery, }) : 0; @@ -270,8 +279,14 @@ export const putAgentsReassignHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asInternalUser; try { - await AgentService.reassignAgent(soClient, request.params.agentId, request.body.policy_id); + await AgentService.reassignAgent( + soClient, + esClient, + request.params.agentId, + request.body.policy_id + ); const body: PutAgentReassignResponse = {}; return response.ok({ body }); @@ -293,16 +308,19 @@ export const postBulkAgentsReassignHandler: RequestHandler< } const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asInternalUser; try { // Reassign by array of IDs const result = Array.isArray(request.body.agents) ? await AgentService.reassignAgents( soClient, + esClient, { agentIds: request.body.agents }, request.body.policy_id ) : await AgentService.reassignAgents( soClient, + esClient, { kuery: request.body.agents }, request.body.policy_id ); @@ -326,10 +344,13 @@ export const getAgentStatusForAgentPolicyHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; + try { // TODO change path const results = await AgentService.getAgentStatusForAgentPolicy( soClient, + esClient, request.query.policyId, request.query.kuery ); diff --git a/x-pack/plugins/fleet/server/routes/agent/index.ts b/x-pack/plugins/fleet/server/routes/agent/index.ts index 54a30fbc9320..c088349a995a 100644 --- a/x-pack/plugins/fleet/server/routes/agent/index.ts +++ b/x-pack/plugins/fleet/server/routes/agent/index.ts @@ -294,6 +294,9 @@ export const registerElasticAgentRoutes = (router: IRouter, config: FleetConfigT getSavedObjectsClientContract: appContextService.getInternalUserSOClient.bind( appContextService ), + getElasticsearchClientContract: appContextService.getInternalUserESClient.bind( + appContextService + ), saveAgentEvents: AgentService.saveAgentEvents, }) ); @@ -313,6 +316,9 @@ export const registerElasticAgentRoutes = (router: IRouter, config: FleetConfigT getSavedObjectsClientContract: appContextService.getInternalUserSOClient.bind( appContextService ), + getElasticsearchClientContract: appContextService.getInternalUserESClient.bind( + appContextService + ), saveAgentEvents: AgentService.saveAgentEvents, }) ); diff --git a/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts b/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts index 861d7c45c6f0..41c183789f9f 100644 --- a/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/unenroll_handler.ts @@ -18,9 +18,10 @@ export const postAgentUnenrollHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asInternalUser; try { if (request.body?.force === true) { - await AgentService.forceUnenrollAgent(soClient, request.params.agentId); + await AgentService.forceUnenrollAgent(soClient, esClient, request.params.agentId); } else { await AgentService.unenrollAgent(soClient, request.params.agentId); } @@ -44,14 +45,15 @@ export const postBulkAgentsUnenrollHandler: RequestHandler< }); } const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asInternalUser; const unenrollAgents = request.body?.force === true ? AgentService.forceUnenrollAgents : AgentService.unenrollAgents; try { if (Array.isArray(request.body.agents)) { - await unenrollAgents(soClient, { agentIds: request.body.agents }); + await unenrollAgents(soClient, esClient, { agentIds: request.body.agents }); } else { - await unenrollAgents(soClient, { kuery: request.body.agents }); + await unenrollAgents(soClient, esClient, { kuery: request.body.agents }); } const body: PostBulkAgentUnenrollResponse = {}; diff --git a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts index 93e6609167a2..7b068674d382 100644 --- a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts @@ -83,6 +83,7 @@ export const postBulkAgentsUpgradeHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asInternalUser; const { version, source_uri: sourceUri, agents, force } = request.body; const kibanaVersion = appContextService.getKibanaVersion(); try { @@ -98,14 +99,14 @@ export const postBulkAgentsUpgradeHandler: RequestHandler< try { if (Array.isArray(agents)) { - await AgentService.sendUpgradeAgentsActions(soClient, { + await AgentService.sendUpgradeAgentsActions(soClient, esClient, { agentIds: agents, sourceUri, version, force, }); } else { - await AgentService.sendUpgradeAgentsActions(soClient, { + await AgentService.sendUpgradeAgentsActions(soClient, esClient, { kuery: agents, sourceUri, version, diff --git a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts index 25aaf5f9a465..8f7fd4427f58 100644 --- a/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/agent_policy/handlers.ts @@ -39,6 +39,7 @@ export const getAgentPoliciesHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; const { full: withPackagePolicies = false, ...restOfQuery } = request.query; try { const { items, total, page, perPage } = await agentPolicyService.list(soClient, { @@ -55,7 +56,7 @@ export const getAgentPoliciesHandler: RequestHandler< await bluebird.map( items, (agentPolicy: GetAgentPoliciesResponseItem) => - listAgents(soClient, { + listAgents(soClient, esClient, { showInactive: false, perPage: 0, page: 1, @@ -100,6 +101,7 @@ export const createAgentPolicyHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser; const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; const withSysMonitoring = request.query.sys_monitoring ?? false; @@ -109,7 +111,7 @@ export const createAgentPolicyHandler: RequestHandler< AgentPolicy, NewPackagePolicy | undefined >([ - agentPolicyService.create(soClient, request.body, { + agentPolicyService.create(soClient, esClient, request.body, { user, }), // If needed, retrieve System package information and build a new package policy for the system package @@ -126,7 +128,7 @@ export const createAgentPolicyHandler: RequestHandler< if (withSysMonitoring && newSysPackagePolicy !== undefined && agentPolicy !== undefined) { newSysPackagePolicy.policy_id = agentPolicy.id; newSysPackagePolicy.namespace = agentPolicy.namespace; - await packagePolicyService.create(soClient, callCluster, newSysPackagePolicy, { + await packagePolicyService.create(soClient, esClient, callCluster, newSysPackagePolicy, { user, bumpRevision: false, }); @@ -152,10 +154,12 @@ export const updateAgentPolicyHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; const user = await appContextService.getSecurity()?.authc.getCurrentUser(request); try { const agentPolicy = await agentPolicyService.update( soClient, + esClient, request.params.agentPolicyId, request.body, { @@ -177,10 +181,12 @@ export const copyAgentPolicyHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; const user = await appContextService.getSecurity()?.authc.getCurrentUser(request); try { const agentPolicy = await agentPolicyService.copy( soClient, + esClient, request.params.agentPolicyId, request.body, { @@ -203,9 +209,11 @@ export const deleteAgentPoliciesHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; try { const body: DeleteAgentPolicyResponse = await agentPolicyService.delete( soClient, + esClient, request.body.agentPolicyId ); return response.ok({ diff --git a/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts b/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts index afecd7bd7d82..4f54b4e155ea 100644 --- a/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts +++ b/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts @@ -26,12 +26,18 @@ export const getEnrollmentApiKeysHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; + try { - const { items, total, page, perPage } = await APIKeyService.listEnrollmentApiKeys(soClient, { - page: request.query.page, - perPage: request.query.perPage, - kuery: request.query.kuery, - }); + const { items, total, page, perPage } = await APIKeyService.listEnrollmentApiKeys( + soClient, + esClient, + { + page: request.query.page, + perPage: request.query.perPage, + kuery: request.query.kuery, + } + ); const body: GetEnrollmentAPIKeysResponse = { list: items, total, page, perPage }; return response.ok({ body }); @@ -45,8 +51,9 @@ export const postEnrollmentApiKeyHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; try { - const apiKey = await APIKeyService.generateEnrollmentAPIKey(soClient, { + const apiKey = await APIKeyService.generateEnrollmentAPIKey(soClient, esClient, { name: request.body.name, expiration: request.body.expiration, agentPolicyId: request.body.policy_id, @@ -64,8 +71,9 @@ export const deleteEnrollmentApiKeyHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; try { - await APIKeyService.deleteEnrollmentApiKey(soClient, request.params.keyId); + await APIKeyService.deleteEnrollmentApiKey(soClient, esClient, request.params.keyId); const body: DeleteEnrollmentAPIKeyResponse = { action: 'deleted' }; @@ -84,8 +92,13 @@ export const getOneEnrollmentApiKeyHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; try { - const apiKey = await APIKeyService.getEnrollmentAPIKey(soClient, request.params.keyId); + const apiKey = await APIKeyService.getEnrollmentAPIKey( + soClient, + esClient, + request.params.keyId + ); const body: GetOneEnrollmentAPIKeyResponse = { item: apiKey }; return response.ok({ body }); diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts index f9fd6047baa7..90a06563efce 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts @@ -25,7 +25,7 @@ jest.mock('../../services/package_policy', (): { compilePackagePolicyInputs: jest.fn((packageInfo, dataInputs) => Promise.resolve(dataInputs)), buildPackagePolicyFromPackage: jest.fn(), bulkCreate: jest.fn(), - create: jest.fn((soClient, callCluster, newData) => + create: jest.fn((soClient, esClient, callCluster, newData) => Promise.resolve({ ...newData, inputs: newData.inputs.map((input) => ({ @@ -201,7 +201,7 @@ describe('When calling package policy', () => { ); await routeHandler(context, request, response); expect(response.ok).toHaveBeenCalled(); - expect(packagePolicyServiceMock.create.mock.calls[0][2]).toEqual({ + expect(packagePolicyServiceMock.create.mock.calls[0][3]).toEqual({ policy_id: 'a5ca00c0-b30c-11ea-9732-1bb05811278c', description: '', enabled: true, diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts index be14970de3e0..bef33c1c98b6 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts @@ -74,6 +74,7 @@ export const createPackagePolicyHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser; const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; let newData = { ...request.body }; @@ -86,9 +87,15 @@ export const createPackagePolicyHandler: RequestHandler< ); // Create package policy - const packagePolicy = await packagePolicyService.create(soClient, callCluster, newData, { - user, - }); + const packagePolicy = await packagePolicyService.create( + soClient, + esClient, + callCluster, + newData, + { + user, + } + ); const body: CreatePackagePolicyResponse = { item: packagePolicy }; return response.ok({ body, @@ -110,6 +117,7 @@ export const updatePackagePolicyHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; const packagePolicy = await packagePolicyService.get(soClient, request.params.packagePolicyId); @@ -131,6 +139,7 @@ export const updatePackagePolicyHandler: RequestHandler< const updatedPackagePolicy = await packagePolicyService.update( soClient, + esClient, request.params.packagePolicyId, { ...newData, package: pkg, inputs }, { user } @@ -149,10 +158,12 @@ export const deletePackagePolicyHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; const user = (await appContextService.getSecurity()?.authc.getCurrentUser(request)) || undefined; try { const body: DeletePackagePoliciesResponse = await packagePolicyService.delete( soClient, + esClient, request.body.packagePolicyIds, { user } ); diff --git a/x-pack/plugins/fleet/server/routes/settings/index.ts b/x-pack/plugins/fleet/server/routes/settings/index.ts index 4eeff629dc22..6f63043c12a2 100644 --- a/x-pack/plugins/fleet/server/routes/settings/index.ts +++ b/x-pack/plugins/fleet/server/routes/settings/index.ts @@ -36,10 +36,12 @@ export const putSettingsHandler: RequestHandler< TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; const user = await appContextService.getSecurity()?.authc.getCurrentUser(request); + try { const settings = await settingsService.saveSettings(soClient, request.body); - await agentPolicyService.bumpAllAgentPolicies(soClient, { + await agentPolicyService.bumpAllAgentPolicies(soClient, esClient, { user: user || undefined, }); const body = { diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.ts index cafccd1895d1..cf0967045f15 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.ts @@ -59,9 +59,10 @@ export const createFleetSetupHandler: RequestHandler< > = async (context, request, response) => { try { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser; - await setupIngestManager(soClient, callCluster); - await setupFleet(soClient, callCluster, { + await setupIngestManager(soClient, esClient, callCluster); + await setupFleet(soClient, esClient, callCluster, { forceRecreate: request.body?.forceRecreate ?? false, }); @@ -75,11 +76,12 @@ export const createFleetSetupHandler: RequestHandler< export const FleetSetupHandler: RequestHandler = async (context, request, response) => { const soClient = context.core.savedObjects.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; const callCluster = context.core.elasticsearch.legacy.client.callAsCurrentUser; try { const body: PostIngestSetupResponse = { isInitialized: true }; - await setupIngestManager(soClient, callCluster); + await setupIngestManager(soClient, esClient, callCluster); return response.ok({ body, }); diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 20bbee2b1c79..dcc686e565b8 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -28,6 +28,7 @@ import { migrateSettingsToV7100, migrateAgentActionToV7100, } from './migrations/to_v7_10_0'; +import { migrateAgentToV7120 } from './migrations/to_v7_12_0'; /* * Saved object types and mappings @@ -67,7 +68,6 @@ const getSavedObjectTypes = ( }, mappings: { properties: { - shared_id: { type: 'keyword' }, type: { type: 'keyword' }, active: { type: 'boolean' }, enrolled_at: { type: 'date' }, @@ -93,6 +93,7 @@ const getSavedObjectTypes = ( }, migrations: { '7.10.0': migrateAgentToV7100, + '7.12.0': migrateAgentToV7120, }, }, [AGENT_ACTION_SAVED_OBJECT_TYPE]: { @@ -385,7 +386,6 @@ export function registerEncryptedSavedObjects( type: AGENT_SAVED_OBJECT_TYPE, attributesToEncrypt: new Set(['default_api_key']), attributesToExcludeFromAAD: new Set([ - 'shared_id', 'type', 'active', 'enrolled_at', diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_12_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_12_0.ts new file mode 100644 index 000000000000..841e56a60091 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_12_0.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 { SavedObjectMigrationFn } from 'kibana/server'; +import { Agent } from '../../types'; + +export const migrateAgentToV7120: SavedObjectMigrationFn = ( + agentDoc +) => { + delete agentDoc.attributes.shared_id; + + return agentDoc; +}; diff --git a/x-pack/plugins/fleet/server/services/agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policy.test.ts index f9a8b63bb83a..de3647bec016 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { savedObjectsClientMock } from 'src/core/server/mocks'; +import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; import { agentPolicyService } from './agent_policy'; import { agentPolicyUpdateEventHandler } from './agent_policy_update'; import { Output } from '../types'; @@ -78,7 +78,9 @@ describe('agent policy', () => { revision: 1, monitoring_enabled: ['metrics'], }); - await agentPolicyService.bumpRevision(soClient, 'agent-policy'); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + + await agentPolicyService.bumpRevision(soClient, esClient, 'agent-policy'); expect(agentPolicyUpdateEventHandler).toHaveBeenCalledTimes(1); }); @@ -90,7 +92,9 @@ describe('agent policy', () => { revision: 1, monitoring_enabled: ['metrics'], }); - await agentPolicyService.bumpAllAgentPolicies(soClient); + const esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + + await agentPolicyService.bumpAllAgentPolicies(soClient, esClient, undefined); expect(agentPolicyUpdateEventHandler).toHaveBeenCalledTimes(1); }); diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 0fd41d074eff..81d98823b526 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -5,7 +5,12 @@ */ import { uniq } from 'lodash'; import { safeLoad } from 'js-yaml'; -import { SavedObjectsClientContract, SavedObjectsBulkUpdateResponse } from 'src/core/server'; +import uuid from 'uuid/v4'; +import { + ElasticsearchClient, + SavedObjectsClientContract, + SavedObjectsBulkUpdateResponse, +} from 'src/core/server'; import { AuthenticatedUser } from '../../../security/server'; import { DEFAULT_AGENT_POLICY, @@ -26,6 +31,8 @@ import { agentPolicyStatuses, storedPackagePoliciesToAgentInputs, dataTypes, + FleetServerPolicy, + AGENT_POLICY_INDEX, } from '../../common'; import { AgentPolicyNameExistsError } from '../errors'; import { createAgentPolicyAction, listAgents } from './agents'; @@ -36,20 +43,23 @@ import { getSettings } from './settings'; import { normalizeKuery, escapeSearchQueryPhrase } from './saved_object'; import { getFullAgentPolicyKibanaConfig } from '../../common/services/full_agent_policy_kibana_config'; import { isAgentsSetup } from './agents/setup'; +import { appContextService } from './app_context'; const SAVED_OBJECT_TYPE = AGENT_POLICY_SAVED_OBJECT_TYPE; class AgentPolicyService { private triggerAgentPolicyUpdatedEvent = async ( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, action: 'created' | 'updated' | 'deleted', agentPolicyId: string ) => { - return agentPolicyUpdateEventHandler(soClient, action, agentPolicyId); + return agentPolicyUpdateEventHandler(soClient, esClient, action, agentPolicyId); }; private async _update( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, id: string, agentPolicy: Partial, user?: AuthenticatedUser, @@ -78,14 +88,15 @@ class AgentPolicyService { }); if (options.bumpRevision) { - await this.triggerAgentPolicyUpdatedEvent(soClient, 'updated', id); + await this.triggerAgentPolicyUpdatedEvent(soClient, esClient, 'updated', id); } return (await this.get(soClient, id)) as AgentPolicy; } public async ensureDefaultAgentPolicy( - soClient: SavedObjectsClientContract + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient ): Promise<{ created: boolean; defaultAgentPolicy: AgentPolicy; @@ -103,7 +114,7 @@ class AgentPolicyService { return { created: true, - defaultAgentPolicy: await this.create(soClient, newDefaultAgentPolicy), + defaultAgentPolicy: await this.create(soClient, esClient, newDefaultAgentPolicy), }; } @@ -118,6 +129,7 @@ class AgentPolicyService { public async create( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, agentPolicy: NewAgentPolicy, options?: { id?: string; user?: AuthenticatedUser } ): Promise { @@ -134,7 +146,7 @@ class AgentPolicyService { ); if (!agentPolicy.is_default) { - await this.triggerAgentPolicyUpdatedEvent(soClient, 'created', newSo.id); + await this.triggerAgentPolicyUpdatedEvent(soClient, esClient, 'created', newSo.id); } return { id: newSo.id, ...newSo.attributes }; @@ -244,6 +256,7 @@ class AgentPolicyService { public async update( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, id: string, agentPolicy: Partial, options?: { user?: AuthenticatedUser } @@ -254,11 +267,12 @@ class AgentPolicyService { name: agentPolicy.name, }); } - return this._update(soClient, id, agentPolicy, options?.user); + return this._update(soClient, esClient, id, agentPolicy, options?.user); } public async copy( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, id: string, newAgentPolicyProps: Pick, options?: { user?: AuthenticatedUser } @@ -272,6 +286,7 @@ class AgentPolicyService { const { namespace, monitoring_enabled } = baseAgentPolicy; const newAgentPolicy = await this.create( soClient, + esClient, { namespace, monitoring_enabled, @@ -288,10 +303,16 @@ class AgentPolicyService { return newPackagePolicy; } ); - await packagePolicyService.bulkCreate(soClient, newPackagePolicies, newAgentPolicy.id, { - ...options, - bumpRevision: false, - }); + await packagePolicyService.bulkCreate( + soClient, + esClient, + newPackagePolicies, + newAgentPolicy.id, + { + ...options, + bumpRevision: false, + } + ); } // Get updated agent policy @@ -307,15 +328,18 @@ class AgentPolicyService { public async bumpRevision( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, id: string, options?: { user?: AuthenticatedUser } ): Promise { - const res = await this._update(soClient, id, {}, options?.user); + const res = await this._update(soClient, esClient, id, {}, options?.user); return res; } + public async bumpAllAgentPolicies( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, options?: { user?: AuthenticatedUser } ): Promise>> { const currentPolicies = await soClient.find({ @@ -335,7 +359,7 @@ class AgentPolicyService { await Promise.all( currentPolicies.saved_objects.map((policy) => - this.triggerAgentPolicyUpdatedEvent(soClient, 'updated', policy.id) + this.triggerAgentPolicyUpdatedEvent(soClient, esClient, 'updated', policy.id) ) ); @@ -344,6 +368,7 @@ class AgentPolicyService { public async assignPackagePolicies( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, id: string, packagePolicyIds: string[], options: { user?: AuthenticatedUser; bumpRevision: boolean } = { bumpRevision: true } @@ -356,6 +381,7 @@ class AgentPolicyService { return await this._update( soClient, + esClient, id, { package_policies: uniq( @@ -369,6 +395,7 @@ class AgentPolicyService { public async unassignPackagePolicies( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, id: string, packagePolicyIds: string[], options?: { user?: AuthenticatedUser } @@ -381,6 +408,7 @@ class AgentPolicyService { return await this._update( soClient, + esClient, id, { package_policies: uniq( @@ -409,6 +437,7 @@ class AgentPolicyService { public async delete( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, id: string ): Promise { const agentPolicy = await this.get(soClient, id, false); @@ -418,12 +447,12 @@ class AgentPolicyService { const { defaultAgentPolicy: { id: defaultAgentPolicyId }, - } = await this.ensureDefaultAgentPolicy(soClient); + } = await this.ensureDefaultAgentPolicy(soClient, esClient); if (id === defaultAgentPolicyId) { throw new Error('The default agent policy cannot be deleted'); } - const { total } = await listAgents(soClient, { + const { total } = await listAgents(soClient, esClient, { showInactive: false, perPage: 0, page: 1, @@ -435,12 +464,17 @@ class AgentPolicyService { } if (agentPolicy.package_policies && agentPolicy.package_policies.length) { - await packagePolicyService.delete(soClient, agentPolicy.package_policies as string[], { - skipUnassignFromAgentPolicies: true, - }); + await packagePolicyService.delete( + soClient, + esClient, + agentPolicy.package_policies as string[], + { + skipUnassignFromAgentPolicies: true, + } + ); } await soClient.delete(SAVED_OBJECT_TYPE, id); - await this.triggerAgentPolicyUpdatedEvent(soClient, 'deleted', id); + await this.triggerAgentPolicyUpdatedEvent(soClient, esClient, 'deleted', id); return { id, name: agentPolicy.name, @@ -450,6 +484,19 @@ class AgentPolicyService { public async createFleetPolicyChangeAction( soClient: SavedObjectsClientContract, agentPolicyId: string + ) { + return appContextService.getConfig()?.agents.fleetServerEnabled + ? this.createFleetPolicyChangeFleetServer( + soClient, + appContextService.getInternalUserESClient(), + agentPolicyId + ) + : this.createFleetPolicyChangeActionSO(soClient, agentPolicyId); + } + + public async createFleetPolicyChangeActionSO( + soClient: SavedObjectsClientContract, + agentPolicyId: string ) { // If Agents is not setup skip the creation of POLICY_CHANGE agent actions // the action will be created during the fleet setup @@ -478,6 +525,38 @@ class AgentPolicyService { }); } + public async createFleetPolicyChangeFleetServer( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + agentPolicyId: string + ) { + // If Agents is not setup skip the creation of POLICY_CHANGE agent actions + // the action will be created during the fleet setup + if (!(await isAgentsSetup(soClient))) { + return; + } + const policy = await agentPolicyService.getFullAgentPolicy(soClient, agentPolicyId); + if (!policy || !policy.revision) { + return; + } + + const fleetServerPolicy: FleetServerPolicy = { + '@timestamp': new Date().toISOString(), + revision_idx: policy.revision, + coordinator_idx: 0, + data: (policy as unknown) as FleetServerPolicy['data'], + policy_id: policy.id, + default_fleet_server: false, + }; + + await esClient.create({ + index: AGENT_POLICY_INDEX, + body: fleetServerPolicy, + id: uuid(), + refresh: 'wait_for', + }); + } + public async getFullAgentPolicy( soClient: SavedObjectsClientContract, id: string, diff --git a/x-pack/plugins/fleet/server/services/agent_policy_update.ts b/x-pack/plugins/fleet/server/services/agent_policy_update.ts index fe06de765bbf..32c041b44681 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy_update.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy_update.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, SavedObjectsClientContract } from 'src/core/server'; +import { ElasticsearchClient, KibanaRequest, SavedObjectsClientContract } from 'src/core/server'; import { generateEnrollmentAPIKey, deleteEnrollmentApiKeyForAgentPolicyId } from './api_keys'; import { isAgentsSetup, unenrollForAgentPolicyId } from './agents'; import { agentPolicyService } from './agent_policy'; @@ -27,6 +27,7 @@ const fakeRequest = ({ export async function agentPolicyUpdateEventHandler( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, action: string, agentPolicyId: string ) { @@ -40,7 +41,7 @@ export async function agentPolicyUpdateEventHandler( const internalSoClient = appContextService.getInternalUserSOClient(fakeRequest); if (action === 'created') { - await generateEnrollmentAPIKey(soClient, { + await generateEnrollmentAPIKey(soClient, esClient, { agentPolicyId, }); await agentPolicyService.createFleetPolicyChangeAction(internalSoClient, agentPolicyId); @@ -51,7 +52,7 @@ export async function agentPolicyUpdateEventHandler( } if (action === 'deleted') { - await unenrollForAgentPolicyId(soClient, agentPolicyId); + await unenrollForAgentPolicyId(soClient, esClient, agentPolicyId); await deleteEnrollmentApiKeyForAgentPolicyId(soClient, agentPolicyId); } } diff --git a/x-pack/plugins/fleet/server/services/agents/acks.test.ts b/x-pack/plugins/fleet/server/services/agents/acks.test.ts index 4b09fb93e01a..1626df4fd02c 100644 --- a/x-pack/plugins/fleet/server/services/agents/acks.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/acks.test.ts @@ -5,7 +5,7 @@ */ import Boom from '@hapi/boom'; import { SavedObjectsBulkResponse } from 'kibana/server'; -import { savedObjectsClientMock } from 'src/core/server/mocks'; +import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; import { Agent, @@ -19,6 +19,7 @@ import { acknowledgeAgentActions } from './acks'; describe('test agent acks services', () => { it('should succeed on valid and matched actions', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockSavedObjectsClient.bulkGet.mockReturnValue( Promise.resolve({ @@ -41,6 +42,7 @@ describe('test agent acks services', () => { await acknowledgeAgentActions( mockSavedObjectsClient, + mockElasticsearchClient, ({ id: 'id', type: AGENT_TYPE_PERMANENT, @@ -59,6 +61,7 @@ describe('test agent acks services', () => { it('should update config field on the agent if a policy change is acknowledged with an agent without policy', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const actionAttributes = { type: 'POLICY_CHANGE', @@ -85,6 +88,7 @@ describe('test agent acks services', () => { await acknowledgeAgentActions( mockSavedObjectsClient, + mockElasticsearchClient, ({ id: 'id', type: AGENT_TYPE_PERMANENT, @@ -118,6 +122,7 @@ describe('test agent acks services', () => { it('should update config field on the agent if a policy change is acknowledged with a higher revision than the agent one', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const actionAttributes = { type: 'POLICY_CHANGE', @@ -144,6 +149,7 @@ describe('test agent acks services', () => { await acknowledgeAgentActions( mockSavedObjectsClient, + mockElasticsearchClient, ({ id: 'id', type: AGENT_TYPE_PERMANENT, @@ -178,6 +184,7 @@ describe('test agent acks services', () => { it('should not update config field on the agent if a policy change is acknowledged with a lower revision than the agent one', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; const actionAttributes = { type: 'POLICY_CHANGE', @@ -204,6 +211,7 @@ describe('test agent acks services', () => { await acknowledgeAgentActions( mockSavedObjectsClient, + mockElasticsearchClient, ({ id: 'id', type: AGENT_TYPE_PERMANENT, @@ -226,6 +234,7 @@ describe('test agent acks services', () => { it('should not update config field on the agent if a policy change for an old revision is acknowledged', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockSavedObjectsClient.bulkGet.mockReturnValue( Promise.resolve({ @@ -249,6 +258,7 @@ describe('test agent acks services', () => { await acknowledgeAgentActions( mockSavedObjectsClient, + mockElasticsearchClient, ({ id: 'id', type: AGENT_TYPE_PERMANENT, @@ -271,6 +281,7 @@ describe('test agent acks services', () => { it('should fail for actions that cannot be found on agent actions list', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockSavedObjectsClient.bulkGet.mockReturnValue( Promise.resolve({ saved_objects: [ @@ -288,6 +299,7 @@ describe('test agent acks services', () => { try { await acknowledgeAgentActions( mockSavedObjectsClient, + mockElasticsearchClient, ({ id: 'id', type: AGENT_TYPE_PERMANENT, @@ -310,6 +322,7 @@ describe('test agent acks services', () => { it('should fail for events that have types not in the allowed acknowledgement type list', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockSavedObjectsClient.bulkGet.mockReturnValue( Promise.resolve({ @@ -333,6 +346,7 @@ describe('test agent acks services', () => { try { await acknowledgeAgentActions( mockSavedObjectsClient, + mockElasticsearchClient, ({ id: 'id', type: AGENT_TYPE_PERMANENT, diff --git a/x-pack/plugins/fleet/server/services/agents/acks.ts b/x-pack/plugins/fleet/server/services/agents/acks.ts index 814251345788..fab6dae0d23d 100644 --- a/x-pack/plugins/fleet/server/services/agents/acks.ts +++ b/x-pack/plugins/fleet/server/services/agents/acks.ts @@ -5,6 +5,7 @@ */ import { + ElasticsearchClient, KibanaRequest, SavedObjectsBulkCreateObject, SavedObjectsBulkResponse, @@ -40,6 +41,7 @@ const actionCache = new LRU({ export async function acknowledgeAgentActions( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, agent: Agent, agentEvents: AgentEvent[] ): Promise { @@ -79,7 +81,7 @@ export async function acknowledgeAgentActions( const isAgentUnenrolled = actions.some((action) => action.type === 'UNENROLL'); if (isAgentUnenrolled) { - await forceUnenrollAgent(soClient, agent.id); + await forceUnenrollAgent(soClient, esClient, agent.id); } const upgradeAction = actions.find((action) => action.type === 'UPGRADE'); @@ -196,6 +198,7 @@ export async function saveAgentEvents( export interface AcksService { acknowledgeAgentActions: ( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, agent: Agent, actionIds: AgentEvent[] ) => Promise; @@ -207,6 +210,8 @@ export interface AcksService { getSavedObjectsClientContract: (kibanaRequest: KibanaRequest) => SavedObjectsClientContract; + getElasticsearchClientContract: () => ElasticsearchClient; + saveAgentEvents: ( soClient: SavedObjectsClientContract, events: AgentEvent[] diff --git a/x-pack/plugins/fleet/server/services/agents/actions.ts b/x-pack/plugins/fleet/server/services/agents/actions.ts index f2cdd1f31e69..cb893a8b88c9 100644 --- a/x-pack/plugins/fleet/server/services/agents/actions.ts +++ b/x-pack/plugins/fleet/server/services/agents/actions.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server'; import { Agent, AgentAction, @@ -307,7 +307,11 @@ export async function getLatestConfigChangeAction( } export interface ActionsService { - getAgent: (soClient: SavedObjectsClientContract, agentId: string) => Promise; + getAgent: ( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + agentId: string + ) => Promise; createAgentAction: ( soClient: SavedObjectsClientContract, diff --git a/x-pack/plugins/fleet/server/services/agents/checkin/index.ts b/x-pack/plugins/fleet/server/services/agents/checkin/index.ts index 19a5c2dc0876..d9e7d9889efd 100644 --- a/x-pack/plugins/fleet/server/services/agents/checkin/index.ts +++ b/x-pack/plugins/fleet/server/services/agents/checkin/index.ts @@ -5,7 +5,11 @@ */ import deepEqual from 'fast-deep-equal'; -import { SavedObjectsClientContract, SavedObjectsBulkCreateObject } from 'src/core/server'; +import { + ElasticsearchClient, + SavedObjectsClientContract, + SavedObjectsBulkCreateObject, +} from 'src/core/server'; import { Agent, NewAgentEvent, @@ -20,6 +24,7 @@ import { getAgentActionsForCheckin } from '../actions'; export async function agentCheckin( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, agent: Agent, data: { events: NewAgentEvent[]; @@ -54,7 +59,7 @@ export async function agentCheckin( } // Wait for new actions - actions = await agentCheckinState.subscribeToNewActions(soClient, agent, options); + actions = await agentCheckinState.subscribeToNewActions(soClient, esClient, agent, options); return { actions }; } diff --git a/x-pack/plugins/fleet/server/services/agents/checkin/state.ts b/x-pack/plugins/fleet/server/services/agents/checkin/state.ts index 63f22b82611c..bdbf391650bc 100644 --- a/x-pack/plugins/fleet/server/services/agents/checkin/state.ts +++ b/x-pack/plugins/fleet/server/services/agents/checkin/state.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'src/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import { Agent } from '../../../types'; import { appContextService } from '../../app_context'; import { agentCheckinStateConnectedAgentsFactory } from './state_connected_agents'; @@ -35,6 +35,7 @@ function agentCheckinStateFactory() { return { subscribeToNewActions: async ( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, agent: Agent, options?: { signal: AbortSignal } ) => { @@ -44,7 +45,7 @@ function agentCheckinStateFactory() { return agentConnected.wrapPromise( agent.id, - newActions.subscribeToNewActions(soClient, agent, options) + newActions.subscribeToNewActions(soClient, esClient, agent, options) ); }, start, diff --git a/x-pack/plugins/fleet/server/services/agents/checkin/state_new_actions.ts b/x-pack/plugins/fleet/server/services/agents/checkin/state_new_actions.ts index 59887d223371..0d5394a88a87 100644 --- a/x-pack/plugins/fleet/server/services/agents/checkin/state_new_actions.ts +++ b/x-pack/plugins/fleet/server/services/agents/checkin/state_new_actions.ts @@ -21,7 +21,7 @@ import { timeout, take, } from 'rxjs/operators'; -import { SavedObjectsClientContract, KibanaRequest } from 'src/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract, KibanaRequest } from 'src/core/server'; import { Agent, AgentAction, @@ -228,6 +228,7 @@ export function agentCheckinStateNewActionsFactory() { async function subscribeToNewActions( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, agent: Agent, options?: { signal: AbortSignal } ): Promise { @@ -262,7 +263,7 @@ export function agentCheckinStateNewActionsFactory() { (action) => action.type === 'INTERNAL_POLICY_REASSIGN' ); if (hasConfigReassign) { - return from(getAgent(soClient, agent.id)).pipe( + return from(getAgent(soClient, esClient, agent.id)).pipe( concatMap((refreshedAgent) => { if (!refreshedAgent.policy_id) { throw new Error('Agent does not have a policy assigned'); diff --git a/x-pack/plugins/fleet/server/services/agents/crud.ts b/x-pack/plugins/fleet/server/services/agents/crud.ts index bcd409e5f7ea..58f64c65e081 100644 --- a/x-pack/plugins/fleet/server/services/agents/crud.ts +++ b/x-pack/plugins/fleet/server/services/agents/crud.ts @@ -4,29 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ import Boom from '@hapi/boom'; -import { SavedObjectsClientContract } from 'src/core/server'; -import { isAgentUpgradeable } from '../../../common'; -import { AGENT_SAVED_OBJECT_TYPE, AGENT_EVENT_SAVED_OBJECT_TYPE } from '../../constants'; -import { AgentSOAttributes, Agent, AgentEventSOAttributes, ListWithKuery } from '../../types'; -import { escapeSearchQueryPhrase, normalizeKuery, findAllSOs } from '../saved_object'; +import { SavedObjectsClientContract, ElasticsearchClient } from 'src/core/server'; + +import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; +import { AgentSOAttributes, Agent, ListWithKuery } from '../../types'; +import { escapeSearchQueryPhrase } from '../saved_object'; import { savedObjectToAgent } from './saved_objects'; import { appContextService } from '../../services'; - -const ACTIVE_AGENT_CONDITION = `${AGENT_SAVED_OBJECT_TYPE}.attributes.active:true`; -const INACTIVE_AGENT_CONDITION = `NOT (${ACTIVE_AGENT_CONDITION})`; - -function _joinFilters(filters: string[], operator = 'AND') { - return filters.reduce((acc: string | undefined, filter) => { - if (acc) { - return `${acc} ${operator} (${filter})`; - } - - return `(${filter})`; - }, undefined); -} +import * as crudServiceSO from './crud_so'; +import * as crudServiceFleetServer from './crud_fleet_server'; export async function listAgents( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, options: ListWithKuery & { showInactive: boolean; } @@ -36,52 +26,16 @@ export async function listAgents( page: number; perPage: number; }> { - const { - page = 1, - perPage = 20, - sortField = 'enrolled_at', - sortOrder = 'desc', - kuery, - showInactive = false, - showUpgradeable, - } = options; - const filters = []; - - if (kuery && kuery !== '') { - filters.push(normalizeKuery(AGENT_SAVED_OBJECT_TYPE, kuery)); - } - - if (showInactive === false) { - filters.push(ACTIVE_AGENT_CONDITION); - } + const fleetServerEnabled = appContextService.getConfig()?.agents?.fleetServerEnabled; - let { saved_objects: agentSOs, total } = await soClient.find({ - type: AGENT_SAVED_OBJECT_TYPE, - filter: _joinFilters(filters), - sortField, - sortOrder, - page, - perPage, - }); - // filtering for a range on the version string will not work, - // nor does filtering on a flattened field (local_metadata), so filter here - if (showUpgradeable) { - agentSOs = agentSOs.filter((agent) => - isAgentUpgradeable(savedObjectToAgent(agent), appContextService.getKibanaVersion()) - ); - total = agentSOs.length; - } - - return { - agents: agentSOs.map(savedObjectToAgent), - total, - page, - perPage, - }; + return fleetServerEnabled + ? crudServiceFleetServer.listAgents(esClient, options) + : crudServiceSO.listAgents(soClient, options); } export async function listAllAgents( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, options: Omit & { showInactive: boolean; } @@ -89,55 +43,34 @@ export async function listAllAgents( agents: Agent[]; total: number; }> { - const { sortField = 'enrolled_at', sortOrder = 'desc', kuery, showInactive = false } = options; - const filters = []; + const fleetServerEnabled = appContextService.getConfig()?.agents?.fleetServerEnabled; - if (kuery && kuery !== '') { - filters.push(normalizeKuery(AGENT_SAVED_OBJECT_TYPE, kuery)); - } - - if (showInactive === false) { - filters.push(ACTIVE_AGENT_CONDITION); - } - - const { saved_objects: agentSOs, total } = await findAllSOs(soClient, { - type: AGENT_SAVED_OBJECT_TYPE, - kuery: _joinFilters(filters), - sortField, - sortOrder, - }); - - return { - agents: agentSOs.map(savedObjectToAgent), - total, - }; + return fleetServerEnabled + ? crudServiceFleetServer.listAllAgents(esClient, options) + : crudServiceSO.listAllAgents(soClient, options); } export async function countInactiveAgents( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, options: Pick ): Promise { - const { kuery } = options; - const filters = [INACTIVE_AGENT_CONDITION]; + const fleetServerEnabled = appContextService.getConfig()?.agents?.fleetServerEnabled; - if (kuery && kuery !== '') { - filters.push(normalizeKuery(AGENT_SAVED_OBJECT_TYPE, kuery)); - } - - const { total } = await soClient.find({ - type: AGENT_SAVED_OBJECT_TYPE, - filter: _joinFilters(filters), - perPage: 0, - }); - - return total; + return fleetServerEnabled + ? crudServiceFleetServer.countInactiveAgents(esClient, options) + : crudServiceSO.countInactiveAgents(soClient, options); } -export async function getAgent(soClient: SavedObjectsClientContract, agentId: string) { - const agent = savedObjectToAgent( - await soClient.get(AGENT_SAVED_OBJECT_TYPE, agentId) - ); - return agent; +export async function getAgent( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + agentId: string +) { + const fleetServerEnabled = appContextService.getConfig()?.agents?.fleetServerEnabled; + return fleetServerEnabled + ? crudServiceFleetServer.getAgent(esClient, agentId) + : crudServiceSO.getAgent(soClient, agentId); } export async function getAgents(soClient: SavedObjectsClientContract, agentIds: string[]) { @@ -187,31 +120,13 @@ export async function updateAgent( }); } -export async function deleteAgent(soClient: SavedObjectsClientContract, agentId: string) { - const agent = await getAgent(soClient, agentId); - if (agent.type === 'EPHEMERAL') { - // Delete events - let more = true; - while (more === true) { - const { saved_objects: events } = await soClient.find({ - type: AGENT_EVENT_SAVED_OBJECT_TYPE, - fields: ['id'], - search: agentId, - searchFields: ['agent_id'], - perPage: 1000, - }); - if (events.length === 0) { - more = false; - } - for (const event of events) { - await soClient.delete(AGENT_EVENT_SAVED_OBJECT_TYPE, event.id); - } - } - await soClient.delete(AGENT_SAVED_OBJECT_TYPE, agentId); - return; - } - - await soClient.update(AGENT_SAVED_OBJECT_TYPE, agentId, { - active: false, - }); +export async function deleteAgent( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + agentId: string +) { + const fleetServerEnabled = appContextService.getConfig()?.agents?.fleetServerEnabled; + return fleetServerEnabled + ? crudServiceFleetServer.deleteAgent(esClient, agentId) + : crudServiceSO.deleteAgent(soClient, agentId); } diff --git a/x-pack/plugins/fleet/server/services/agents/crud_fleet_server.ts b/x-pack/plugins/fleet/server/services/agents/crud_fleet_server.ts new file mode 100644 index 000000000000..9c5e45c05de0 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agents/crud_fleet_server.ts @@ -0,0 +1,197 @@ +/* + * 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 '@hapi/boom'; +import { SavedObjectsClientContract, ElasticsearchClient } from 'src/core/server'; + +import { isAgentUpgradeable, SO_SEARCH_LIMIT } from '../../../common'; +import { AGENT_SAVED_OBJECT_TYPE, AGENTS_INDEX } from '../../constants'; +import { ESSearchHit } from '../../../../../typings/elasticsearch'; +import { AgentSOAttributes, Agent, ListWithKuery } from '../../types'; +import { escapeSearchQueryPhrase, normalizeKuery } from '../saved_object'; +import { savedObjectToAgent } from './saved_objects'; +import { searchHitToAgent } from './helpers'; +import { appContextService } from '../../services'; + +const ACTIVE_AGENT_CONDITION = 'active:true'; +const INACTIVE_AGENT_CONDITION = `NOT (${ACTIVE_AGENT_CONDITION})`; + +function _joinFilters(filters: string[], operator = 'AND') { + return filters.reduce((acc: string | undefined, filter) => { + if (acc) { + return `${acc} ${operator} (${filter})`; + } + + return `(${filter})`; + }, undefined); +} + +function removeSOAttributes(kuery: string) { + return kuery.replace(/attributes\./g, '').replace(/fleet-agents\./g, ''); +} + +export async function listAgents( + esClient: ElasticsearchClient, + options: ListWithKuery & { + showInactive: boolean; + } +): Promise<{ + agents: Agent[]; + total: number; + page: number; + perPage: number; +}> { + const { + page = 1, + perPage = 20, + sortField = 'enrolled_at', + sortOrder = 'desc', + kuery, + showInactive = false, + showUpgradeable, + } = options; + const filters = []; + + if (kuery && kuery !== '') { + filters.push(removeSOAttributes(kuery)); + } + + if (showInactive === false) { + filters.push(ACTIVE_AGENT_CONDITION); + } + + const res = await esClient.search({ + index: AGENTS_INDEX, + from: (page - 1) * perPage, + size: perPage, + sort: `${sortField}:${sortOrder}`, + track_total_hits: true, + q: _joinFilters(filters), + }); + + let agentResults: Agent[] = res.body.hits.hits.map(searchHitToAgent); + let total = res.body.hits.total.value; + + // filtering for a range on the version string will not work, + // nor does filtering on a flattened field (local_metadata), so filter here + if (showUpgradeable) { + agentResults = agentResults.filter((agent) => + isAgentUpgradeable(agent, appContextService.getKibanaVersion()) + ); + total = agentResults.length; + } + + return { + agents: res.body.hits.hits.map(searchHitToAgent), + total, + page, + perPage, + }; +} + +export async function listAllAgents( + esClient: ElasticsearchClient, + options: Omit & { + showInactive: boolean; + } +): Promise<{ + agents: Agent[]; + total: number; +}> { + const res = await listAgents(esClient, { ...options, page: 1, perPage: SO_SEARCH_LIMIT }); + + return { + agents: res.agents, + total: res.total, + }; +} + +export async function countInactiveAgents( + esClient: ElasticsearchClient, + options: Pick +): Promise { + const { kuery } = options; + const filters = [INACTIVE_AGENT_CONDITION]; + + if (kuery && kuery !== '') { + filters.push(normalizeKuery(AGENT_SAVED_OBJECT_TYPE, kuery)); + } + + const res = await esClient.search({ + index: AGENTS_INDEX, + size: 0, + track_total_hits: true, + q: _joinFilters(filters), + }); + + return res.body.hits.total.value; +} + +export async function getAgent(esClient: ElasticsearchClient, agentId: string) { + const agentHit = await esClient.get>({ + index: AGENTS_INDEX, + id: agentId, + }); + const agent = searchHitToAgent(agentHit.body); + + return agent; +} + +export async function getAgents(soClient: SavedObjectsClientContract, agentIds: string[]) { + const agentSOs = await soClient.bulkGet( + agentIds.map((agentId) => ({ + id: agentId, + type: AGENT_SAVED_OBJECT_TYPE, + })) + ); + const agents = agentSOs.saved_objects.map(savedObjectToAgent); + return agents; +} + +export async function getAgentByAccessAPIKeyId( + soClient: SavedObjectsClientContract, + accessAPIKeyId: string +): Promise { + const response = await soClient.find({ + type: AGENT_SAVED_OBJECT_TYPE, + searchFields: ['access_api_key_id'], + search: escapeSearchQueryPhrase(accessAPIKeyId), + }); + const [agent] = response.saved_objects.map(savedObjectToAgent); + + if (!agent) { + throw Boom.notFound('Agent not found'); + } + if (agent.access_api_key_id !== accessAPIKeyId) { + throw new Error('Agent api key id is not matching'); + } + if (!agent.active) { + throw Boom.forbidden('Agent inactive'); + } + + return agent; +} + +export async function updateAgent( + soClient: SavedObjectsClientContract, + agentId: string, + data: { + userProvidedMetatada: any; + } +) { + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agentId, { + user_provided_metadata: data.userProvidedMetatada, + }); +} + +export async function deleteAgent(esClient: ElasticsearchClient, agentId: string) { + await esClient.update({ + id: agentId, + index: AGENT_SAVED_OBJECT_TYPE, + body: { + active: false, + }, + }); +} diff --git a/x-pack/plugins/fleet/server/services/agents/crud_so.ts b/x-pack/plugins/fleet/server/services/agents/crud_so.ts new file mode 100644 index 000000000000..eb8f389741a6 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agents/crud_so.ts @@ -0,0 +1,195 @@ +/* + * 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 '@hapi/boom'; +import { SavedObjectsClientContract } from 'src/core/server'; + +import { isAgentUpgradeable } from '../../../common'; +import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; +import { AgentSOAttributes, Agent, ListWithKuery } from '../../types'; +import { escapeSearchQueryPhrase, normalizeKuery, findAllSOs } from '../saved_object'; +import { savedObjectToAgent } from './saved_objects'; +import { appContextService } from '../../services'; + +const ACTIVE_AGENT_CONDITION = `${AGENT_SAVED_OBJECT_TYPE}.attributes.active:true`; +const INACTIVE_AGENT_CONDITION = `NOT (${ACTIVE_AGENT_CONDITION})`; + +function _joinFilters(filters: string[], operator = 'AND') { + return filters.reduce((acc: string | undefined, filter) => { + if (acc) { + return `${acc} ${operator} (${filter})`; + } + + return `(${filter})`; + }, undefined); +} + +export async function listAgents( + soClient: SavedObjectsClientContract, + options: ListWithKuery & { + showInactive: boolean; + } +): Promise<{ + agents: Agent[]; + total: number; + page: number; + perPage: number; +}> { + const { + page = 1, + perPage = 20, + sortField = 'enrolled_at', + sortOrder = 'desc', + kuery, + showInactive = false, + showUpgradeable, + } = options; + const filters = []; + + if (kuery && kuery !== '') { + filters.push(normalizeKuery(AGENT_SAVED_OBJECT_TYPE, kuery)); + } + + if (showInactive === false) { + filters.push(ACTIVE_AGENT_CONDITION); + } + + let { saved_objects: agentSOs, total } = await soClient.find({ + type: AGENT_SAVED_OBJECT_TYPE, + filter: _joinFilters(filters), + sortField, + sortOrder, + page, + perPage, + }); + // filtering for a range on the version string will not work, + // nor does filtering on a flattened field (local_metadata), so filter here + if (showUpgradeable) { + agentSOs = agentSOs.filter((agent) => + isAgentUpgradeable(savedObjectToAgent(agent), appContextService.getKibanaVersion()) + ); + total = agentSOs.length; + } + + return { + agents: agentSOs.map(savedObjectToAgent), + total, + page, + perPage, + }; +} + +export async function listAllAgents( + soClient: SavedObjectsClientContract, + options: Omit & { + showInactive: boolean; + } +): Promise<{ + agents: Agent[]; + total: number; +}> { + const { sortField = 'enrolled_at', sortOrder = 'desc', kuery, showInactive = false } = options; + const filters = []; + + if (kuery && kuery !== '') { + filters.push(normalizeKuery(AGENT_SAVED_OBJECT_TYPE, kuery)); + } + + if (showInactive === false) { + filters.push(ACTIVE_AGENT_CONDITION); + } + + const { saved_objects: agentSOs, total } = await findAllSOs(soClient, { + type: AGENT_SAVED_OBJECT_TYPE, + kuery: _joinFilters(filters), + sortField, + sortOrder, + }); + + return { + agents: agentSOs.map(savedObjectToAgent), + total, + }; +} + +export async function countInactiveAgents( + soClient: SavedObjectsClientContract, + options: Pick +): Promise { + const { kuery } = options; + const filters = [INACTIVE_AGENT_CONDITION]; + + if (kuery && kuery !== '') { + filters.push(normalizeKuery(AGENT_SAVED_OBJECT_TYPE, kuery)); + } + + const { total } = await soClient.find({ + type: AGENT_SAVED_OBJECT_TYPE, + filter: _joinFilters(filters), + perPage: 0, + }); + + return total; +} + +export async function getAgent(soClient: SavedObjectsClientContract, agentId: string) { + const agent = savedObjectToAgent( + await soClient.get(AGENT_SAVED_OBJECT_TYPE, agentId) + ); + return agent; +} + +export async function getAgents(soClient: SavedObjectsClientContract, agentIds: string[]) { + const agentSOs = await soClient.bulkGet( + agentIds.map((agentId) => ({ + id: agentId, + type: AGENT_SAVED_OBJECT_TYPE, + })) + ); + const agents = agentSOs.saved_objects.map(savedObjectToAgent); + return agents; +} + +export async function getAgentByAccessAPIKeyId( + soClient: SavedObjectsClientContract, + accessAPIKeyId: string +): Promise { + const response = await soClient.find({ + type: AGENT_SAVED_OBJECT_TYPE, + searchFields: ['access_api_key_id'], + search: escapeSearchQueryPhrase(accessAPIKeyId), + }); + const [agent] = response.saved_objects.map(savedObjectToAgent); + + if (!agent) { + throw Boom.notFound('Agent not found'); + } + if (agent.access_api_key_id !== accessAPIKeyId) { + throw new Error('Agent api key id is not matching'); + } + if (!agent.active) { + throw Boom.forbidden('Agent inactive'); + } + + return agent; +} + +export async function updateAgent( + soClient: SavedObjectsClientContract, + agentId: string, + data: { + userProvidedMetatada: any; + } +) { + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agentId, { + user_provided_metadata: data.userProvidedMetatada, + }); +} + +export async function deleteAgent(soClient: SavedObjectsClientContract, agentId: string) { + await soClient.update(AGENT_SAVED_OBJECT_TYPE, agentId, { + active: false, + }); +} diff --git a/x-pack/plugins/fleet/server/services/agents/enroll.ts b/x-pack/plugins/fleet/server/services/agents/enroll.ts index 39b757b9776e..113f302d52b4 100644 --- a/x-pack/plugins/fleet/server/services/agents/enroll.ts +++ b/x-pack/plugins/fleet/server/services/agents/enroll.ts @@ -20,26 +20,16 @@ export async function enroll( soClient: SavedObjectsClientContract, type: AgentType, agentPolicyId: string, - metadata?: { local: any; userProvided: any }, - sharedId?: string + metadata?: { local: any; userProvided: any } ): Promise { const agentVersion = metadata?.local?.elastic?.agent?.version; validateAgentVersion(agentVersion); - const existingAgent = sharedId ? await getAgentBySharedId(soClient, sharedId) : null; - - if (existingAgent && existingAgent.active === true) { - throw Boom.badRequest('Impossible to enroll an already active agent'); - } - - const enrolledAt = new Date().toISOString(); - const agentData: AgentSOAttributes = { - shared_id: sharedId, active: true, policy_id: agentPolicyId, type, - enrolled_at: enrolledAt, + enrolled_at: new Date().toISOString(), user_provided_metadata: metadata?.userProvided ?? {}, local_metadata: metadata?.local ?? {}, current_error_events: undefined, @@ -48,25 +38,11 @@ export async function enroll( default_api_key: undefined, }; - let agent; - if (existingAgent) { - await soClient.update(AGENT_SAVED_OBJECT_TYPE, existingAgent.id, agentData, { + const agent = savedObjectToAgent( + await soClient.create(AGENT_SAVED_OBJECT_TYPE, agentData, { refresh: false, - }); - agent = { - ...existingAgent, - ...agentData, - user_provided_metadata: metadata?.userProvided ?? {}, - local_metadata: metadata?.local ?? {}, - current_error_events: [], - } as Agent; - } else { - agent = savedObjectToAgent( - await soClient.create(AGENT_SAVED_OBJECT_TYPE, agentData, { - refresh: false, - }) - ); - } + }) + ); const accessAPIKey = await APIKeyService.generateAccessApiKey(soClient, agent.id); @@ -77,22 +53,6 @@ export async function enroll( return { ...agent, access_api_key: accessAPIKey.key }; } -async function getAgentBySharedId(soClient: SavedObjectsClientContract, sharedId: string) { - const response = await soClient.find({ - type: AGENT_SAVED_OBJECT_TYPE, - searchFields: ['shared_id'], - search: sharedId, - }); - - const agents = response.saved_objects.map(savedObjectToAgent); - - if (agents.length > 0) { - return agents[0]; - } - - return null; -} - export function validateAgentVersion( agentVersion: string, kibanaVersion = appContextService.getKibanaVersion() diff --git a/x-pack/plugins/fleet/server/services/agents/helpers.ts b/x-pack/plugins/fleet/server/services/agents/helpers.ts new file mode 100644 index 000000000000..38330a090ae8 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/agents/helpers.ts @@ -0,0 +1,21 @@ +/* + * 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 { ESSearchHit } from '../../../../../typings/elasticsearch'; +import { Agent, AgentSOAttributes } from '../../types'; + +export function searchHitToAgent(hit: ESSearchHit): Agent { + return { + id: hit._id, + ...hit._source, + current_error_events: hit._source.current_error_events + ? JSON.parse(hit._source.current_error_events) + : [], + access_api_key: undefined, + status: undefined, + packages: hit._source.packages ?? [], + }; +} diff --git a/x-pack/plugins/fleet/server/services/agents/reassign.ts b/x-pack/plugins/fleet/server/services/agents/reassign.ts index b656ab12e96c..8a1dc6195088 100644 --- a/x-pack/plugins/fleet/server/services/agents/reassign.ts +++ b/x-pack/plugins/fleet/server/services/agents/reassign.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { SavedObjectsClientContract, ElasticsearchClient } from 'kibana/server'; import Boom from '@hapi/boom'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import { AgentSOAttributes } from '../../types'; @@ -14,6 +14,7 @@ import { createAgentAction, bulkCreateAgentActions } from './actions'; export async function reassignAgent( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, agentId: string, newAgentPolicyId: string ) { @@ -36,6 +37,7 @@ export async function reassignAgent( export async function reassignAgents( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, options: | { agentIds: string[]; @@ -55,7 +57,7 @@ export async function reassignAgents( 'agentIds' in options ? await getAgents(soClient, options.agentIds) : ( - await listAllAgents(soClient, { + await listAllAgents(soClient, esClient, { kuery: options.kuery, showInactive: false, }) diff --git a/x-pack/plugins/fleet/server/services/agents/status.test.ts b/x-pack/plugins/fleet/server/services/agents/status.test.ts index f216cd541eb2..587f0af227ff 100644 --- a/x-pack/plugins/fleet/server/services/agents/status.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/status.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { savedObjectsClientMock } from 'src/core/server/mocks'; +import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; import { getAgentStatusById } from './status'; import { AGENT_TYPE_PERMANENT } from '../../../common/constants'; import { AgentSOAttributes } from '../../../common/types/models'; @@ -13,6 +13,7 @@ import { SavedObject } from 'kibana/server'; describe('Agent status service', () => { it('should return inactive when agent is not active', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockSavedObjectsClient.get = jest.fn().mockReturnValue({ id: 'id', type: AGENT_TYPE_PERMANENT, @@ -22,12 +23,13 @@ describe('Agent status service', () => { user_provided_metadata: {}, }, } as SavedObject); - const status = await getAgentStatusById(mockSavedObjectsClient, 'id'); + const status = await getAgentStatusById(mockSavedObjectsClient, mockElasticsearchClient, 'id'); expect(status).toEqual('inactive'); }); it('should return online when agent is active', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockSavedObjectsClient.get = jest.fn().mockReturnValue({ id: 'id', type: AGENT_TYPE_PERMANENT, @@ -38,12 +40,13 @@ describe('Agent status service', () => { user_provided_metadata: {}, }, } as SavedObject); - const status = await getAgentStatusById(mockSavedObjectsClient, 'id'); + const status = await getAgentStatusById(mockSavedObjectsClient, mockElasticsearchClient, 'id'); expect(status).toEqual('online'); }); it('should return enrolling when agent is active but never checkin', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockSavedObjectsClient.get = jest.fn().mockReturnValue({ id: 'id', type: AGENT_TYPE_PERMANENT, @@ -53,12 +56,13 @@ describe('Agent status service', () => { user_provided_metadata: {}, }, } as SavedObject); - const status = await getAgentStatusById(mockSavedObjectsClient, 'id'); + const status = await getAgentStatusById(mockSavedObjectsClient, mockElasticsearchClient, 'id'); expect(status).toEqual('enrolling'); }); it('should return unenrolling when agent is unenrolling', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockSavedObjectsClient.get = jest.fn().mockReturnValue({ id: 'id', type: AGENT_TYPE_PERMANENT, @@ -70,7 +74,7 @@ describe('Agent status service', () => { user_provided_metadata: {}, }, } as SavedObject); - const status = await getAgentStatusById(mockSavedObjectsClient, 'id'); + const status = await getAgentStatusById(mockSavedObjectsClient, mockElasticsearchClient, 'id'); expect(status).toEqual('unenrolling'); }); }); diff --git a/x-pack/plugins/fleet/server/services/agents/status.ts b/x-pack/plugins/fleet/server/services/agents/status.ts index 74faedc8e293..ba8f8fc36385 100644 --- a/x-pack/plugins/fleet/server/services/agents/status.ts +++ b/x-pack/plugins/fleet/server/services/agents/status.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'src/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import pMap from 'p-map'; import { getAgent, listAgents } from './crud'; import { AGENT_EVENT_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE } from '../../constants'; @@ -14,9 +14,10 @@ import { AgentStatusKueryHelper } from '../../../common/services'; export async function getAgentStatusById( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, agentId: string ): Promise { - const agent = await getAgent(soClient, agentId); + const agent = await getAgent(soClient, esClient, agentId); return AgentStatusKueryHelper.getAgentStatus(agent); } @@ -36,6 +37,7 @@ function joinKuerys(...kuerys: Array) { export async function getAgentStatusForAgentPolicy( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, agentPolicyId?: string, filterKuery?: string ) { @@ -48,7 +50,7 @@ export async function getAgentStatusForAgentPolicy( AgentStatusKueryHelper.buildKueryForUpdatingAgents(), ], (kuery) => - listAgents(soClient, { + listAgents(soClient, esClient, { showInactive: false, perPage: 0, page: 1, diff --git a/x-pack/plugins/fleet/server/services/agents/unenroll.ts b/x-pack/plugins/fleet/server/services/agents/unenroll.ts index 9c2b2bdfe7f6..5246927cb4ee 100644 --- a/x-pack/plugins/fleet/server/services/agents/unenroll.ts +++ b/x-pack/plugins/fleet/server/services/agents/unenroll.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'src/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import { AgentSOAttributes } from '../../types'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import { getAgent } from './crud'; @@ -25,6 +25,7 @@ export async function unenrollAgent(soClient: SavedObjectsClientContract, agentI export async function unenrollAgents( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, options: | { agentIds: string[]; @@ -38,7 +39,7 @@ export async function unenrollAgents( 'agentIds' in options ? await getAgents(soClient, options.agentIds) : ( - await listAllAgents(soClient, { + await listAllAgents(soClient, esClient, { kuery: options.kuery, showInactive: false, }) @@ -70,8 +71,12 @@ export async function unenrollAgents( ); } -export async function forceUnenrollAgent(soClient: SavedObjectsClientContract, agentId: string) { - const agent = await getAgent(soClient, agentId); +export async function forceUnenrollAgent( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + agentId: string +) { + const agent = await getAgent(soClient, esClient, agentId); await Promise.all([ agent.access_api_key_id @@ -90,6 +95,7 @@ export async function forceUnenrollAgent(soClient: SavedObjectsClientContract, a export async function forceUnenrollAgents( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, options: | { agentIds: string[]; @@ -103,7 +109,7 @@ export async function forceUnenrollAgents( 'agentIds' in options ? await getAgents(soClient, options.agentIds) : ( - await listAllAgents(soClient, { + await listAllAgents(soClient, esClient, { kuery: options.kuery, showInactive: false, }) diff --git a/x-pack/plugins/fleet/server/services/agents/update.ts b/x-pack/plugins/fleet/server/services/agents/update.ts index b85a831294b5..7bd807bf4e57 100644 --- a/x-pack/plugins/fleet/server/services/agents/update.ts +++ b/x-pack/plugins/fleet/server/services/agents/update.ts @@ -4,19 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'src/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import { listAgents } from './crud'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import { unenrollAgent } from './unenroll'; export async function unenrollForAgentPolicyId( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, policyId: string ) { let hasMore = true; let page = 1; while (hasMore) { - const { agents } = await listAgents(soClient, { + const { agents } = await listAgents(soClient, esClient, { kuery: `${AGENT_SAVED_OBJECT_TYPE}.policy_id:"${policyId}"`, page: page++, perPage: 1000, diff --git a/x-pack/plugins/fleet/server/services/agents/upgrade.ts b/x-pack/plugins/fleet/server/services/agents/upgrade.ts index cf83a938d3c3..9515cca8ce00 100644 --- a/x-pack/plugins/fleet/server/services/agents/upgrade.ts +++ b/x-pack/plugins/fleet/server/services/agents/upgrade.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'src/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import { AgentSOAttributes, AgentAction, AgentActionSOAttributes } from '../../types'; import { AGENT_ACTION_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE } from '../../constants'; import { bulkCreateAgentActions, createAgentAction } from './actions'; @@ -59,6 +59,7 @@ export async function ackAgentUpgraded( export async function sendUpgradeAgentsActions( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, options: | { agentIds: string[]; @@ -79,7 +80,7 @@ export async function sendUpgradeAgentsActions( 'agentIds' in options ? await getAgents(soClient, options.agentIds) : ( - await listAllAgents(soClient, { + await listAllAgents(soClient, esClient, { kuery: options.kuery, showInactive: false, }) diff --git a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts index 8f67753392e6..747cbae3f71c 100644 --- a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts +++ b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts @@ -4,18 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import uuid from 'uuid'; -import Boom from '@hapi/boom'; -import { SavedObjectsClientContract, SavedObject } from 'src/core/server'; -import { EnrollmentAPIKey, EnrollmentAPIKeySOAttributes } from '../../types'; -import { ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE } from '../../constants'; -import { createAPIKey, invalidateAPIKeys } from './security'; -import { agentPolicyService } from '../agent_policy'; +import { SavedObjectsClientContract, ElasticsearchClient } from 'src/core/server'; +import { EnrollmentAPIKey } from '../../types'; import { appContextService } from '../app_context'; -import { normalizeKuery } from '../saved_object'; +import * as enrollmentApiKeyServiceSO from './enrollment_api_key_so'; +import * as enrollmentApiKeyServiceFleetServer from './enrollment_api_key_fleet_server'; export async function listEnrollmentApiKeys( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, options: { page?: number; perPage?: number; @@ -23,39 +20,23 @@ export async function listEnrollmentApiKeys( showInactive?: boolean; } ): Promise<{ items: EnrollmentAPIKey[]; total: any; page: any; perPage: any }> { - const { page = 1, perPage = 20, kuery } = options; - - // eslint-disable-next-line @typescript-eslint/naming-convention - const { saved_objects, total } = await soClient.find({ - type: ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, - page, - perPage, - sortField: 'created_at', - sortOrder: 'desc', - filter: - kuery && kuery !== '' - ? normalizeKuery(ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, kuery) - : undefined, - }); - - const items = saved_objects.map(savedObjectToEnrollmentApiKey); - - return { - items, - total, - page, - perPage, - }; + if (appContextService.getConfig()?.agents?.fleetServerEnabled === true) { + return enrollmentApiKeyServiceFleetServer.listEnrollmentApiKeys(esClient, options); + } else { + return enrollmentApiKeyServiceSO.listEnrollmentApiKeys(soClient, options); + } } -export async function getEnrollmentAPIKey(soClient: SavedObjectsClientContract, id: string) { - const so = await appContextService - .getEncryptedSavedObjects() - .getDecryptedAsInternalUser( - ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, - id - ); - return savedObjectToEnrollmentApiKey(so); +export async function getEnrollmentAPIKey( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + id: string +) { + if (appContextService.getConfig()?.agents?.fleetServerEnabled === true) { + return enrollmentApiKeyServiceFleetServer.getEnrollmentAPIKey(esClient, id); + } else { + return enrollmentApiKeyServiceSO.getEnrollmentAPIKey(soClient, id); + } } /** @@ -63,112 +44,37 @@ export async function getEnrollmentAPIKey(soClient: SavedObjectsClientContract, * @param soClient * @param id */ -export async function deleteEnrollmentApiKey(soClient: SavedObjectsClientContract, id: string) { - const enrollmentApiKey = await getEnrollmentAPIKey(soClient, id); - - await invalidateAPIKeys(soClient, [enrollmentApiKey.api_key_id]); - - await soClient.update(ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, id, { - active: false, - }); +export async function deleteEnrollmentApiKey( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + id: string +) { + if (appContextService.getConfig()?.agents?.fleetServerEnabled === true) { + return enrollmentApiKeyServiceFleetServer.deleteEnrollmentApiKey(soClient, esClient, id); + } else { + return enrollmentApiKeyServiceSO.deleteEnrollmentApiKey(soClient, id); + } } export async function deleteEnrollmentApiKeyForAgentPolicyId( soClient: SavedObjectsClientContract, agentPolicyId: string ) { - let hasMore = true; - let page = 1; - while (hasMore) { - const { items } = await listEnrollmentApiKeys(soClient, { - page: page++, - perPage: 100, - kuery: `${ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE}.policy_id:${agentPolicyId}`, - }); - - if (items.length === 0) { - hasMore = false; - } - - for (const apiKey of items) { - await deleteEnrollmentApiKey(soClient, apiKey.id); - } - } + return enrollmentApiKeyServiceSO.deleteEnrollmentApiKeyForAgentPolicyId(soClient, agentPolicyId); } export async function generateEnrollmentAPIKey( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, data: { name?: string; expiration?: string; agentPolicyId?: string; } ) { - const id = uuid.v4(); - const { name: providedKeyName } = data; - if (data.agentPolicyId) { - await validateAgentPolicyId(soClient, data.agentPolicyId); - } - const agentPolicyId = - data.agentPolicyId ?? (await agentPolicyService.getDefaultAgentPolicyId(soClient)); - const name = providedKeyName ? `${providedKeyName} (${id})` : id; - const key = await createAPIKey(soClient, name, { - // Useless role to avoid to have the privilege of the user that created the key - 'fleet-apikey-enroll': { - cluster: [], - applications: [ - { - application: '.fleet', - privileges: ['no-privileges'], - resources: ['*'], - }, - ], - }, - }); - - if (!key) { - throw new Error('Unable to create an enrollment api key'); + if (appContextService.getConfig()?.agents?.fleetServerEnabled === true) { + return enrollmentApiKeyServiceFleetServer.generateEnrollmentAPIKey(soClient, esClient, data); + } else { + return enrollmentApiKeyServiceSO.generateEnrollmentAPIKey(soClient, data); } - - const apiKey = Buffer.from(`${key.id}:${key.api_key}`).toString('base64'); - - const so = await soClient.create( - ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, - { - active: true, - api_key_id: key.id, - api_key: apiKey, - name, - policy_id: agentPolicyId, - created_at: new Date().toISOString(), - } - ); - - return getEnrollmentAPIKey(soClient, so.id); -} - -async function validateAgentPolicyId(soClient: SavedObjectsClientContract, agentPolicyId: string) { - try { - await agentPolicyService.get(soClient, agentPolicyId); - } catch (e) { - if (e.isBoom && e.output.statusCode === 404) { - throw Boom.badRequest(`Agent policy ${agentPolicyId} does not exist`); - } - throw e; - } -} - -function savedObjectToEnrollmentApiKey({ - error, - attributes, - id, -}: SavedObject): EnrollmentAPIKey { - if (error) { - throw new Error(error.message); - } - - return { - id, - ...attributes, - }; } diff --git a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key_fleet_server.ts b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key_fleet_server.ts new file mode 100644 index 000000000000..c0aa42c6e4ed --- /dev/null +++ b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key_fleet_server.ts @@ -0,0 +1,205 @@ +/* + * 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 uuid from 'uuid'; +import Boom from '@hapi/boom'; +import { ResponseError } from '@elastic/elasticsearch/lib/errors'; +import { SavedObjectsClientContract, ElasticsearchClient } from 'src/core/server'; +import { EnrollmentAPIKey, FleetServerEnrollmentAPIKey } from '../../types'; +import { ENROLLMENT_API_KEYS_INDEX } from '../../constants'; +import { createAPIKey, invalidateAPIKeys } from './security'; +import { agentPolicyService } from '../agent_policy'; + +// TODO Move these types to another file +interface SearchResponse { + took: number; + timed_out: boolean; + _scroll_id?: string; + hits: { + total: { + value: number; + relation: string; + }; + max_score: number; + hits: Array<{ + _index: string; + _type: string; + _id: string; + _score: number; + _source: T; + _version?: number; + fields?: any; + highlight?: any; + inner_hits?: any; + matched_queries?: string[]; + sort?: string[]; + }>; + }; +} + +type SearchHit = SearchResponse['hits']['hits'][0]; + +export async function listEnrollmentApiKeys( + esClient: ElasticsearchClient, + options: { + page?: number; + perPage?: number; + kuery?: string; + showInactive?: boolean; + } +): Promise<{ items: EnrollmentAPIKey[]; total: any; page: any; perPage: any }> { + const { page = 1, perPage = 20, kuery } = options; + + const res = await esClient.search>({ + index: ENROLLMENT_API_KEYS_INDEX, + from: (page - 1) * perPage, + size: perPage, + sort: 'created_at:desc', + track_total_hits: true, + q: kuery, + }); + + const items = res.body.hits.hits.map(esDocToEnrollmentApiKey); + + return { + items, + total: res.body.hits.total.value, + page, + perPage, + }; +} + +export async function getEnrollmentAPIKey( + esClient: ElasticsearchClient, + id: string +): Promise { + try { + const res = await esClient.get>({ + index: ENROLLMENT_API_KEYS_INDEX, + id, + }); + + return esDocToEnrollmentApiKey(res.body); + } catch (e) { + if (e instanceof ResponseError && e.statusCode === 404) { + throw Boom.notFound(`Enrollment api key ${id} not found`); + } + + throw e; + } +} + +/** + * Invalidate an api key and mark it as inactive + * @param soClient + * @param id + */ +export async function deleteEnrollmentApiKey( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + id: string +) { + const enrollmentApiKey = await getEnrollmentAPIKey(esClient, id); + + await invalidateAPIKeys(soClient, [enrollmentApiKey.api_key_id]); + + await esClient.update({ + index: ENROLLMENT_API_KEYS_INDEX, + id, + body: { + doc: { + active: false, + }, + }, + refresh: 'wait_for', + }); +} + +export async function deleteEnrollmentApiKeyForAgentPolicyId( + soClient: SavedObjectsClientContract, + agentPolicyId: string +) { + throw new Error('NOT IMPLEMENTED'); +} + +export async function generateEnrollmentAPIKey( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + data: { + name?: string; + expiration?: string; + agentPolicyId?: string; + } +): Promise { + const id = uuid.v4(); + const { name: providedKeyName } = data; + if (data.agentPolicyId) { + await validateAgentPolicyId(soClient, data.agentPolicyId); + } + const agentPolicyId = + data.agentPolicyId ?? (await agentPolicyService.getDefaultAgentPolicyId(soClient)); + const name = providedKeyName ? `${providedKeyName} (${id})` : id; + const key = await createAPIKey(soClient, name, { + // Useless role to avoid to have the privilege of the user that created the key + 'fleet-apikey-enroll': { + cluster: [], + applications: [ + { + application: '.fleet', + privileges: ['no-privileges'], + resources: ['*'], + }, + ], + }, + }); + + if (!key) { + throw new Error('Unable to create an enrollment api key'); + } + + const apiKey = Buffer.from(`${key.id}:${key.api_key}`).toString('base64'); + + const body = { + active: true, + api_key_id: key.id, + api_key: apiKey, + name, + policy_id: agentPolicyId, + created_at: new Date().toISOString(), + }; + + const res = await esClient.create({ + index: ENROLLMENT_API_KEYS_INDEX, + body, + id, + refresh: 'wait_for', + }); + + return { + id: res.body._id, + ...body, + }; +} + +async function validateAgentPolicyId(soClient: SavedObjectsClientContract, agentPolicyId: string) { + try { + await agentPolicyService.get(soClient, agentPolicyId); + } catch (e) { + if (e.isBoom && e.output.statusCode === 404) { + throw Boom.badRequest(`Agent policy ${agentPolicyId} does not exist`); + } + throw e; + } +} + +function esDocToEnrollmentApiKey(doc: SearchHit): EnrollmentAPIKey { + return { + id: doc._id, + ...doc._source, + created_at: doc._source.created_at as string, + active: doc._source.active || false, + }; +} diff --git a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key_so.ts b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key_so.ts new file mode 100644 index 000000000000..8f67753392e6 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key_so.ts @@ -0,0 +1,174 @@ +/* + * 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 uuid from 'uuid'; +import Boom from '@hapi/boom'; +import { SavedObjectsClientContract, SavedObject } from 'src/core/server'; +import { EnrollmentAPIKey, EnrollmentAPIKeySOAttributes } from '../../types'; +import { ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE } from '../../constants'; +import { createAPIKey, invalidateAPIKeys } from './security'; +import { agentPolicyService } from '../agent_policy'; +import { appContextService } from '../app_context'; +import { normalizeKuery } from '../saved_object'; + +export async function listEnrollmentApiKeys( + soClient: SavedObjectsClientContract, + options: { + page?: number; + perPage?: number; + kuery?: string; + showInactive?: boolean; + } +): Promise<{ items: EnrollmentAPIKey[]; total: any; page: any; perPage: any }> { + const { page = 1, perPage = 20, kuery } = options; + + // eslint-disable-next-line @typescript-eslint/naming-convention + const { saved_objects, total } = await soClient.find({ + type: ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, + page, + perPage, + sortField: 'created_at', + sortOrder: 'desc', + filter: + kuery && kuery !== '' + ? normalizeKuery(ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, kuery) + : undefined, + }); + + const items = saved_objects.map(savedObjectToEnrollmentApiKey); + + return { + items, + total, + page, + perPage, + }; +} + +export async function getEnrollmentAPIKey(soClient: SavedObjectsClientContract, id: string) { + const so = await appContextService + .getEncryptedSavedObjects() + .getDecryptedAsInternalUser( + ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, + id + ); + return savedObjectToEnrollmentApiKey(so); +} + +/** + * Invalidate an api key and mark it as inactive + * @param soClient + * @param id + */ +export async function deleteEnrollmentApiKey(soClient: SavedObjectsClientContract, id: string) { + const enrollmentApiKey = await getEnrollmentAPIKey(soClient, id); + + await invalidateAPIKeys(soClient, [enrollmentApiKey.api_key_id]); + + await soClient.update(ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, id, { + active: false, + }); +} + +export async function deleteEnrollmentApiKeyForAgentPolicyId( + soClient: SavedObjectsClientContract, + agentPolicyId: string +) { + let hasMore = true; + let page = 1; + while (hasMore) { + const { items } = await listEnrollmentApiKeys(soClient, { + page: page++, + perPage: 100, + kuery: `${ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE}.policy_id:${agentPolicyId}`, + }); + + if (items.length === 0) { + hasMore = false; + } + + for (const apiKey of items) { + await deleteEnrollmentApiKey(soClient, apiKey.id); + } + } +} + +export async function generateEnrollmentAPIKey( + soClient: SavedObjectsClientContract, + data: { + name?: string; + expiration?: string; + agentPolicyId?: string; + } +) { + const id = uuid.v4(); + const { name: providedKeyName } = data; + if (data.agentPolicyId) { + await validateAgentPolicyId(soClient, data.agentPolicyId); + } + const agentPolicyId = + data.agentPolicyId ?? (await agentPolicyService.getDefaultAgentPolicyId(soClient)); + const name = providedKeyName ? `${providedKeyName} (${id})` : id; + const key = await createAPIKey(soClient, name, { + // Useless role to avoid to have the privilege of the user that created the key + 'fleet-apikey-enroll': { + cluster: [], + applications: [ + { + application: '.fleet', + privileges: ['no-privileges'], + resources: ['*'], + }, + ], + }, + }); + + if (!key) { + throw new Error('Unable to create an enrollment api key'); + } + + const apiKey = Buffer.from(`${key.id}:${key.api_key}`).toString('base64'); + + const so = await soClient.create( + ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, + { + active: true, + api_key_id: key.id, + api_key: apiKey, + name, + policy_id: agentPolicyId, + created_at: new Date().toISOString(), + } + ); + + return getEnrollmentAPIKey(soClient, so.id); +} + +async function validateAgentPolicyId(soClient: SavedObjectsClientContract, agentPolicyId: string) { + try { + await agentPolicyService.get(soClient, agentPolicyId); + } catch (e) { + if (e.isBoom && e.output.statusCode === 404) { + throw Boom.badRequest(`Agent policy ${agentPolicyId} does not exist`); + } + throw e; + } +} + +function savedObjectToEnrollmentApiKey({ + error, + attributes, + id, +}: SavedObject): EnrollmentAPIKey { + if (error) { + throw new Error(error.message); + } + + return { + id, + ...attributes, + }; +} diff --git a/x-pack/plugins/fleet/server/services/app_context.ts b/x-pack/plugins/fleet/server/services/app_context.ts index d6b62458ed1f..66ffd3ca5308 100644 --- a/x-pack/plugins/fleet/server/services/app_context.ts +++ b/x-pack/plugins/fleet/server/services/app_context.ts @@ -5,7 +5,13 @@ */ import { BehaviorSubject, Observable } from 'rxjs'; import { first } from 'rxjs/operators'; -import { SavedObjectsServiceStart, HttpServiceSetup, Logger, KibanaRequest } from 'src/core/server'; +import { + ElasticsearchClient, + SavedObjectsServiceStart, + HttpServiceSetup, + Logger, + KibanaRequest, +} from 'src/core/server'; import { EncryptedSavedObjectsClient, EncryptedSavedObjectsPluginSetup, @@ -19,6 +25,7 @@ import { CloudSetup } from '../../../cloud/server'; class AppContextService { private encryptedSavedObjects: EncryptedSavedObjectsClient | undefined; private encryptedSavedObjectsSetup: EncryptedSavedObjectsPluginSetup | undefined; + private esClient: ElasticsearchClient | undefined; private security: SecurityPluginStart | undefined; private config$?: Observable; private configSubject$?: BehaviorSubject; @@ -32,6 +39,7 @@ class AppContextService { private externalCallbacks: ExternalCallbacksStorage = new Map(); public async start(appContext: FleetAppContext) { + this.esClient = appContext.elasticsearch.client.asInternalUser; this.encryptedSavedObjects = appContext.encryptedSavedObjectsStart?.getClient(); this.encryptedSavedObjectsSetup = appContext.encryptedSavedObjectsSetup; this.security = appContext.security; @@ -96,12 +104,20 @@ class AppContextService { } public getInternalUserSOClient(request: KibanaRequest) { - // soClient as kibana internal users, be carefull on how you use it, security is not enabled + // soClient as kibana internal users, be careful on how you use it, security is not enabled return appContextService.getSavedObjects().getScopedClient(request, { excludedWrappers: ['security'], }); } + public getInternalUserESClient() { + if (!this.esClient) { + throw new Error('Elasticsearch start service not set.'); + } + // soClient as kibana internal users, be careful on how you use it, security is not enabled + return this.esClient; + } + public getIsProductionMode() { return this.isProductionMode; } diff --git a/x-pack/plugins/fleet/server/services/fleet_server_migration.ts b/x-pack/plugins/fleet/server/services/fleet_server_migration.ts new file mode 100644 index 000000000000..1a50b5c9df76 --- /dev/null +++ b/x-pack/plugins/fleet/server/services/fleet_server_migration.ts @@ -0,0 +1,75 @@ +/* + * 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 'src/core/server'; +import { + ENROLLMENT_API_KEYS_INDEX, + ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, + FleetServerEnrollmentAPIKey, +} from '../../common'; +import { listEnrollmentApiKeys, getEnrollmentAPIKey } from './api_keys/enrollment_api_key_so'; +import { appContextService } from './app_context'; + +export async function runFleetServerMigration() { + const logger = appContextService.getLogger(); + logger.info('Starting fleet server migration'); + await migrateEnrollmentApiKeys(); + logger.info('Fleet server migration finished'); +} + +function getInternalUserSOClient() { + const fakeRequest = ({ + headers: {}, + getBasePath: () => '', + path: '/', + route: { settings: {} }, + url: { + href: '/', + }, + raw: { + req: { + url: '/', + }, + }, + } as unknown) as KibanaRequest; + + return appContextService.getInternalUserSOClient(fakeRequest); +} + +async function migrateEnrollmentApiKeys() { + const esClient = appContextService.getInternalUserESClient(); + const soClient = getInternalUserSOClient(); + let hasMore = true; + while (hasMore) { + const res = await listEnrollmentApiKeys(soClient, { + page: 1, + perPage: 100, + }); + if (res.total === 0) { + hasMore = false; + } + for (const item of res.items) { + const key = await getEnrollmentAPIKey(soClient, item.id); + + const body: FleetServerEnrollmentAPIKey = { + api_key: key.api_key, + api_key_id: key.api_key_id, + active: key.active, + created_at: key.created_at, + name: key.name, + policy_id: key.policy_id, + }; + await esClient.create({ + index: ENROLLMENT_API_KEYS_INDEX, + body, + id: key.id, + refresh: 'wait_for', + }); + + await soClient.delete(ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, key.id); + } + } +} diff --git a/x-pack/plugins/fleet/server/services/index.ts b/x-pack/plugins/fleet/server/services/index.ts index d9015c519553..b590b2ed002c 100644 --- a/x-pack/plugins/fleet/server/services/index.ts +++ b/x-pack/plugins/fleet/server/services/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract, KibanaRequest } from 'kibana/server'; +import { ElasticsearchClient, SavedObjectsClientContract, KibanaRequest } from 'kibana/server'; import { AgentStatus, Agent, EsAssetReference } from '../types'; import * as settingsService from './settings'; import { getAgent, listAgents } from './agents'; @@ -53,7 +53,11 @@ export interface AgentService { /** * Return the status by the Agent's id */ - getAgentStatusById(soClient: SavedObjectsClientContract, agentId: string): Promise; + getAgentStatusById( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + agentId: string + ): Promise; /** * List agents */ diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index 5e295c157670..eb26b405fbda 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { savedObjectsClientMock } from 'src/core/server/mocks'; +import { elasticsearchServiceMock, savedObjectsClientMock } from 'src/core/server/mocks'; import { createPackagePolicyMock } from '../../common/mocks'; import { packagePolicyService } from './package_policy'; import { PackageInfo, PackagePolicySOAttributes } from '../types'; @@ -345,9 +345,11 @@ describe('Package policy service', () => { throw savedObjectsClient.errors.createConflictError('abc', '123'); } ); + const elasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; await expect( packagePolicyService.update( savedObjectsClient, + elasticsearchClient, 'the-package-policy-id', createPackagePolicyMock() ) diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 95b1a43ec2e5..605b0f6cf65c 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -3,7 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, RequestHandlerContext, SavedObjectsClientContract } from 'src/core/server'; +import { + ElasticsearchClient, + KibanaRequest, + RequestHandlerContext, + SavedObjectsClientContract, +} from 'src/core/server'; import uuid from 'uuid'; import { AuthenticatedUser } from '../../../security/server'; import { @@ -47,6 +52,7 @@ function getDataset(st: string) { class PackagePolicyService { public async create( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, callCluster: CallESAsCurrentUser, packagePolicy: NewPackagePolicy, options?: { id?: string; user?: AuthenticatedUser; bumpRevision?: boolean } @@ -116,10 +122,16 @@ class PackagePolicyService { ); // Assign it to the given agent policy - await agentPolicyService.assignPackagePolicies(soClient, packagePolicy.policy_id, [newSo.id], { - user: options?.user, - bumpRevision: options?.bumpRevision ?? true, - }); + await agentPolicyService.assignPackagePolicies( + soClient, + esClient, + packagePolicy.policy_id, + [newSo.id], + { + user: options?.user, + bumpRevision: options?.bumpRevision ?? true, + } + ); return { id: newSo.id, @@ -130,6 +142,7 @@ class PackagePolicyService { public async bulkCreate( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, packagePolicies: NewPackagePolicy[], agentPolicyId: string, options?: { user?: AuthenticatedUser; bumpRevision?: boolean } @@ -167,6 +180,7 @@ class PackagePolicyService { // Assign it to the given agent policy await agentPolicyService.assignPackagePolicies( soClient, + esClient, agentPolicyId, newSos.map((newSo) => newSo.id), { @@ -252,6 +266,7 @@ class PackagePolicyService { public async update( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, id: string, packagePolicy: UpdatePackagePolicy, options?: { user?: AuthenticatedUser } @@ -308,7 +323,7 @@ class PackagePolicyService { ); // Bump revision of associated agent policy - await agentPolicyService.bumpRevision(soClient, packagePolicy.policy_id, { + await agentPolicyService.bumpRevision(soClient, esClient, packagePolicy.policy_id, { user: options?.user, }); @@ -317,6 +332,7 @@ class PackagePolicyService { public async delete( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, ids: string[], options?: { user?: AuthenticatedUser; skipUnassignFromAgentPolicies?: boolean } ): Promise { @@ -331,6 +347,7 @@ class PackagePolicyService { if (!options?.skipUnassignFromAgentPolicies) { await agentPolicyService.unassignPackagePolicies( soClient, + esClient, packagePolicy.policy_id, [packagePolicy.id], { diff --git a/x-pack/plugins/fleet/server/services/setup.test.ts b/x-pack/plugins/fleet/server/services/setup.test.ts index bb01862aaf31..1e2440ba3b54 100644 --- a/x-pack/plugins/fleet/server/services/setup.test.ts +++ b/x-pack/plugins/fleet/server/services/setup.test.ts @@ -41,8 +41,9 @@ describe('setupIngestManager', () => { soClient.find = mockedMethodThrowsError(); soClient.get = mockedMethodThrowsError(); soClient.update = mockedMethodThrowsError(); + const esClient = context.core.elasticsearch.client.asCurrentUser; - const setupPromise = setupIngestManager(soClient, jest.fn()); + const setupPromise = setupIngestManager(soClient, esClient, jest.fn()); await expect(setupPromise).rejects.toThrow('SO method mocked to throw'); await expect(setupPromise).rejects.toThrow(Error); }); @@ -53,8 +54,9 @@ describe('setupIngestManager', () => { soClient.find = mockedMethodThrowsCustom(); soClient.get = mockedMethodThrowsCustom(); soClient.update = mockedMethodThrowsCustom(); + const esClient = context.core.elasticsearch.client.asCurrentUser; - const setupPromise = setupIngestManager(soClient, jest.fn()); + const setupPromise = setupIngestManager(soClient, esClient, jest.fn()); await expect(setupPromise).rejects.toThrow('method mocked to throw'); await expect(setupPromise).rejects.toThrow(CustomTestError); }); diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index c37eed191088..0dcdfeb7b380 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -5,7 +5,7 @@ */ import uuid from 'uuid'; -import { SavedObjectsClientContract } from 'src/core/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'src/core/server'; import { CallESAsCurrentUser } from '../types'; import { agentPolicyService } from './agent_policy'; import { outputService } from './output'; @@ -39,13 +39,15 @@ export interface SetupStatus { export async function setupIngestManager( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, callCluster: CallESAsCurrentUser ): Promise { - return awaitIfPending(async () => createSetupSideEffects(soClient, callCluster)); + return awaitIfPending(async () => createSetupSideEffects(soClient, esClient, callCluster)); } async function createSetupSideEffects( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, callCluster: CallESAsCurrentUser ): Promise { const [ @@ -56,7 +58,8 @@ async function createSetupSideEffects( // packages installed by default ensureInstalledDefaultPackages(soClient, callCluster), outputService.ensureDefaultOutput(soClient), - agentPolicyService.ensureDefaultAgentPolicy(soClient), + agentPolicyService.ensureDefaultAgentPolicy(soClient, esClient), + updateFleetRoleIfExists(callCluster), settingsService.getSettings(soClient).catch((e: any) => { if (e.isBoom && e.output.statusCode === 404) { const defaultSettings = createDefaultSettings(); @@ -109,6 +112,7 @@ async function createSetupSideEffects( if (!isInstalled) { await addPackageToAgentPolicy( soClient, + esClient, callCluster, installedPackage, agentPolicyWithPackagePolicies, @@ -123,14 +127,25 @@ async function createSetupSideEffects( return { isIntialized: true }; } -export async function setupFleet( - soClient: SavedObjectsClientContract, - callCluster: CallESAsCurrentUser, - options?: { forceRecreate?: boolean } -) { - // Create fleet_enroll role - // This should be done directly in ES at some point - const res = await callCluster('transport.request', { +async function updateFleetRoleIfExists(callCluster: CallESAsCurrentUser) { + try { + await callCluster('transport.request', { + method: 'GET', + path: `/_security/role/${FLEET_ENROLL_ROLE}`, + }); + } catch (e) { + if (e.status === 404) { + return; + } + + throw e; + } + + return putFleetRole(callCluster); +} + +async function putFleetRole(callCluster: CallESAsCurrentUser) { + return callCluster('transport.request', { method: 'PUT', path: `/_security/role/${FLEET_ENROLL_ROLE}`, body: { @@ -152,6 +167,18 @@ export async function setupFleet( ], }, }); +} + +export async function setupFleet( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, + callCluster: CallESAsCurrentUser, + options?: { forceRecreate?: boolean } +) { + // Create fleet_enroll role + // This should be done directly in ES at some point + const res = await putFleetRole(callCluster); + // If the role is already created skip the rest unless you have forceRecreate set to true if (options?.forceRecreate !== true && res.role.created === false) { return; @@ -189,7 +216,7 @@ export async function setupFleet( await Promise.all( agentPolicies.map((agentPolicy) => { - return generateEnrollmentAPIKey(soClient, { + return generateEnrollmentAPIKey(soClient, esClient, { name: `Default`, agentPolicyId: agentPolicy.id, }); @@ -209,6 +236,7 @@ function generateRandomPassword() { async function addPackageToAgentPolicy( soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, callCluster: CallESAsCurrentUser, packageToInstall: Installation, agentPolicy: AgentPolicy, @@ -227,7 +255,7 @@ async function addPackageToAgentPolicy( agentPolicy.namespace ); - await packagePolicyService.create(soClient, callCluster, newPackagePolicy, { + await packagePolicyService.create(soClient, esClient, callCluster, newPackagePolicy, { bumpRevision: false, }); } diff --git a/x-pack/plugins/fleet/server/types/index.tsx b/x-pack/plugins/fleet/server/types/index.tsx index 7e6e6d5e408b..d3ac40215917 100644 --- a/x-pack/plugins/fleet/server/types/index.tsx +++ b/x-pack/plugins/fleet/server/types/index.tsx @@ -77,6 +77,8 @@ export { PostAgentCheckinRequest, DataType, dataTypes, + // Fleet Server types + FleetServerEnrollmentAPIKey, } from '../../common'; export type CallESAsCurrentUser = LegacyScopedClusterClient['callAsCurrentUser']; diff --git a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts index 3e9262c2a912..a37002114c77 100644 --- a/x-pack/plugins/fleet/server/types/rest_spec/agent.ts +++ b/x-pack/plugins/fleet/server/types/rest_spec/agent.ts @@ -83,6 +83,7 @@ export const PostAgentEnrollRequestBodyJSONSchema = { type: 'object', properties: { type: { type: 'string', enum: ['EPHEMERAL', 'PERMANENT', 'TEMPORARY'] }, + // TODO deprecated should be removed in 8.0.0 shared_id: { type: 'string' }, metadata: { type: 'object', diff --git a/x-pack/plugins/global_search/server/mocks.ts b/x-pack/plugins/global_search/server/mocks.ts index 88be7f6e861a..f0498302808e 100644 --- a/x-pack/plugins/global_search/server/mocks.ts +++ b/x-pack/plugins/global_search/server/mocks.ts @@ -9,9 +9,11 @@ import { GlobalSearchPluginSetup, GlobalSearchPluginStart, RouteHandlerGlobalSearchContext, + GlobalSearchRequestHandlerContext, } from './types'; import { searchServiceMock } from './services/search_service.mock'; import { contextMock } from './services/context.mock'; +import { coreMock } from '../../../../src/core/server/mocks'; const createSetupMock = (): jest.Mocked => { const searchMock = searchServiceMock.createSetupContract(); @@ -41,9 +43,21 @@ const createRouteHandlerContextMock = (): jest.Mocked => { + const handlerContextMock = { + find: jest.fn(), + getSearchableTypes: jest.fn(), + }; + + handlerContextMock.find.mockReturnValue(of([])); + + return { core: coreMock.createRequestHandlerContext(), globalSearch: handlerContextMock }; +}; + export const globalSearchPluginMock = { createSetupContract: createSetupMock, createStartContract: createStartMock, createRouteHandlerContext: createRouteHandlerContextMock, createProviderContext: contextMock.create, + createRequestHandlerContext: createRequestHandlerContextMock, }; diff --git a/x-pack/plugins/global_search/server/plugin.ts b/x-pack/plugins/global_search/server/plugin.ts index 9d6844dde50f..29c63efd64df 100644 --- a/x-pack/plugins/global_search/server/plugin.ts +++ b/x-pack/plugins/global_search/server/plugin.ts @@ -14,16 +14,10 @@ import { registerRoutes } from './routes'; import { GlobalSearchPluginSetup, GlobalSearchPluginStart, - RouteHandlerGlobalSearchContext, + GlobalSearchRequestHandlerContext, } from './types'; import { GlobalSearchConfigType } from './config'; -declare module 'src/core/server' { - interface RequestHandlerContext { - globalSearch?: RouteHandlerGlobalSearchContext; - } -} - // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface GlobalSearchPluginSetupDeps {} export interface GlobalSearchPluginStartDeps { @@ -56,12 +50,15 @@ export class GlobalSearchPlugin registerRoutes(core.http.createRouter()); - core.http.registerRouteHandlerContext('globalSearch', (_, req) => { - return { - find: (term, options) => this.searchServiceStart!.find(term, options, req), - getSearchableTypes: () => this.searchServiceStart!.getSearchableTypes(req), - }; - }); + core.http.registerRouteHandlerContext( + 'globalSearch', + (_, req) => { + return { + find: (term, options) => this.searchServiceStart!.find(term, options, req), + getSearchableTypes: () => this.searchServiceStart!.getSearchableTypes(req), + }; + } + ); return { registerResultProvider, diff --git a/x-pack/plugins/global_search/server/routes/find.ts b/x-pack/plugins/global_search/server/routes/find.ts index 0b82a035348e..f9bc0d869882 100644 --- a/x-pack/plugins/global_search/server/routes/find.ts +++ b/x-pack/plugins/global_search/server/routes/find.ts @@ -6,10 +6,10 @@ import { reduce, map } from 'rxjs/operators'; import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import { GlobalSearchRouter } from '../types'; import { GlobalSearchFindError } from '../../common/errors'; -export const registerInternalFindRoute = (router: IRouter) => { +export const registerInternalFindRoute = (router: GlobalSearchRouter) => { router.post( { path: '/internal/global_search/find', diff --git a/x-pack/plugins/global_search/server/routes/get_searchable_types.ts b/x-pack/plugins/global_search/server/routes/get_searchable_types.ts index f9cc69e4a28a..c2e58b0cd9ba 100644 --- a/x-pack/plugins/global_search/server/routes/get_searchable_types.ts +++ b/x-pack/plugins/global_search/server/routes/get_searchable_types.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; +import { GlobalSearchRouter } from '../types'; -export const registerInternalSearchableTypesRoute = (router: IRouter) => { +export const registerInternalSearchableTypesRoute = (router: GlobalSearchRouter) => { router.get( { path: '/internal/global_search/searchable_types', diff --git a/x-pack/plugins/global_search/server/routes/index.ts b/x-pack/plugins/global_search/server/routes/index.ts index 0eeb443b72b5..7f11f01cbc46 100644 --- a/x-pack/plugins/global_search/server/routes/index.ts +++ b/x-pack/plugins/global_search/server/routes/index.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; +import { GlobalSearchRouter } from '../types'; import { registerInternalFindRoute } from './find'; import { registerInternalSearchableTypesRoute } from './get_searchable_types'; -export const registerRoutes = (router: IRouter) => { +export const registerRoutes = (router: GlobalSearchRouter) => { registerInternalFindRoute(router); registerInternalSearchableTypesRoute(router); }; diff --git a/x-pack/plugins/global_search/server/routes/integration_tests/find.test.ts b/x-pack/plugins/global_search/server/routes/integration_tests/find.test.ts index c37bcdbf8474..db46cdfff360 100644 --- a/x-pack/plugins/global_search/server/routes/integration_tests/find.test.ts +++ b/x-pack/plugins/global_search/server/routes/integration_tests/find.test.ts @@ -41,13 +41,12 @@ describe('POST /internal/global_search/find', () => { ({ server, httpSetup } = await setupServer(pluginId)); globalSearchHandlerContext = globalSearchPluginMock.createRouteHandlerContext(); - httpSetup.registerRouteHandlerContext( - pluginId, - 'globalSearch', - () => globalSearchHandlerContext - ); + httpSetup.registerRouteHandlerContext< + ReturnType, + 'globalSearch' + >(pluginId, 'globalSearch', () => globalSearchHandlerContext); - const router = httpSetup.createRouter('/'); + const router = httpSetup.createRouter('/'); registerInternalFindRoute(router); diff --git a/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts b/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts index b3b6862599d6..66528e4fbe85 100644 --- a/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts +++ b/x-pack/plugins/global_search/server/routes/integration_tests/get_searchable_types.test.ts @@ -24,13 +24,14 @@ describe('GET /internal/global_search/searchable_types', () => { ({ server, httpSetup } = await setupServer(pluginId)); globalSearchHandlerContext = globalSearchPluginMock.createRouteHandlerContext(); - httpSetup.registerRouteHandlerContext( - pluginId, - 'globalSearch', - () => globalSearchHandlerContext - ); - - const router = httpSetup.createRouter('/'); + httpSetup.registerRouteHandlerContext< + ReturnType, + 'globalSearch' + >(pluginId, 'globalSearch', () => globalSearchHandlerContext); + + const router = httpSetup.createRouter< + ReturnType + >('/'); registerInternalSearchableTypesRoute(router); diff --git a/x-pack/plugins/global_search/server/types.ts b/x-pack/plugins/global_search/server/types.ts index 48c40fdb66e1..17b3505041f0 100644 --- a/x-pack/plugins/global_search/server/types.ts +++ b/x-pack/plugins/global_search/server/types.ts @@ -5,12 +5,14 @@ */ import { Observable } from 'rxjs'; -import { +import type { ISavedObjectTypeRegistry, ILegacyScopedClusterClient, IUiSettingsClient, SavedObjectsClientContract, Capabilities, + IRouter, + RequestHandlerContext, } from 'src/core/server'; import { GlobalSearchBatchedResults, @@ -24,6 +26,17 @@ import { SearchServiceSetup, SearchServiceStart } from './services'; export type GlobalSearchPluginSetup = Pick; export type GlobalSearchPluginStart = Pick; +/** + * @internal + */ +export interface GlobalSearchRequestHandlerContext extends RequestHandlerContext { + globalSearch: RouteHandlerGlobalSearchContext; +} + +/** + * @internal + */ +export type GlobalSearchRouter = IRouter; /** * globalSearch route handler context. * diff --git a/x-pack/plugins/index_management/server/plugin.ts b/x-pack/plugins/index_management/server/plugin.ts index 99facacacfe4..3717e7e94d29 100644 --- a/x-pack/plugins/index_management/server/plugin.ts +++ b/x-pack/plugins/index_management/server/plugin.ts @@ -4,19 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -declare module 'kibana/server' { - interface RequestHandlerContext { - dataManagement?: DataManagementContext; - } -} - import { i18n } from '@kbn/i18n'; import { CoreSetup, Plugin, Logger, PluginInitializerContext, - ILegacyScopedClusterClient, ILegacyCustomClusterClient, } from 'src/core/server'; @@ -26,10 +19,7 @@ import { ApiRoutes } from './routes'; import { License, IndexDataEnricher } from './services'; import { isEsError, handleEsError, parseEsError } from './shared_imports'; import { elasticsearchJsPlugin } from './client/elasticsearch'; - -export interface DataManagementContext { - client: ILegacyScopedClusterClient; -} +import type { IndexManagementRequestHandlerContext } from './types'; export interface IndexManagementPluginSetup { indexDataEnricher: { @@ -61,7 +51,7 @@ export class IndexMgmtServerPlugin implements Plugin(); this.license.setup( { @@ -92,14 +82,17 @@ export class IndexMgmtServerPlugin implements Plugin { - this.dataManagementESClient = - this.dataManagementESClient ?? (await getCustomEsClient(getStartServices)); + http.registerRouteHandlerContext( + 'dataManagement', + async (ctx, request) => { + this.dataManagementESClient = + this.dataManagementESClient ?? (await getCustomEsClient(getStartServices)); - return { - client: this.dataManagementESClient.asScoped(request), - }; - }); + return { + client: this.dataManagementESClient.asScoped(request), + }; + } + ); this.apiRoutes.setup({ router, diff --git a/x-pack/plugins/index_management/server/services/license.ts b/x-pack/plugins/index_management/server/services/license.ts index 9b68acd073c4..22497a9d45ec 100644 --- a/x-pack/plugins/index_management/server/services/license.ts +++ b/x-pack/plugins/index_management/server/services/license.ts @@ -4,15 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import { Logger } from 'src/core/server'; -import { - KibanaRequest, - KibanaResponseFactory, - RequestHandler, - RequestHandlerContext, -} from 'kibana/server'; +import type { KibanaRequest, KibanaResponseFactory, RequestHandler } from 'kibana/server'; import { LicensingPluginSetup } from '../../../licensing/server'; import { LicenseType } from '../../../licensing/common/types'; +import type { IndexManagementRequestHandlerContext } from '../types'; export interface LicenseStatus { isValid: boolean; @@ -53,11 +49,13 @@ export class License { }); } - guardApiRoute(handler: RequestHandler) { + guardApiRoute( + handler: RequestHandler + ) { const license = this; return function licenseCheck( - ctx: RequestHandlerContext, + ctx: Context, request: KibanaRequest, response: KibanaResponseFactory ) { diff --git a/x-pack/plugins/index_management/server/types.ts b/x-pack/plugins/index_management/server/types.ts index 16a6b43af851..34d03129c62d 100644 --- a/x-pack/plugins/index_management/server/types.ts +++ b/x-pack/plugins/index_management/server/types.ts @@ -3,7 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { LegacyScopedClusterClient, IRouter } from 'src/core/server'; +import type { + LegacyScopedClusterClient, + ILegacyScopedClusterClient, + IRouter, + RequestHandlerContext, +} from 'src/core/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { SecurityPluginSetup } from '../../security/server'; @@ -17,7 +22,7 @@ export interface Dependencies { } export interface RouteDependencies { - router: IRouter; + router: IndexManagementRouter; license: License; config: { isSecurityEnabled: () => boolean; @@ -31,3 +36,26 @@ export interface RouteDependencies { } export type CallAsCurrentUser = LegacyScopedClusterClient['callAsCurrentUser']; + +export interface DataManagementContext { + client: ILegacyScopedClusterClient; +} + +/** + * @internal + */ +export interface IndexManagementApiRequestHandlerContext { + client: ILegacyScopedClusterClient; +} + +/** + * @internal + */ +export interface IndexManagementRequestHandlerContext extends RequestHandlerContext { + dataManagement: IndexManagementApiRequestHandlerContext; +} + +/** + * @internal + */ +export type IndexManagementRouter = IRouter; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap index b8cdc0acac1d..a5d97813e4b1 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/__snapshots__/conditional_tooltip.test.tsx.snap @@ -21,9 +21,10 @@ exports[`ConditionalToolTip should just work 1`] = ` host-01
CPU usage @@ -35,9 +36,10 @@ exports[`ConditionalToolTip should just work 1`] = ` Memory usage @@ -49,9 +51,10 @@ exports[`ConditionalToolTip should just work 1`] = ` Outbound traffic @@ -63,9 +66,10 @@ exports[`ConditionalToolTip should just work 1`] = ` Inbound traffic @@ -76,5 +80,35 @@ exports[`ConditionalToolTip should just work 1`] = ` 8Mbit/s + + + My Custom Label + + + 34.1% + + + + + Avg of host.network.out.packets + + + 4,392.2 + + `; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx index e01ca3ab6e84..fbca85e2d449 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.test.tsx @@ -22,7 +22,12 @@ jest.mock('../../../../../containers/source', () => ({ jest.mock('../../hooks/use_snaphot'); import { useSnapshot } from '../../hooks/use_snaphot'; +jest.mock('../../hooks/use_waffle_options'); +import { useWaffleOptionsContext } from '../../hooks/use_waffle_options'; const mockedUseSnapshot = useSnapshot as jest.Mock>; +const mockedUseWaffleOptionsContext = useWaffleOptionsContext as jest.Mock< + ReturnType +>; const NODE: InfraWaffleMapNode = { pathId: 'host-01', @@ -50,6 +55,7 @@ const ChildComponent = () =>
child
; describe('ConditionalToolTip', () => { afterEach(() => { mockedUseSnapshot.mockReset(); + mockedUseWaffleOptionsContext.mockReset(); }); function createWrapper(currentTime: number = Date.now(), hidden: boolean = false) { @@ -77,6 +83,7 @@ describe('ConditionalToolTip', () => { interval: '', reload: jest.fn(() => Promise.resolve()), }); + mockedUseWaffleOptionsContext.mockReturnValue(mockedUseWaffleOptionsContexReturnValue); const currentTime = Date.now(); const wrapper = createWrapper(currentTime, true); expect(wrapper.find(ChildComponent).exists()).toBeTruthy(); @@ -95,6 +102,18 @@ describe('ConditionalToolTip', () => { { name: 'memory', value: 0.8, avg: 0.8, max: 1 }, { name: 'tx', value: 1000000, avg: 1000000, max: 1000000 }, { name: 'rx', value: 1000000, avg: 1000000, max: 1000000 }, + { + name: 'cedd6ca0-5775-11eb-a86f-adb714b6c486', + max: 0.34164999922116596, + value: 0.34140000740687054, + avg: 0.20920833365784752, + }, + { + name: 'e12dd700-5775-11eb-a86f-adb714b6c486', + max: 4703.166666666667, + value: 4392.166666666667, + avg: 3704.6666666666674, + }, ], }, ], @@ -103,6 +122,7 @@ describe('ConditionalToolTip', () => { interval: '60s', reload: reloadMock, }); + mockedUseWaffleOptionsContext.mockReturnValue(mockedUseWaffleOptionsContexReturnValue); const currentTime = Date.now(); const wrapper = createWrapper(currentTime, false); expect(wrapper.find(ChildComponent).exists()).toBeTruthy(); @@ -114,7 +134,25 @@ describe('ConditionalToolTip', () => { }, }, }); - const expectedMetrics = [{ type: 'cpu' }, { type: 'memory' }, { type: 'tx' }, { type: 'rx' }]; + const expectedMetrics = [ + { type: 'cpu' }, + { type: 'memory' }, + { type: 'tx' }, + { type: 'rx' }, + { + aggregation: 'avg', + field: 'host.cpu.pct', + id: 'cedd6ca0-5775-11eb-a86f-adb714b6c486', + label: 'My Custom Label', + type: 'custom', + }, + { + aggregation: 'avg', + field: 'host.network.out.packets', + id: 'e12dd700-5775-11eb-a86f-adb714b6c486', + type: 'custom', + }, + ]; expect(mockedUseSnapshot).toBeCalledWith( expectedQuery, expectedMetrics, @@ -143,6 +181,7 @@ describe('ConditionalToolTip', () => { interval: '', reload: reloadMock, }); + mockedUseWaffleOptionsContext.mockReturnValue(mockedUseWaffleOptionsContexReturnValue); const currentTime = Date.now(); const wrapper = createWrapper(currentTime, false); expect(wrapper.find(ChildComponent).exists()).toBeTruthy(); @@ -154,3 +193,44 @@ describe('ConditionalToolTip', () => { expect(reloadMock).not.toHaveBeenCalled(); }); }); + +const mockedUseWaffleOptionsContexReturnValue: ReturnType = { + changeMetric: jest.fn(() => {}), + changeGroupBy: jest.fn(() => {}), + changeNodeType: jest.fn(() => {}), + changeView: jest.fn(() => {}), + changeCustomOptions: jest.fn(() => {}), + changeAutoBounds: jest.fn(() => {}), + changeBoundsOverride: jest.fn(() => {}), + changeAccount: jest.fn(() => {}), + changeRegion: jest.fn(() => {}), + changeCustomMetrics: jest.fn(() => {}), + changeLegend: jest.fn(() => {}), + changeSort: jest.fn(() => {}), + setWaffleOptionsState: jest.fn(() => {}), + boundsOverride: { max: 1, min: 0 }, + autoBounds: true, + accountId: '', + region: '', + sort: { by: 'name', direction: 'desc' }, + groupBy: [], + nodeType: 'host', + customOptions: [], + view: 'map', + metric: { type: 'cpu' }, + customMetrics: [ + { + aggregation: 'avg', + field: 'host.cpu.pct', + id: 'cedd6ca0-5775-11eb-a86f-adb714b6c486', + label: 'My Custom Label', + type: 'custom', + }, + { + aggregation: 'avg', + field: 'host.network.out.packets', + id: 'e12dd700-5775-11eb-a86f-adb714b6c486', + type: 'custom', + }, + ], +}; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx index 8082752a88b7..7ec1ae905a64 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx @@ -6,6 +6,8 @@ import React, { useCallback, useState, useEffect } from 'react'; import { EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { first } from 'lodash'; +import { getCustomMetricLabel } from '../../../../../../common/formatters/get_custom_metric_label'; +import { SnapshotCustomMetricInput } from '../../../../../../common/http_api'; import { withTheme, EuiTheme } from '../../../../../../../observability/public'; import { useSourceContext } from '../../../../../containers/source'; import { findInventoryModel } from '../../../../../../common/inventory_models'; @@ -18,6 +20,8 @@ import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../../lib/li import { useSnapshot } from '../../hooks/use_snaphot'; import { createInventoryMetricFormatter } from '../../lib/create_inventory_metric_formatter'; import { SNAPSHOT_METRIC_TRANSLATIONS } from '../../../../../../common/inventory_models/intl_strings'; +import { useWaffleOptionsContext } from '../../hooks/use_waffle_options'; +import { createFormatterForMetric } from '../../../metrics_explorer/components/helpers/create_formatter_for_metric'; export interface Props { currentTime: number; @@ -35,9 +39,15 @@ export const ConditionalToolTip = withTheme( const { sourceId } = useSourceContext(); const [timer, setTimer] = useState | null>(null); const model = findInventoryModel(nodeType); - const requestMetrics = model.tooltipMetrics.map((type) => ({ type })) as Array<{ - type: SnapshotMetricType; - }>; + const { customMetrics } = useWaffleOptionsContext(); + const requestMetrics = model.tooltipMetrics + .map((type) => ({ type })) + .concat(customMetrics) as Array< + | { + type: SnapshotMetricType; + } + | SnapshotCustomMetricInput + >; const query = JSON.stringify({ bool: { filter: { @@ -45,7 +55,6 @@ export const ConditionalToolTip = withTheme( }, }, }); - const { nodes, reload } = useSnapshot( query, requestMetrics, @@ -74,7 +83,6 @@ export const ConditionalToolTip = withTheme( if (hidden) { return children; } - const dataNode = first(nodes); const metrics = (dataNode && dataNode.metrics) || []; const content = ( @@ -91,10 +99,18 @@ export const ConditionalToolTip = withTheme( {metrics.map((metric) => { const metricName = SnapshotMetricTypeRT.is(metric.name) ? metric.name : 'custom'; const name = SNAPSHOT_METRIC_TRANSLATIONS[metricName] || metricName; - const formatter = createInventoryMetricFormatter({ type: metricName }); + // if custom metric, find field and label from waffleOptionsContext result + // because useSnapshot does not return it + const customMetric = + name === 'custom' ? customMetrics.find((item) => item.id === metric.name) : null; + const formatter = customMetric + ? createFormatterForMetric(customMetric) + : createInventoryMetricFormatter({ type: metricName }); return ( - - {name} + + + {customMetric ? getCustomMetricLabel(customMetric) : name} + {(metric.value && formatter(metric.value)) || '-'} diff --git a/x-pack/plugins/infra/server/lib/adapters/fields/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/fields/adapter_types.ts index a1630281c2f7..d6b069997b61 100644 --- a/x-pack/plugins/infra/server/lib/adapters/fields/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/fields/adapter_types.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; export interface FieldsAdapter { getIndexFields( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, indices: string ): Promise; } diff --git a/x-pack/plugins/infra/server/lib/adapters/fields/framework_fields_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/fields/framework_fields_adapter.ts index 8a9389ed585e..57345f1353e9 100644 --- a/x-pack/plugins/infra/server/lib/adapters/fields/framework_fields_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/fields/framework_fields_adapter.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; import { KibanaFramework } from '../framework/kibana_framework_adapter'; import { FieldsAdapter, IndexFieldDescriptor } from './adapter_types'; @@ -16,7 +16,7 @@ export class FrameworkFieldsAdapter implements FieldsAdapter { } public async getIndexFields( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, indices: string ): Promise { const indexPatternsService = this.framework.getIndexPatternsService(requestContext); diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts index 7f686b4d7717..b96b0e5bb0b4 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/kibana_framework_adapter.ts @@ -23,16 +23,16 @@ import { CoreSetup, IRouter, KibanaRequest, - RequestHandlerContext, KibanaResponseFactory, RouteMethod, } from '../../../../../../../src/core/server'; import { RequestHandler } from '../../../../../../../src/core/server'; import { InfraConfig } from '../../../plugin'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; import { IndexPatternsFetcher, UI_SETTINGS } from '../../../../../../../src/plugins/data/server'; export class KibanaFramework { - public router: IRouter; + public router: IRouter; public plugins: InfraServerPluginSetupDeps; constructor(core: CoreSetup, config: InfraConfig, plugins: InfraServerPluginSetupDeps) { @@ -42,7 +42,7 @@ export class KibanaFramework { public registerRoute( config: InfraRouteConfig, - handler: RequestHandler + handler: RequestHandler ) { const defaultOptions = { tags: ['access:infra'], @@ -88,7 +88,7 @@ export class KibanaFramework { }, }; async function handler( - context: RequestHandlerContext, + context: InfraPluginRequestHandlerContext, request: KibanaRequest, response: KibanaResponseFactory ) { @@ -100,7 +100,7 @@ export class KibanaFramework { const gqlResponse = await runHttpQuery([context, request], { method: request.route.method.toUpperCase(), - options: (req: RequestHandlerContext, rawReq: KibanaRequest) => ({ + options: (req: InfraPluginRequestHandlerContext, rawReq: KibanaRequest) => ({ context: { req, rawReq }, schema: gqlSchema, }), @@ -147,48 +147,48 @@ export class KibanaFramework { } callWithRequest( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, endpoint: 'search', options?: CallWithRequestParams ): Promise>; callWithRequest( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, endpoint: 'msearch', options?: CallWithRequestParams ): Promise>; callWithRequest( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, endpoint: 'fieldCaps', options?: CallWithRequestParams ): Promise; callWithRequest( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, endpoint: 'indices.existsAlias', options?: CallWithRequestParams ): Promise; callWithRequest( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, method: 'indices.getAlias', options?: object ): Promise; callWithRequest( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, method: 'indices.get' | 'ml.getBuckets', options?: object ): Promise; callWithRequest( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, method: 'transport.request', options?: CallWithRequestParams ): Promise; callWithRequest( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, endpoint: string, options?: CallWithRequestParams ): Promise; public async callWithRequest( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, endpoint: string, params: CallWithRequestParams ) { @@ -216,7 +216,9 @@ export class KibanaFramework { }); } - public getIndexPatternsService(requestContext: RequestHandlerContext): IndexPatternsFetcher { + public getIndexPatternsService( + requestContext: InfraPluginRequestHandlerContext + ): IndexPatternsFetcher { return new IndexPatternsFetcher(requestContext.core.elasticsearch.client.asCurrentUser, true); } @@ -235,7 +237,7 @@ export class KibanaFramework { } public async makeTSVBRequest( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, rawRequest: KibanaRequest, model: TSVBMetricModel, timerange: { min: number; max: number }, diff --git a/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts index 98c42ab7d98a..ffbc750af14f 100644 --- a/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts @@ -10,8 +10,8 @@ import { constant, identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; import * as runtimeTypes from 'io-ts'; import { compact } from 'lodash'; -import { RequestHandlerContext } from 'src/core/server'; import { JsonArray } from '../../../../../../../src/plugins/kibana_utils/common'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; import { LogEntriesAdapter, LogEntriesParams, @@ -30,7 +30,7 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { constructor(private readonly framework: KibanaFramework) {} public async getLogEntries( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, fields: string[], params: LogEntriesParams @@ -123,7 +123,7 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { } public async getContainedLogSummaryBuckets( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, startTimestamp: number, endTimestamp: number, diff --git a/x-pack/plugins/infra/server/lib/adapters/metrics/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/adapter_types.ts index f786c043ee27..e20f1ab05fd5 100644 --- a/x-pack/plugins/infra/server/lib/adapters/metrics/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/metrics/adapter_types.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext, KibanaRequest } from 'src/core/server'; +import { KibanaRequest } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; import { NodeDetailsRequest, NodeDetailsMetricData, @@ -23,7 +24,7 @@ export interface InfraMetricsRequestOptions export interface InfraMetricsAdapter { getMetrics( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, options: InfraMetricsRequestOptions, request: KibanaRequest ): Promise; diff --git a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts index 5718d49ae79d..2c7d79b1c64d 100644 --- a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import { flatten, get } from 'lodash'; -import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; +import { KibanaRequest } from 'src/core/server'; import { NodeDetailsMetricData } from '../../../../common/http_api/node_details_api'; import { KibanaFramework } from '../framework/kibana_framework_adapter'; import { InfraMetricsAdapter, InfraMetricsRequestOptions } from './adapter_types'; @@ -19,6 +19,7 @@ import { } from '../../../../common/inventory_models/types'; import { calculateMetricInterval } from '../../../utils/calculate_metric_interval'; import { CallWithRequestParams, InfraDatabaseSearchResponse } from '../framework'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; export class KibanaMetricsAdapter implements InfraMetricsAdapter { private framework: KibanaFramework; @@ -28,7 +29,7 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter { } public async getMetrics( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, options: InfraMetricsRequestOptions, rawRequest: KibanaRequest ): Promise { @@ -94,7 +95,7 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter { metricId: InventoryMetric, options: InfraMetricsRequestOptions, nodeField: string, - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, rawRequest: KibanaRequest ) { const createTSVBModel = get(metrics, ['tsvb', metricId]) as TSVBMetricModelCreator | undefined; diff --git a/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts index 2a61e64c94fc..cfa001788246 100644 --- a/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; import { InfraSourceStatusAdapter, SourceIndexStatus } from '../../source_status'; import { InfraDatabaseGetIndicesResponse } from '../framework'; import { KibanaFramework } from '../framework/kibana_framework_adapter'; @@ -12,7 +12,7 @@ import { KibanaFramework } from '../framework/kibana_framework_adapter'; export class InfraElasticsearchSourceStatusAdapter implements InfraSourceStatusAdapter { constructor(private readonly framework: KibanaFramework) {} - public async getIndexNames(requestContext: RequestHandlerContext, aliasName: string) { + public async getIndexNames(requestContext: InfraPluginRequestHandlerContext, aliasName: string) { const indexMaps = await Promise.all([ this.framework .callWithRequest(requestContext, 'indices.getAlias', { @@ -34,14 +34,14 @@ export class InfraElasticsearchSourceStatusAdapter implements InfraSourceStatusA ); } - public async hasAlias(requestContext: RequestHandlerContext, aliasName: string) { + public async hasAlias(requestContext: InfraPluginRequestHandlerContext, aliasName: string) { return await this.framework.callWithRequest(requestContext, 'indices.existsAlias', { name: aliasName, }); } public async getIndexStatus( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, indexNames: string ): Promise { return await this.framework diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts index e1657968b3f9..0ecea1f5db32 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_chart_preview.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; import { InfraSource } from '../../sources'; import { KibanaFramework } from '../../adapters/framework/kibana_framework_adapter'; import { @@ -29,7 +29,7 @@ import { decodeOrThrow } from '../../../../common/runtime_types'; const COMPOSITE_GROUP_SIZE = 40; export async function getChartPreviewData( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceConfiguration: InfraSource, callWithRequest: KibanaFramework['callWithRequest'], alertParams: GetLogAlertsChartPreviewDataAlertParamsSubset, @@ -114,7 +114,7 @@ const addHistogramAggregationToQuery = ( const getUngroupedResults = async ( query: object, - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, callWithRequest: KibanaFramework['callWithRequest'] ) => { return decodeOrThrow(UngroupedSearchQueryResponseRT)( @@ -124,7 +124,7 @@ const getUngroupedResults = async ( const getGroupedResults = async ( query: object, - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, callWithRequest: KibanaFramework['callWithRequest'] ) => { let compositeGroupBuckets: GroupedSearchQueryResponse['aggregations']['groups']['buckets'] = []; diff --git a/x-pack/plugins/infra/server/lib/create_search_client.ts b/x-pack/plugins/infra/server/lib/create_search_client.ts index d79d20b502e9..cc354754c340 100644 --- a/x-pack/plugins/infra/server/lib/create_search_client.ts +++ b/x-pack/plugins/infra/server/lib/create_search_client.ts @@ -3,12 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../types'; import { CallWithRequestParams, InfraDatabaseSearchResponse } from './adapters/framework'; import { KibanaFramework } from './adapters/framework/kibana_framework_adapter'; export const createSearchClient = ( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, framework: KibanaFramework ) => ( opts: CallWithRequestParams diff --git a/x-pack/plugins/infra/server/lib/domains/fields_domain.ts b/x-pack/plugins/infra/server/lib/domains/fields_domain.ts index ecbc71f4895c..a8bd09c28f94 100644 --- a/x-pack/plugins/infra/server/lib/domains/fields_domain.ts +++ b/x-pack/plugins/infra/server/lib/domains/fields_domain.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../types'; import { InfraIndexField, InfraIndexType } from '../../graphql/types'; import { FieldsAdapter } from '../adapters/fields'; import { InfraSources } from '../sources'; @@ -16,7 +16,7 @@ export class InfraFieldsDomain { ) {} public async getFields( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceId: string, indexType: InfraIndexType ): Promise { diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts index d9f125908b32..0b1df3abd465 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts +++ b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/log_entries_domain.ts @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; import { JsonObject } from '../../../../../../../src/plugins/kibana_utils/common'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; + import { LogEntriesSummaryBucket, LogEntriesSummaryHighlightsBucket, @@ -68,7 +69,7 @@ export class InfraLogEntriesDomain { ) {} public async getLogEntriesAround( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceId: string, params: LogEntriesAroundParams, columnOverrides?: LogEntriesRequest['columns'] @@ -128,7 +129,7 @@ export class InfraLogEntriesDomain { } public async getLogEntries( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceId: string, params: LogEntriesParams, columnOverrides?: LogEntriesRequest['columns'] @@ -187,7 +188,7 @@ export class InfraLogEntriesDomain { } public async getLogSummaryBucketsBetween( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceId: string, start: number, end: number, @@ -210,7 +211,7 @@ export class InfraLogEntriesDomain { } public async getLogSummaryHighlightBucketsBetween( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceId: string, startTimestamp: number, endTimestamp: number, @@ -256,7 +257,7 @@ export class InfraLogEntriesDomain { } public async getLogEntryDatasets( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, timestampField: string, indexName: string, startTime: number, @@ -297,14 +298,14 @@ export class InfraLogEntriesDomain { export interface LogEntriesAdapter { getLogEntries( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, fields: string[], params: LogEntriesParams ): Promise<{ documents: LogEntryDocument[]; hasMoreBefore?: boolean; hasMoreAfter?: boolean }>; getContainedLogSummaryBuckets( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, startTimestamp: number, endTimestamp: number, diff --git a/x-pack/plugins/infra/server/lib/domains/metrics_domain.ts b/x-pack/plugins/infra/server/lib/domains/metrics_domain.ts index ac76e264ff0e..0189fa885af0 100644 --- a/x-pack/plugins/infra/server/lib/domains/metrics_domain.ts +++ b/x-pack/plugins/infra/server/lib/domains/metrics_domain.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; +import { KibanaRequest } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../types'; import { InfraMetricsAdapter, InfraMetricsRequestOptions } from '../adapters/metrics/adapter_types'; import { NodeDetailsMetricData } from '../../../common/http_api/node_details_api'; @@ -16,7 +17,7 @@ export class InfraMetricsDomain { } public async getMetrics( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, options: InfraMetricsRequestOptions, rawRequest: KibanaRequest ): Promise { diff --git a/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts index c8278bd30875..6d6ac68a4d41 100644 --- a/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts +++ b/x-pack/plugins/infra/server/lib/infra_ml/metrics_hosts_anomalies.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../types'; import { InfraRequestHandlerContext } from '../../types'; import { TracingSpan, startTracingSpan } from '../../../common/performance_tracing'; import { fetchMlJob } from './common'; @@ -73,7 +73,7 @@ async function getCompatibleAnomaliesJobIds( } export async function getMetricsHostsAnomalies( - context: RequestHandlerContext & { infra: Required }, + context: InfraPluginRequestHandlerContext & { infra: Required }, sourceId: string, startTime: number, endTime: number, diff --git a/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts b/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts index c8427ef489c4..6d8ac7fa00d5 100644 --- a/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts +++ b/x-pack/plugins/infra/server/lib/infra_ml/metrics_k8s_anomalies.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../types'; import { InfraRequestHandlerContext } from '../../types'; import { TracingSpan, startTracingSpan } from '../../../common/performance_tracing'; import { fetchMlJob } from './common'; @@ -73,7 +73,7 @@ async function getCompatibleAnomaliesJobIds( } export async function getMetricK8sAnomalies( - context: RequestHandlerContext & { infra: Required }, + context: InfraPluginRequestHandlerContext & { infra: Required }, sourceId: string, startTime: number, endTime: number, diff --git a/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts index 44731fe465d2..c6a459391228 100644 --- a/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts +++ b/x-pack/plugins/infra/server/lib/log_analysis/log_entry_anomalies.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; -import { InfraRequestHandlerContext } from '../../types'; +import type { InfraPluginRequestHandlerContext, InfraRequestHandlerContext } from '../../types'; import { TracingSpan, startTracingSpan } from '../../../common/performance_tracing'; import { fetchMlJob, getLogEntryDatasets } from './common'; import { @@ -92,7 +91,7 @@ async function getCompatibleAnomaliesJobIds( } export async function getLogEntryAnomalies( - context: RequestHandlerContext & { infra: Required }, + context: InfraPluginRequestHandlerContext & { infra: Required }, sourceId: string, startTime: number, endTime: number, @@ -291,7 +290,7 @@ async function fetchLogEntryAnomalies( } export async function getLogEntryExamples( - context: RequestHandlerContext & { infra: Required }, + context: InfraPluginRequestHandlerContext & { infra: Required }, sourceId: string, startTime: number, endTime: number, @@ -353,7 +352,7 @@ export async function getLogEntryExamples( } export async function fetchLogEntryExamples( - context: RequestHandlerContext & { infra: Required }, + context: InfraPluginRequestHandlerContext & { infra: Required }, sourceId: string, indices: string, timestampField: string, diff --git a/x-pack/plugins/infra/server/lib/source_status.ts b/x-pack/plugins/infra/server/lib/source_status.ts index c383d0193356..5bfeff23f9ae 100644 --- a/x-pack/plugins/infra/server/lib/source_status.ts +++ b/x-pack/plugins/infra/server/lib/source_status.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../types'; import { InfraSources } from './sources'; export class InfraSourceStatus { @@ -14,7 +14,7 @@ export class InfraSourceStatus { ) {} public async getLogIndexNames( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceId: string ): Promise { const sourceConfiguration = await this.libs.sources.getSourceConfiguration( @@ -28,7 +28,7 @@ export class InfraSourceStatus { return indexNames; } public async getMetricIndexNames( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceId: string ): Promise { const sourceConfiguration = await this.libs.sources.getSourceConfiguration( @@ -42,7 +42,7 @@ export class InfraSourceStatus { return indexNames; } public async hasLogAlias( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceId: string ): Promise { const sourceConfiguration = await this.libs.sources.getSourceConfiguration( @@ -56,7 +56,7 @@ export class InfraSourceStatus { return hasAlias; } public async hasMetricAlias( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceId: string ): Promise { const sourceConfiguration = await this.libs.sources.getSourceConfiguration( @@ -70,7 +70,7 @@ export class InfraSourceStatus { return hasAlias; } public async getLogIndexStatus( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceId: string ): Promise { const sourceConfiguration = await this.libs.sources.getSourceConfiguration( @@ -84,7 +84,7 @@ export class InfraSourceStatus { return indexStatus; } public async hasMetricIndices( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceId: string ): Promise { const sourceConfiguration = await this.libs.sources.getSourceConfiguration( @@ -102,10 +102,13 @@ export class InfraSourceStatus { export type SourceIndexStatus = 'missing' | 'empty' | 'available'; export interface InfraSourceStatusAdapter { - getIndexNames(requestContext: RequestHandlerContext, aliasName: string): Promise; - hasAlias(requestContext: RequestHandlerContext, aliasName: string): Promise; + getIndexNames( + requestContext: InfraPluginRequestHandlerContext, + aliasName: string + ): Promise; + hasAlias(requestContext: InfraPluginRequestHandlerContext, aliasName: string): Promise; getIndexStatus( - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, indexNames: string ): Promise; } diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index 693e98521ada..207c2efa0f13 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -28,7 +28,7 @@ import { InfraBackendLibs, InfraDomainLibs } from './lib/infra_types'; import { infraSourceConfigurationSavedObjectType, InfraSources } from './lib/sources'; import { InfraSourceStatus } from './lib/source_status'; import { LogEntriesService } from './services/log_entries'; -import { InfraRequestHandlerContext } from './types'; +import { InfraPluginRequestHandlerContext } from './types'; import { UsageCollector } from './usage/usage_collector'; export const config = { @@ -146,9 +146,9 @@ export class InfraServerPlugin { initInfraServer(this.libs); registerAlertTypes(plugins.alerts, this.libs); - core.http.registerRouteHandlerContext( + core.http.registerRouteHandlerContext( 'infra', - (context, request): InfraRequestHandlerContext => { + (context, request) => { const mlSystem = plugins.ml?.mlSystemProvider(request, context.core.savedObjects.client); const mlAnomalyDetectors = plugins.ml?.anomalyDetectorsProvider( request, diff --git a/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts b/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts index af9e9c5f57c5..2be812043a0c 100644 --- a/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts +++ b/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'kibana/server'; import { InventoryCloudAccount } from '../../../../common/http_api/inventory_meta_api'; import { InfraMetadataAggregationResponse, @@ -14,6 +13,7 @@ import { InfraSourceConfiguration } from '../../../lib/sources'; import { KibanaFramework } from '../../../lib/adapters/framework/kibana_framework_adapter'; import { InventoryItemType } from '../../../../common/inventory_models/types'; import { findInventoryModel } from '../../../../common/inventory_models'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; export interface CloudMetaData { accounts: InventoryCloudAccount[]; @@ -23,7 +23,7 @@ export interface CloudMetaData { export const getCloudMetadata = async ( framework: KibanaFramework, - req: RequestHandlerContext, + req: InfraPluginRequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, nodeType: InventoryItemType, currentTime: number diff --git a/x-pack/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts index 82427a833a20..57d8fb9eb950 100644 --- a/x-pack/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts +++ b/x-pack/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; import { InfraMetadataAggregationBucket, InfraMetadataAggregationResponse, @@ -19,7 +19,7 @@ export interface InfraCloudMetricsAdapterResponse { export const getCloudMetricsMetadata = async ( framework: KibanaFramework, - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, instanceId: string, timeRange: { from: number; to: number } diff --git a/x-pack/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts index 7753d3161039..d1dec098af11 100644 --- a/x-pack/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts +++ b/x-pack/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts @@ -5,7 +5,7 @@ */ import { get } from 'lodash'; -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; import { InfraMetadataAggregationBucket, InfraMetadataAggregationResponse, @@ -23,7 +23,7 @@ export interface InfraMetricsAdapterResponse { export const getMetricMetadata = async ( framework: KibanaFramework, - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, nodeId: string, nodeType: InventoryItemType, diff --git a/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts index b378b42e2ff5..21ef61a55e35 100644 --- a/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts +++ b/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts @@ -6,7 +6,7 @@ import { set } from '@elastic/safer-lodash-set'; import { first, startsWith } from 'lodash'; -import { RequestHandlerContext } from 'src/core/server'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; import { KibanaFramework } from '../../../lib/adapters/framework/kibana_framework_adapter'; import { InfraSourceConfiguration } from '../../../lib/sources'; import { InfraMetadataInfo } from '../../../../common/http_api/metadata_api'; @@ -17,7 +17,7 @@ import { InventoryItemType } from '../../../../common/inventory_models/types'; export const getNodeInfo = async ( framework: KibanaFramework, - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, nodeId: string, nodeType: InventoryItemType, diff --git a/x-pack/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts index b4656178d395..e52976519ef4 100644 --- a/x-pack/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts +++ b/x-pack/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts @@ -5,14 +5,14 @@ */ import { first, get } from 'lodash'; -import { RequestHandlerContext } from 'src/core/server'; import { KibanaFramework } from '../../../lib/adapters/framework/kibana_framework_adapter'; import { InfraSourceConfiguration } from '../../../lib/sources'; import { findInventoryFields } from '../../../../common/inventory_models'; +import type { InfraPluginRequestHandlerContext } from '../../../types'; export const getPodNodeName = async ( framework: KibanaFramework, - requestContext: RequestHandlerContext, + requestContext: InfraPluginRequestHandlerContext, sourceConfiguration: InfraSourceConfiguration, nodeId: string, nodeType: 'host' | 'pod' | 'container', diff --git a/x-pack/plugins/infra/server/types.ts b/x-pack/plugins/infra/server/types.ts index 735569a790f6..2a30bf7cf093 100644 --- a/x-pack/plugins/infra/server/types.ts +++ b/x-pack/plugins/infra/server/types.ts @@ -3,7 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import type { RequestHandlerContext } from 'src/core/server'; +import type { DataApiRequestHandlerContext } from '../../../../src/plugins/data/server'; import { MlPluginSetup } from '../../ml/server'; export type MlSystem = ReturnType; @@ -21,8 +22,10 @@ export interface InfraSpacesRequestHandlerContext { export type InfraRequestHandlerContext = InfraMlRequestHandlerContext & InfraSpacesRequestHandlerContext; -declare module 'src/core/server' { - interface RequestHandlerContext { - infra?: InfraRequestHandlerContext; - } +/** + * @internal + */ +export interface InfraPluginRequestHandlerContext extends RequestHandlerContext { + infra: InfraRequestHandlerContext; + search: DataApiRequestHandlerContext; } diff --git a/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts b/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts index 6d16e045d26d..19169957f4c5 100644 --- a/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts +++ b/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -// import { RequestHandlerContext } from 'src/core/server'; import { findInventoryModel } from '../../common/inventory_models'; // import { KibanaFramework } from '../lib/adapters/framework/kibana_framework_adapter'; import { InventoryItemType } from '../../common/inventory_models/types'; diff --git a/x-pack/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/plugins/lens/public/app_plugin/app.test.tsx index 5e38cb49114e..5aec6b02c057 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.test.tsx @@ -10,7 +10,7 @@ import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { App } from './app'; import { LensAppProps, LensAppServices } from './types'; -import { EditorFrameInstance } from '../types'; +import { EditorFrameInstance, EditorFrameProps } from '../types'; import { Document } from '../persistence'; import { DOC_TYPE } from '../../common'; import { mount } from 'enzyme'; @@ -44,6 +44,8 @@ import { import { LensAttributeService } from '../lens_attribute_service'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { EmbeddableStateTransfer } from '../../../../../src/plugins/embeddable/public'; +import { NativeRenderer } from '../native_renderer'; +import moment from 'moment'; jest.mock('../editor_frame_service/editor_frame/expression_helpers'); jest.mock('src/core/public'); @@ -144,6 +146,11 @@ function createMockTimefilter() { return unsubscribe; }, }), + calculateBounds: jest.fn(() => ({ + min: moment('2021-01-10T04:00:00.000Z'), + max: moment('2021-01-10T08:00:00.000Z'), + })), + getBounds: jest.fn(() => timeFilter), getRefreshInterval: () => {}, getRefreshIntervalDefaults: () => {}, getAutoRefreshFetch$: () => ({ @@ -233,6 +240,9 @@ describe('Lens App', () => { }), }, search: createMockSearchService(), + nowProvider: { + get: jest.fn(), + }, } as unknown) as DataPublicPluginStart, storage: { get: jest.fn(), @@ -306,8 +316,8 @@ describe('Lens App', () => { />, Object { "dateRange": Object { - "fromDate": "now-7d", - "toDate": "now", + "fromDate": "2021-01-10T04:00:00.000Z", + "toDate": "2021-01-10T08:00:00.000Z", }, "doc": undefined, "filters": Array [], @@ -350,7 +360,7 @@ describe('Lens App', () => { expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ - dateRange: { fromDate: 'now-7d', toDate: 'now' }, + dateRange: { fromDate: '2021-01-10T04:00:00.000Z', toDate: '2021-01-10T08:00:00.000Z' }, query: { query: '', language: 'kuery' }, filters: [pinnedFilter], }) @@ -1008,7 +1018,7 @@ describe('Lens App', () => { expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ - dateRange: { fromDate: 'now-7d', toDate: 'now' }, + dateRange: { fromDate: '2021-01-10T04:00:00.000Z', toDate: '2021-01-10T08:00:00.000Z' }, query: { query: '', language: 'kuery' }, }) ); @@ -1055,7 +1065,11 @@ describe('Lens App', () => { }); it('updates the editor frame when the user changes query or time in the search bar', () => { - const { component, frame } = mountWith({}); + const { component, frame, services } = mountWith({}); + (services.data.query.timefilter.timefilter.calculateBounds as jest.Mock).mockReturnValue({ + min: moment('2021-01-09T04:00:00.000Z'), + max: moment('2021-01-09T08:00:00.000Z'), + }); act(() => component.find(TopNavMenu).prop('onQuerySubmit')!({ dateRange: { from: 'now-14d', to: 'now-7d' }, @@ -1071,10 +1085,14 @@ describe('Lens App', () => { }), {} ); + expect(services.data.query.timefilter.timefilter.setTime).toHaveBeenCalledWith({ + from: 'now-14d', + to: 'now-7d', + }); expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ - dateRange: { fromDate: 'now-14d', toDate: 'now-7d' }, + dateRange: { fromDate: '2021-01-09T04:00:00.000Z', toDate: '2021-01-09T08:00:00.000Z' }, query: { query: 'new', language: 'lucene' }, }) ); @@ -1237,6 +1255,34 @@ describe('Lens App', () => { ); }); + it('clears all existing unpinned filters when the active saved query is cleared', () => { + const { component, frame, services } = mountWith({}); + act(() => + component.find(TopNavMenu).prop('onQuerySubmit')!({ + dateRange: { from: 'now-14d', to: 'now-7d' }, + query: { query: 'new', language: 'lucene' }, + }) + ); + const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; + const field = ({ name: 'myfield' } as unknown) as IFieldType; + const pinnedField = ({ name: 'pinnedField' } as unknown) as IFieldType; + const unpinned = esFilters.buildExistsFilter(field, indexPattern); + const pinned = esFilters.buildExistsFilter(pinnedField, indexPattern); + FilterManager.setFiltersStore([pinned], esFilters.FilterStateStore.GLOBAL_STATE); + act(() => services.data.query.filterManager.setFilters([pinned, unpinned])); + component.update(); + act(() => component.find(TopNavMenu).prop('onClearSavedQuery')!()); + component.update(); + expect(frame.mount).toHaveBeenLastCalledWith( + expect.any(Element), + expect.objectContaining({ + filters: [pinned], + }) + ); + }); + }); + + describe('search session id management', () => { it('updates the searchSessionId when the query is updated', () => { const { component, frame } = mountWith({}); act(() => { @@ -1263,12 +1309,12 @@ describe('Lens App', () => { expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ - searchSessionId: `sessionId-1`, + searchSessionId: `sessionId-2`, }) ); }); - it('clears all existing unpinned filters when the active saved query is cleared', () => { + it('updates the searchSessionId when the active saved query is cleared', () => { const { component, frame, services } = mountWith({}); act(() => component.find(TopNavMenu).prop('onQuerySubmit')!({ @@ -1286,31 +1332,67 @@ describe('Lens App', () => { component.update(); act(() => component.find(TopNavMenu).prop('onClearSavedQuery')!()); component.update(); - expect(frame.mount).toHaveBeenLastCalledWith( + expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ - filters: [pinned], + searchSessionId: `sessionId-2`, }) ); }); - it('updates the searchSessionId when the active saved query is cleared', () => { - const { component, frame, services } = mountWith({}); - act(() => - component.find(TopNavMenu).prop('onQuerySubmit')!({ - dateRange: { from: 'now-14d', to: 'now-7d' }, - query: { query: 'new', language: 'lucene' }, + const mockUpdate = { + filterableIndexPatterns: [], + doc: { + title: '', + description: '', + visualizationType: '', + state: { + datasourceStates: {}, + visualization: {}, + filters: [], + query: { query: '', language: 'lucene' }, + }, + references: [], + }, + isSaveable: true, + activeData: undefined, + }; + + it('does not update the searchSessionId when the state changes', () => { + const { component, frame } = mountWith({}); + act(() => { + (component.find(NativeRenderer).prop('nativeProps') as EditorFrameProps).onChange( + mockUpdate + ); + }); + component.update(); + expect(frame.mount).not.toHaveBeenCalledWith( + expect.any(Element), + expect.objectContaining({ + searchSessionId: `sessionId-2`, }) ); - const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; - const field = ({ name: 'myfield' } as unknown) as IFieldType; - const pinnedField = ({ name: 'pinnedField' } as unknown) as IFieldType; - const unpinned = esFilters.buildExistsFilter(field, indexPattern); - const pinned = esFilters.buildExistsFilter(pinnedField, indexPattern); - FilterManager.setFiltersStore([pinned], esFilters.FilterStateStore.GLOBAL_STATE); - act(() => services.data.query.filterManager.setFilters([pinned, unpinned])); - component.update(); - act(() => component.find(TopNavMenu).prop('onClearSavedQuery')!()); + }); + + it('does update the searchSessionId when the state changes and too much time passed', () => { + const { component, frame, services } = mountWith({}); + + // time range is 100,000ms ago to 30,000ms ago (that's a lag of 30 percent) + (services.data.nowProvider.get as jest.Mock).mockReturnValue(new Date(Date.now() - 30000)); + (services.data.query.timefilter.timefilter.getTime as jest.Mock).mockReturnValue({ + from: 'now-2m', + to: 'now', + }); + (services.data.query.timefilter.timefilter.getBounds as jest.Mock).mockReturnValue({ + min: moment(Date.now() - 100000), + max: moment(Date.now() - 30000), + }); + + act(() => { + (component.find(NativeRenderer).prop('nativeProps') as EditorFrameProps).onChange( + mockUpdate + ); + }); component.update(); expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), @@ -1319,6 +1401,34 @@ describe('Lens App', () => { }) ); }); + + it('does not update the searchSessionId when the state changes and too little time has passed', () => { + const { component, frame, services } = mountWith({}); + + // time range is 100,000ms ago to 300ms ago (that's a lag of .3 percent, not enough to trigger a session update) + (services.data.nowProvider.get as jest.Mock).mockReturnValue(new Date(Date.now() - 300)); + (services.data.query.timefilter.timefilter.getTime as jest.Mock).mockReturnValue({ + from: 'now-2m', + to: 'now', + }); + (services.data.query.timefilter.timefilter.getBounds as jest.Mock).mockReturnValue({ + min: moment(Date.now() - 100000), + max: moment(Date.now() - 300), + }); + + act(() => { + (component.find(NativeRenderer).prop('nativeProps') as EditorFrameProps).onChange( + mockUpdate + ); + }); + component.update(); + expect(frame.mount).not.toHaveBeenCalledWith( + expect.any(Element), + expect.objectContaining({ + searchSessionId: `sessionId-2`, + }) + ); + }); }); describe('showing a confirm message when leaving', () => { diff --git a/x-pack/plugins/lens/public/app_plugin/app.tsx b/x-pack/plugins/lens/public/app_plugin/app.tsx index 3f10cb341105..28e1f6da6074 100644 --- a/x-pack/plugins/lens/public/app_plugin/app.tsx +++ b/x-pack/plugins/lens/public/app_plugin/app.tsx @@ -7,7 +7,7 @@ import './app.scss'; import _ from 'lodash'; -import React, { useState, useEffect, useCallback, useMemo } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { NotificationsStart } from 'kibana/public'; import { EuiBreadcrumb } from '@elastic/eui'; @@ -39,6 +39,7 @@ import { LensByReferenceInput, LensEmbeddableInput, } from '../editor_frame_service/embeddable/embeddable'; +import { useTimeRange } from './time_range'; export function App({ history, @@ -107,9 +108,11 @@ export function App({ state.searchSessionId, ]); - // Need a stable reference for the frame component of the dateRange - const { from: fromDate, to: toDate } = data.query.timefilter.timefilter.getTime(); - const currentDateRange = useMemo(() => ({ fromDate, toDate }), [fromDate, toDate]); + const { resolvedDateRange, from: fromDate, to: toDate } = useTimeRange( + data, + state.lastKnownDoc, + setState + ); const onError = useCallback( (e: { message: string }) => @@ -658,7 +661,7 @@ export function App({ render={editorFrame.mount} nativeProps={{ searchSessionId: state.searchSessionId, - dateRange: currentDateRange, + dateRange: resolvedDateRange, query: state.query, filters: state.filters, savedQuery: state.savedQuery, @@ -670,7 +673,7 @@ export function App({ if (isSaveable !== state.isSaveable) { setState((s) => ({ ...s, isSaveable })); } - if (!_.isEqual(state.persistedDoc, doc)) { + if (!_.isEqual(state.persistedDoc, doc) && !_.isEqual(state.lastKnownDoc, doc)) { setState((s) => ({ ...s, lastKnownDoc: doc })); } if (!_.isEqual(state.activeData, activeData)) { diff --git a/x-pack/plugins/lens/public/app_plugin/time_range.ts b/x-pack/plugins/lens/public/app_plugin/time_range.ts new file mode 100644 index 000000000000..5a9f2d5ab9e9 --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/time_range.ts @@ -0,0 +1,82 @@ +/* + * 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 './app.scss'; + +import _ from 'lodash'; +import moment from 'moment'; +import { useEffect, useMemo } from 'react'; +import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; +import { LensAppState } from './types'; +import { Document } from '../persistence'; + +function containsDynamicMath(dateMathString: string) { + return dateMathString.includes('now'); +} + +const TIME_LAG_PERCENTAGE_LIMIT = 0.02; + +/** + * Fetches the current global time range from data plugin and restarts session + * if the fixed "now" parameter is diverging too much from the actual current time. + * @param data data plugin contract to manage current now value, time range and session + * @param lastKnownDoc Current state of the editor + * @param setState state setter for Lens app state + */ +export function useTimeRange( + data: DataPublicPluginStart, + lastKnownDoc: Document | undefined, + setState: React.Dispatch> +) { + const timefilter = data.query.timefilter.timefilter; + const { from, to } = data.query.timefilter.timefilter.getTime(); + const currentNow = data.nowProvider.get(); + + // Need a stable reference for the frame component of the dateRange + const resolvedDateRange = useMemo(() => { + const { min, max } = timefilter.calculateBounds({ + from, + to, + }); + return { fromDate: min?.toISOString() || from, toDate: max?.toISOString() || to }; + // recalculate current date range if current "now" value changes because calculateBounds + // depends on it internally + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [timefilter, currentNow, from, to]); + + useEffect(() => { + const unresolvedTimeRange = timefilter.getTime(); + if ( + !containsDynamicMath(unresolvedTimeRange.from) && + !containsDynamicMath(unresolvedTimeRange.to) + ) { + return; + } + + const { min, max } = timefilter.getBounds(); + + if (!min || !max) { + // bounds not fully specified, bailing out + return; + } + + // calculate length of currently configured range in ms + const timeRangeLength = moment.duration(max.diff(min)).asMilliseconds(); + + // calculate lag of managed "now" for date math + const nowDiff = Date.now() - data.nowProvider.get().valueOf(); + + // if the lag is signifcant, start a new session to clear the cache + if (nowDiff > timeRangeLength * TIME_LAG_PERCENTAGE_LIMIT) { + setState((s) => ({ + ...s, + searchSessionId: data.search.session.start(), + })); + } + }, [data.nowProvider, data.search.session, timefilter, lastKnownDoc, setState]); + + return { resolvedDateRange, from, to }; +} diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index 2cb815596d8b..f908d16afe47 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -252,7 +252,6 @@ export function EditorFrame(props: EditorFrameProps) { state.visualization, state.activeData, props.query, - props.dateRange, props.filters, props.savedQuery, state.title, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index 1bdffc90797a..dc7b291b7120 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -299,6 +299,17 @@ export function DimensionEditor(props: DimensionEditorProps) { } ); + // Need to workout early on the error to decide whether to show this or an help text + const fieldErrorMessage = + (selectedOperationDefinition?.input !== 'fullReference' || + (incompleteOperation && operationDefinitionMap[incompleteOperation].input === 'field')) && + getErrorMessage( + selectedColumn, + Boolean(incompleteOperation), + selectedOperationDefinition?.input, + currentFieldIsInvalid + ); + return (
@@ -342,6 +353,11 @@ export function DimensionEditor(props: DimensionEditorProps) { existingFields={state.existingFields} selectionStyle={selectedOperationDefinition.selectionStyle} dateRange={dateRange} + labelAppend={selectedOperationDefinition?.getHelpMessage?.({ + data: props.data, + uiSettings: props.uiSettings, + currentColumn: state.layers[layerId].columns[columnId], + })} {...services} /> ); @@ -360,12 +376,15 @@ export function DimensionEditor(props: DimensionEditorProps) { })} fullWidth isInvalid={Boolean(incompleteOperation || currentFieldIsInvalid)} - error={getErrorMessage( - selectedColumn, - Boolean(incompleteOperation), - selectedOperationDefinition?.input, - currentFieldIsInvalid - )} + error={fieldErrorMessage} + labelAppend={ + !fieldErrorMessage && + selectedOperationDefinition?.getHelpMessage?.({ + data: props.data, + uiSettings: props.uiSettings, + currentColumn: state.layers[layerId].columns[columnId], + }) + } > { id: 'bytes', title: 'Bytes', }), + deserialize: jest.fn().mockReturnValue({ + convert: () => 'formatted', + }), } as unknown) as DataPublicPluginStart['fieldFormats'], } as unknown) as DataPublicPluginStart, core: {} as CoreSetup, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx index d73530ec8a92..1a394584360a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.tsx @@ -8,7 +8,13 @@ import './dimension_editor.scss'; import _ from 'lodash'; import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFormRow, EuiSpacer, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { + EuiFormRow, + EuiFormRowProps, + EuiSpacer, + EuiComboBox, + EuiComboBoxOptionOption, +} from '@elastic/eui'; import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import type { DataPublicPluginStart } from 'src/plugins/data/public'; @@ -40,6 +46,7 @@ export interface ReferenceEditorProps { currentIndexPattern: IndexPattern; existingFields: IndexPatternPrivateState['existingFields']; dateRange: DateRange; + labelAppend?: EuiFormRowProps['labelAppend']; // Services uiSettings: IUiSettingsClient; @@ -59,6 +66,7 @@ export function ReferenceEditor(props: ReferenceEditorProps) { validation, selectionStyle, dateRange, + labelAppend, ...services } = props; @@ -251,6 +259,7 @@ export function ReferenceEditor(props: ReferenceEditorProps) { })} fullWidth isInvalid={showFieldInvalid} + labelAppend={labelAppend} > { + return ( + + + + + {children} + + + ); +}; + +export const HelpPopover = ({ + anchorPosition, + button, + children, + closePopover, + isOpen, + title, +}: { + anchorPosition?: EuiPopoverProps['anchorPosition']; + button: EuiPopoverProps['button']; + children: ReactNode; + closePopover: EuiPopoverProps['closePopover']; + isOpen: EuiPopoverProps['isOpen']; + title?: string; +}) => { + return ( + + {title && {title}} + + + {children} + + + ); +}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx index d9805b337c00..d43dbccd92f8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/calculations/moving_average.tsx @@ -5,10 +5,9 @@ */ import { i18n } from '@kbn/i18n'; -import { useState } from 'react'; -import React from 'react'; -import { EuiFormRow } from '@elastic/eui'; -import { EuiFieldNumber } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { useState } from 'react'; +import { EuiFieldNumber, EuiFormRow } from '@elastic/eui'; import { FormattedIndexPatternColumn, ReferenceBasedIndexPatternColumn } from '../column_types'; import { IndexPatternLayer } from '../../../types'; import { @@ -21,6 +20,7 @@ import { import { updateColumnParam } from '../../layer_helpers'; import { isValidNumber, useDebounceWithOptions } from '../helpers'; import { adjustTimeScaleOnOtherColumnChange } from '../../time_scale_utils'; +import { HelpPopover, HelpPopoverButton } from '../../../help_popover'; import type { OperationDefinition, ParamEditorProps } from '..'; const ofName = buildLabelFunction((name?: string) => { @@ -111,6 +111,7 @@ export const movingAverageOperation: OperationDefinition< }) ); }, + getHelpMessage: () => , getDisabledStatus(indexPattern, layer) { return checkForDateHistogram( layer, @@ -168,3 +169,79 @@ function MovingAverageParamEditor({ ); } + +const MovingAveragePopup = () => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + return ( + setIsPopoverOpen(!isPopoverOpen)}> + {i18n.translate('xpack.lens.indexPattern.movingAverage.helpText', { + defaultMessage: 'How it works', + })} + + } + closePopover={() => setIsPopoverOpen(false)} + isOpen={isPopoverOpen} + title={i18n.translate('xpack.lens.indexPattern.movingAverage.titleHelp', { + defaultMessage: 'How moving average works', + })} + > +

+ +

+ +

+ +

+ +

+ +

+ +
    +
  • (1 + 2 + 3 + 4 + 5) / 5 = 3
  • +
  • (2 + 3 + 4 + 5 + 6) / 5 = 4
  • +
  • ...
  • +
  • (5 + 6 + 7 + 8 + 9) / 5 = 7
  • +
+ +

+ +

+

+ +

+
    +
  • (1 + 2) / 2 = 1.5
  • +
  • (1 + 2 + 3) / 3 = 2
  • +
  • (1 + 2 + 3 + 4) / 4 = 2.5
  • +
  • (1 + 2 + 3 + 4 + 5) / 5 = 3
  • +
+ +

+ +

+
+ ); +}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx index a41cc88c4f29..2e61f4fc3e24 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/date_histogram.tsx @@ -4,31 +4,36 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { + EuiBasicTable, + EuiCode, + EuiFieldNumber, + EuiFlexGroup, + EuiFlexItem, EuiFormRow, + EuiSelect, + EuiSpacer, EuiSwitch, EuiSwitchEvent, - EuiFieldNumber, - EuiSelect, - EuiFlexItem, - EuiFlexGroup, EuiTextColor, - EuiSpacer, } from '@elastic/eui'; import { updateColumnParam } from '../layer_helpers'; import { OperationDefinition } from './index'; import { FieldBasedIndexPatternColumn } from './column_types'; import { AggFunctionsMapping, + DataPublicPluginStart, IndexPatternAggRestrictions, search, + UI_SETTINGS, } from '../../../../../../../src/plugins/data/public'; import { buildExpressionFunction } from '../../../../../../../src/plugins/expressions/public'; import { getInvalidFieldMessage, getSafeName } from './helpers'; +import { HelpPopover, HelpPopoverButton } from '../../help_popover'; const { isValidInterval } = search.aggs; const autoInterval = 'auto'; @@ -54,6 +59,7 @@ export const dateHistogramOperation: OperationDefinition< priority: 5, // Highest priority level used getErrorMessage: (layer, columnId, indexPattern) => getInvalidFieldMessage(layer.columns[columnId] as FieldBasedIndexPatternColumn, indexPattern), + getHelpMessage: (props) => , getPossibleOperationForField: ({ aggregationRestrictions, aggregatable, type }) => { if ( type === 'date' && @@ -334,3 +340,77 @@ function restrictedInterval(aggregationRestrictions?: Partial { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const infiniteBound = i18n.translate('xpack.lens.indexPattern.dateHistogram.moreThanYear', { + defaultMessage: 'More than a year', + }); + const upToLabel = i18n.translate('xpack.lens.indexPattern.dateHistogram.upTo', { + defaultMessage: 'Up to', + }); + + return ( + setIsPopoverOpen(!isPopoverOpen)}> + {i18n.translate('xpack.lens.indexPattern.dateHistogram.autoHelpText', { + defaultMessage: 'How it works', + })} + + } + closePopover={() => setIsPopoverOpen(false)} + isOpen={isPopoverOpen} + title={i18n.translate('xpack.lens.indexPattern.dateHistogram.titleHelp', { + defaultMessage: 'How auto date histogram works', + })} + > +

+ {i18n.translate('xpack.lens.indexPattern.dateHistogram.autoBasicExplanation', { + defaultMessage: 'The auto date histogram splits a date field into buckets by interval.', + })} +

+ +

+ {UI_SETTINGS.HISTOGRAM_MAX_BARS}, + targetBarSetting: {UI_SETTINGS.HISTOGRAM_BAR_TARGET}, + }} + /> +

+ +

+ {i18n.translate('xpack.lens.indexPattern.dateHistogram.autoAdvancedExplanation', { + defaultMessage: 'The interval follows this logic:', + })} +

+ + ({ + bound: typeof bound === 'number' ? infiniteBound : `${upToLabel} ${boundLabel}`, + interval: intervalLabel, + }))} + columns={[ + { + field: 'bound', + name: i18n.translate('xpack.lens.indexPattern.dateHistogram.autoBoundHeader', { + defaultMessage: 'Target interval measured', + }), + }, + { + field: 'interval', + name: i18n.translate('xpack.lens.indexPattern.dateHistogram.autoIntervalHeader', { + defaultMessage: 'Interval used', + }), + }, + ]} + /> +
+ ); +}; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index 36c9cf75d2b6..7dbc7d3b986a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -126,6 +126,12 @@ export interface ParamEditorProps { data: DataPublicPluginStart; } +export interface HelpProps { + currentColumn: C; + uiSettings: IUiSettingsClient; + data: DataPublicPluginStart; +} + export type TimeScalingMode = 'disabled' | 'mandatory' | 'optional'; interface BaseOperationDefinitionProps { @@ -201,6 +207,8 @@ interface BaseOperationDefinitionProps { * If set to optional, time scaling won't be enabled by default and can be removed. */ timeScalingMode?: TimeScalingMode; + + getHelpMessage?: (props: HelpProps) => React.ReactNode; } interface BaseBuildColumnArgs { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx index df955be6b490..ad5c146ff662 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx @@ -6,21 +6,73 @@ import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButtonEmpty, + EuiButtonIcon, + EuiCode, + EuiFlexGroup, + EuiFlexItem, EuiFormRow, EuiRange, - EuiFlexItem, - EuiFlexGroup, - EuiButtonIcon, EuiToolTip, - EuiIconTip, } from '@elastic/eui'; -import { IFieldFormat } from 'src/plugins/data/public'; +import type { IFieldFormat } from 'src/plugins/data/public'; +import { UI_SETTINGS } from '../../../../../../../../src/plugins/data/public'; import { RangeColumnParams, UpdateParamsFnType, MODES_TYPES } from './ranges'; import { AdvancedRangeEditor } from './advanced_editor'; import { TYPING_DEBOUNCE_TIME, MODES, MIN_HISTOGRAM_BARS } from './constants'; import { useDebounceWithOptions } from '../helpers'; +import { HelpPopover, HelpPopoverButton } from '../../../help_popover'; + +const GranularityHelpPopover = () => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + return ( + setIsPopoverOpen(!isPopoverOpen)}> + {i18n.translate('xpack.lens.indexPattern.ranges.granularityHelpText', { + defaultMessage: 'How it works', + })} + + } + closePopover={() => setIsPopoverOpen(false)} + isOpen={isPopoverOpen} + title={i18n.translate('xpack.lens.indexPattern.ranges.granularityPopoverTitle', { + defaultMessage: 'How granularity interval works', + })} + > +

+ {i18n.translate('xpack.lens.indexPattern.ranges.granularityPopoverBasicExplanation', { + defaultMessage: + 'Interval granularity divides the field into evenly spaced intervals based on the minimum and maximum values for the field.', + })} +

+ +

+ {UI_SETTINGS.HISTOGRAM_MAX_BARS}, + }} + /> +

+ +

+ {i18n.translate('xpack.lens.indexPattern.ranges.granularityPopoverAdvancedExplanation', { + defaultMessage: + 'Intervals are incremented by 10, 5 or 2: for example an interval can be 100 or 0.2 .', + })} +

+
+ ); +}; const BaseRangeEditor = ({ maxBars, @@ -49,12 +101,7 @@ const BaseRangeEditor = ({ const granularityLabel = i18n.translate('xpack.lens.indexPattern.ranges.granularity', { defaultMessage: 'Intervals granularity', }); - const granularityLabelDescription = i18n.translate( - 'xpack.lens.indexPattern.ranges.granularityDescription', - { - defaultMessage: 'Divides the field into evenly spaced intervals.', - } - ); + const decreaseButtonLabel = i18n.translate('xpack.lens.indexPattern.ranges.decreaseButtonLabel', { defaultMessage: 'Decrease granularity', }); @@ -65,21 +112,12 @@ const BaseRangeEditor = ({ return ( <> - {granularityLabel}{' '} - - - } + label={granularityLabel} data-test-subj="indexPattern-ranges-section-label" labelType="legend" fullWidth display="rowCompressed" + labelAppend={} > diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.scss b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.scss index b9ff6a56d8e3..a2caeb93477f 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.scss +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.scss @@ -1,3 +1,3 @@ .lnsXyToolbar__popover { - width: 320px; + width: 365px; } diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx index 351b1f0d7165..b8bca09bb353 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_config_panel.tsx @@ -21,6 +21,7 @@ import { EuiColorPickerProps, EuiToolTip, EuiIcon, + EuiIconTip, } from '@elastic/eui'; import { PaletteRegistry } from 'src/plugins/charts/public'; import { @@ -327,9 +328,25 @@ export function XyToolbar(props: VisualizationToolbarProps) { {isFittingEnabled ? ( + {i18n.translate('xpack.lens.xyChart.missingValuesLabel', { + defaultMessage: 'Missing values', + })}{' '} + + + } > , getStartServices: StartServicesAccessor<{}, LicensingPluginStart> -): IContextProvider, 'licensing'> { +): IContextProvider { return async function licensingRouteHandlerContext() { const [, , { featureUsage }] = await getStartServices(); const license = await license$.pipe(take(1)).toPromise(); diff --git a/x-pack/plugins/licensing/server/mocks.ts b/x-pack/plugins/licensing/server/mocks.ts index 1a2b543b47df..cab1823f22ac 100644 --- a/x-pack/plugins/licensing/server/mocks.ts +++ b/x-pack/plugins/licensing/server/mocks.ts @@ -7,7 +7,7 @@ import { BehaviorSubject } from 'rxjs'; import { LicensingPluginSetup, LicensingPluginStart, - LicensingRequestHandlerContext, + LicensingApiRequestHandlerContext, } from './types'; import { licenseMock } from '../common/licensing.mock'; import { featureUsageMock } from './services/feature_usage_service.mock'; @@ -49,8 +49,8 @@ const createStartMock = (): jest.Mocked => { const createRequestHandlerContextMock = ( ...options: Parameters -): jest.Mocked => { - const mock: jest.Mocked = { +): jest.Mocked => { + const mock: jest.Mocked = { license: licenseMock.createLicense(...options), featureUsage: featureUsageMock.createStart(), }; diff --git a/x-pack/plugins/licensing/server/routes/feature_usage.ts b/x-pack/plugins/licensing/server/routes/feature_usage.ts index fa26d09903dc..da4853145c33 100644 --- a/x-pack/plugins/licensing/server/routes/feature_usage.ts +++ b/x-pack/plugins/licensing/server/routes/feature_usage.ts @@ -3,11 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IRouter, StartServicesAccessor } from 'src/core/server'; +import { StartServicesAccessor } from 'src/core/server'; import { LicensingPluginStart } from '../types'; +import { LicensingRouter } from '../types'; export function registerFeatureUsageRoute( - router: IRouter, + router: LicensingRouter, getStartServices: StartServicesAccessor<{}, LicensingPluginStart> ) { router.get( diff --git a/x-pack/plugins/licensing/server/routes/index.ts b/x-pack/plugins/licensing/server/routes/index.ts index 16065d8e19ad..3a86653c6cb4 100644 --- a/x-pack/plugins/licensing/server/routes/index.ts +++ b/x-pack/plugins/licensing/server/routes/index.ts @@ -4,15 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter, StartServicesAccessor } from 'src/core/server'; +import { StartServicesAccessor } from 'src/core/server'; import { LicensingPluginStart } from '../types'; import { FeatureUsageServiceSetup } from '../services'; import { registerInfoRoute } from './info'; import { registerFeatureUsageRoute } from './feature_usage'; import { registerNotifyFeatureUsageRoute, registerRegisterFeatureRoute } from './internal'; +import { LicensingRouter } from '../types'; export function registerRoutes( - router: IRouter, + router: LicensingRouter, featureUsageSetup: FeatureUsageServiceSetup, getStartServices: StartServicesAccessor<{}, LicensingPluginStart> ) { diff --git a/x-pack/plugins/licensing/server/routes/info.ts b/x-pack/plugins/licensing/server/routes/info.ts index cad873014e27..c07649bf1b12 100644 --- a/x-pack/plugins/licensing/server/routes/info.ts +++ b/x-pack/plugins/licensing/server/routes/info.ts @@ -3,9 +3,9 @@ * 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 { LicensingRouter } from '../types'; -export function registerInfoRoute(router: IRouter) { +export function registerInfoRoute(router: LicensingRouter) { router.get({ path: '/api/licensing/info', validate: false }, (context, request, response) => { return response.ok({ body: context.licensing.license, diff --git a/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts b/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts index ec70472574be..552126bff17b 100644 --- a/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts +++ b/x-pack/plugins/licensing/server/routes/internal/notify_feature_usage.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import { LicensingRouter } from '../../types'; -export function registerNotifyFeatureUsageRoute(router: IRouter) { +export function registerNotifyFeatureUsageRoute(router: LicensingRouter) { router.post( { path: '/internal/licensing/feature_usage/notify', diff --git a/x-pack/plugins/licensing/server/routes/internal/register_feature.ts b/x-pack/plugins/licensing/server/routes/internal/register_feature.ts index 418e98fc1b2a..750dc29ed273 100644 --- a/x-pack/plugins/licensing/server/routes/internal/register_feature.ts +++ b/x-pack/plugins/licensing/server/routes/internal/register_feature.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; import { LicenseType, LICENSE_TYPE } from '../../../common/types'; import { FeatureUsageServiceSetup } from '../../services'; +import { LicensingRouter } from '../../types'; export function registerRegisterFeatureRoute( - router: IRouter, + router: LicensingRouter, featureUsageSetup: FeatureUsageServiceSetup ) { router.post( diff --git a/x-pack/plugins/licensing/server/types.ts b/x-pack/plugins/licensing/server/types.ts index dd1277429eab..1c7fc6965368 100644 --- a/x-pack/plugins/licensing/server/types.ts +++ b/x-pack/plugins/licensing/server/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { Observable } from 'rxjs'; -import { ILegacyClusterClient } from 'src/core/server'; +import type { ILegacyClusterClient, IRouter, RequestHandlerContext } from 'src/core/server'; import { ILicense, LicenseStatus, LicenseType } from '../common/types'; import { FeatureUsageServiceSetup, FeatureUsageServiceStart } from './services'; @@ -44,17 +44,23 @@ export interface RawLicense { * The APIs exposed on the `licensing` key of {@link RequestHandlerContext} for plugins that depend on licensing. * @public */ -export interface LicensingRequestHandlerContext { +export interface LicensingApiRequestHandlerContext { featureUsage: FeatureUsageServiceStart; license: ILicense; } -declare module 'src/core/server' { - interface RequestHandlerContext { - licensing: LicensingRequestHandlerContext; - } +/** + * @internal + */ +export interface LicensingRequestHandlerContext extends RequestHandlerContext { + licensing: LicensingApiRequestHandlerContext; } +/** + * @internal + */ +export type LicensingRouter = IRouter; + /** @public */ export interface LicensingPluginSetup { /** diff --git a/x-pack/plugins/licensing/server/wrap_route_with_license_check.ts b/x-pack/plugins/licensing/server/wrap_route_with_license_check.ts index e0cac8d9db20..188c8dbf2715 100644 --- a/x-pack/plugins/licensing/server/wrap_route_with_license_check.ts +++ b/x-pack/plugins/licensing/server/wrap_route_with_license_check.ts @@ -4,26 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - RequestHandler, - RequestHandlerContext, - KibanaRequest, - RouteMethod, - KibanaResponseFactory, -} from 'src/core/server'; +import { RequestHandler, KibanaRequest, RouteMethod, KibanaResponseFactory } from 'src/core/server'; import { ILicense } from '../common/types'; +import type { LicensingRequestHandlerContext } from './types'; export type CheckLicense = ( license: ILicense ) => { valid: false; message: string } | { valid: true; message: null }; -export function wrapRouteWithLicenseCheck( +export function wrapRouteWithLicenseCheck( checkLicense: CheckLicense, - handler: RequestHandler -): RequestHandler { + handler: RequestHandler +): RequestHandler { return async ( - context: RequestHandlerContext, + context: Context, request: KibanaRequest, response: KibanaResponseFactory ) => { diff --git a/x-pack/plugins/lists/server/index.ts b/x-pack/plugins/lists/server/index.ts index ea27073e3053..2738736d62a3 100644 --- a/x-pack/plugins/lists/server/index.ts +++ b/x-pack/plugins/lists/server/index.ts @@ -13,7 +13,7 @@ import { ListPlugin } from './plugin'; export { ListClient } from './services/lists/list_client'; export { CreateExceptionListItemOptions } from './services/exception_lists/exception_list_client_types'; export { ExceptionListClient } from './services/exception_lists/exception_list_client'; -export { ListPluginSetup } from './types'; +export type { ListPluginSetup, ListsApiRequestHandlerContext } from './types'; export const config = { schema: ConfigSchema }; export const plugin = (initializerContext: PluginInitializerContext): ListPlugin => diff --git a/x-pack/plugins/lists/server/plugin.ts b/x-pack/plugins/lists/server/plugin.ts index 670f0fe684cc..c6d42e5ac4f2 100644 --- a/x-pack/plugins/lists/server/plugin.ts +++ b/x-pack/plugins/lists/server/plugin.ts @@ -19,6 +19,7 @@ import type { ContextProviderReturn, ListPluginSetup, ListsPluginStart, + ListsRequestHandlerContext, PluginsStart, } from './types'; import { createConfig$ } from './create_config'; @@ -44,8 +45,11 @@ export class ListPlugin initSavedObjects(core.savedObjects); - core.http.registerRouteHandlerContext('lists', this.createRouteHandlerContext()); - const router = core.http.createRouter(); + core.http.registerRouteHandlerContext( + 'lists', + this.createRouteHandlerContext() + ); + const router = core.http.createRouter(); initRoutes(router, config); return { diff --git a/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts index cce4038ff48d..86072b18db65 100644 --- a/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { ENDPOINT_LIST_ID, ENDPOINT_LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -18,7 +17,7 @@ import { import { getExceptionListClient } from './utils/get_exception_list_client'; import { validateExceptionListSize } from './validate'; -export const createEndpointListItemRoute = (router: IRouter): void => { +export const createEndpointListItemRoute = (router: ListsPluginRouter): void => { router.post( { options: { diff --git a/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts b/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts index 91b6a328c864..51f647d76f40 100644 --- a/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_endpoint_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { ENDPOINT_LIST_URL } from '../../common/constants'; import { buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -22,7 +21,7 @@ import { getExceptionListClient } from './utils/get_exception_list_client'; * object. * @param router The router to use. */ -export const createEndpointListRoute = (router: IRouter): void => { +export const createEndpointListRoute = (router: ListsPluginRouter): void => { router.post( { options: { diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts index afcb0f99c8a3..b0456e13d15d 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -19,7 +18,7 @@ import { getExceptionListClient } from './utils/get_exception_list_client'; import { endpointDisallowedFields } from './endpoint_disallowed_fields'; import { validateEndpointExceptionItemEntries, validateExceptionListSize } from './validate'; -export const createExceptionListItemRoute = (router: IRouter): void => { +export const createExceptionListItemRoute = (router: ListsPluginRouter): void => { router.post( { options: { diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts index fd2ba6340009..506b70c92357 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { EXCEPTION_LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getExceptionListClient } from './utils/get_exception_list_client'; -export const createExceptionListRoute = (router: IRouter): void => { +export const createExceptionListRoute = (router: ListsPluginRouter): void => { router.post( { options: { diff --git a/x-pack/plugins/lists/server/routes/create_list_index_route.ts b/x-pack/plugins/lists/server/routes/create_list_index_route.ts index be08093dc705..bca6f1d085eb 100644 --- a/x-pack/plugins/lists/server/routes/create_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_index_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; import { LIST_INDEX } from '../../common/constants'; @@ -13,7 +12,7 @@ import { acknowledgeSchema } from '../../common/schemas'; import { getListClient } from '.'; -export const createListIndexRoute = (router: IRouter): void => { +export const createListIndexRoute = (router: ListsPluginRouter): void => { router.post( { options: { diff --git a/x-pack/plugins/lists/server/routes/create_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_list_item_route.ts index bd2828d331d8..50d7b141ddef 100644 --- a/x-pack/plugins/lists/server/routes/create_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { createListItemSchema, listItemSchema } from '../../common/schemas'; @@ -13,7 +12,7 @@ import { validate } from '../../common/shared_imports'; import { getListClient } from '.'; -export const createListItemRoute = (router: IRouter): void => { +export const createListItemRoute = (router: ListsPluginRouter): void => { router.post( { options: { diff --git a/x-pack/plugins/lists/server/routes/create_list_route.ts b/x-pack/plugins/lists/server/routes/create_list_route.ts index 90f5bf9b2c65..49749bbab904 100644 --- a/x-pack/plugins/lists/server/routes/create_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -13,7 +12,7 @@ import { CreateListSchemaDecoded, createListSchema, listSchema } from '../../com import { getListClient } from '.'; -export const createListRoute = (router: IRouter): void => { +export const createListRoute = (router: ListsPluginRouter): void => { router.post( { options: { diff --git a/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts index 380fdcf86206..c8367561711f 100644 --- a/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_endpoint_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { ENDPOINT_LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getErrorMessageExceptionListItem, getExceptionListClient } from './utils'; -export const deleteEndpointListItemRoute = (router: IRouter): void => { +export const deleteEndpointListItemRoute = (router: ListsPluginRouter): void => { router.delete( { options: { diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts index 07e0fad20c90..fd313f48e1cc 100644 --- a/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getErrorMessageExceptionListItem, getExceptionListClient } from './utils'; -export const deleteExceptionListItemRoute = (router: IRouter): void => { +export const deleteExceptionListItemRoute = (router: ListsPluginRouter): void => { router.delete( { options: { diff --git a/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts b/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts index 769ce732240b..ae9078de1af9 100644 --- a/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_exception_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { EXCEPTION_LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getErrorMessageExceptionList, getExceptionListClient } from './utils'; -export const deleteExceptionListRoute = (router: IRouter): void => { +export const deleteExceptionListRoute = (router: ListsPluginRouter): void => { router.delete( { options: { diff --git a/x-pack/plugins/lists/server/routes/delete_list_index_route.ts b/x-pack/plugins/lists/server/routes/delete_list_index_route.ts index aa587273036a..5e2b4cc2a941 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_index_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_INDEX } from '../../common/constants'; import { buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -29,7 +28,7 @@ import { getListClient } from '.'; * * And ensuring they're all gone */ -export const deleteListIndexRoute = (router: IRouter): void => { +export const deleteListIndexRoute = (router: ListsPluginRouter): void => { router.delete( { options: { diff --git a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts index fa1adf8a39ed..673520ec59e3 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -13,7 +12,7 @@ import { deleteListItemSchema, listItemArraySchema, listItemSchema } from '../.. import { getListClient } from '.'; -export const deleteListItemRoute = (router: IRouter): void => { +export const deleteListItemRoute = (router: ListsPluginRouter): void => { router.delete( { options: { diff --git a/x-pack/plugins/lists/server/routes/delete_list_route.ts b/x-pack/plugins/lists/server/routes/delete_list_route.ts index bf8fac7f3dd8..bf411247b74d 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -22,7 +21,7 @@ import { ExceptionListClient } from '../services/exception_lists/exception_list_ import { getExceptionListClient, getListClient } from '.'; -export const deleteListRoute = (router: IRouter): void => { +export const deleteListRoute = (router: ListsPluginRouter): void => { router.delete( { options: { diff --git a/x-pack/plugins/lists/server/routes/export_exception_list_route.ts b/x-pack/plugins/lists/server/routes/export_exception_list_route.ts index 1394bf48cd2c..132df176834b 100644 --- a/x-pack/plugins/lists/server/routes/export_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/export_exception_list_route.ts @@ -4,15 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { EXCEPTION_LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { exportExceptionListQuerySchema } from '../../common/schemas'; import { getExceptionListClient } from './utils'; -export const exportExceptionListRoute = (router: IRouter): void => { +export const exportExceptionListRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/export_list_item_route.ts b/x-pack/plugins/lists/server/routes/export_list_item_route.ts index 98167931c434..6f5321380aa7 100644 --- a/x-pack/plugins/lists/server/routes/export_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/export_list_item_route.ts @@ -6,15 +6,14 @@ import { Stream } from 'stream'; -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { exportListItemQuerySchema } from '../../common/schemas'; import { getListClient } from '.'; -export const exportListItemRoute = (router: IRouter): void => { +export const exportListItemRoute = (router: ListsPluginRouter): void => { router.post( { options: { diff --git a/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts index d6a459b3ac96..2b7cb6789b37 100644 --- a/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_endpoint_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { ENDPOINT_LIST_ID, ENDPOINT_LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getExceptionListClient } from './utils'; -export const findEndpointListItemRoute = (router: IRouter): void => { +export const findEndpointListItemRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts index 103cba700013..108dd88adb6b 100644 --- a/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_exception_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getExceptionListClient } from './utils'; -export const findExceptionListItemRoute = (router: IRouter): void => { +export const findExceptionListItemRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/find_exception_list_route.ts b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts index 41342261ef68..1a284b27da62 100644 --- a/x-pack/plugins/lists/server/routes/find_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/find_exception_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { EXCEPTION_LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getExceptionListClient } from './utils'; -export const findExceptionListRoute = (router: IRouter): void => { +export const findExceptionListRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/find_list_item_route.ts b/x-pack/plugins/lists/server/routes/find_list_item_route.ts index 454ea891857c..4734168d270e 100644 --- a/x-pack/plugins/lists/server/routes/find_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/find_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -18,7 +17,7 @@ import { decodeCursor } from '../services/utils'; import { getListClient } from './utils'; -export const findListItemRoute = (router: IRouter): void => { +export const findListItemRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/find_list_route.ts b/x-pack/plugins/lists/server/routes/find_list_route.ts index d751214006dc..fc7a69f6df11 100644 --- a/x-pack/plugins/lists/server/routes/find_list_route.ts +++ b/x-pack/plugins/lists/server/routes/find_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -14,7 +13,7 @@ import { decodeCursor } from '../services/utils'; import { getListClient } from './utils'; -export const findListRoute = (router: IRouter): void => { +export const findListRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/import_list_item_route.ts b/x-pack/plugins/lists/server/routes/import_list_item_route.ts index f7ecc7ac1ac8..0039f7f5d9f9 100644 --- a/x-pack/plugins/lists/server/routes/import_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/import_list_item_route.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; import { schema } from '@kbn/config-schema'; +import type { ListsPluginRouter } from '../types'; import { LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +17,7 @@ import { createStreamFromBuffer } from './utils/create_stream_from_buffer'; import { getListClient } from '.'; -export const importListItemRoute = (router: IRouter, config: ConfigType): void => { +export const importListItemRoute = (router: ListsPluginRouter, config: ConfigType): void => { router.post( { options: { diff --git a/x-pack/plugins/lists/server/routes/init_routes.ts b/x-pack/plugins/lists/server/routes/init_routes.ts index 1f29d0aaeeb4..a176bd39bb50 100644 --- a/x-pack/plugins/lists/server/routes/init_routes.ts +++ b/x-pack/plugins/lists/server/routes/init_routes.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { ConfigType } from '../config'; import { @@ -46,7 +45,7 @@ import { updateListRoute, } from '.'; -export const initRoutes = (router: IRouter, config: ConfigType): void => { +export const initRoutes = (router: ListsPluginRouter, config: ConfigType): void => { // lists createListRoute(router); readListRoute(router); diff --git a/x-pack/plugins/lists/server/routes/patch_list_item_route.ts b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts index 58cca0313006..a14d70106cb7 100644 --- a/x-pack/plugins/lists/server/routes/patch_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/patch_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -13,7 +12,7 @@ import { listItemSchema, patchListItemSchema } from '../../common/schemas'; import { getListClient } from '.'; -export const patchListItemRoute = (router: IRouter): void => { +export const patchListItemRoute = (router: ListsPluginRouter): void => { router.patch( { options: { diff --git a/x-pack/plugins/lists/server/routes/patch_list_route.ts b/x-pack/plugins/lists/server/routes/patch_list_route.ts index 763f3f495ca1..9c5b59cefb5b 100644 --- a/x-pack/plugins/lists/server/routes/patch_list_route.ts +++ b/x-pack/plugins/lists/server/routes/patch_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -13,7 +12,7 @@ import { listSchema, patchListSchema } from '../../common/schemas'; import { getListClient } from '.'; -export const patchListRoute = (router: IRouter): void => { +export const patchListRoute = (router: ListsPluginRouter): void => { router.patch( { options: { diff --git a/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts index e80347d97bb7..2dc79483b434 100644 --- a/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_endpoint_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { ENDPOINT_LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getErrorMessageExceptionListItem, getExceptionListClient } from './utils'; -export const readEndpointListItemRoute = (router: IRouter): void => { +export const readEndpointListItemRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts index 0cfac6467f08..139a485ab0c5 100644 --- a/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_exception_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getErrorMessageExceptionListItem, getExceptionListClient } from './utils'; -export const readExceptionListItemRoute = (router: IRouter): void => { +export const readExceptionListItemRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/read_exception_list_route.ts b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts index d9359881616f..a8dffa696ea7 100644 --- a/x-pack/plugins/lists/server/routes/read_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/read_exception_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { EXCEPTION_LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getErrorMessageExceptionList, getExceptionListClient } from './utils'; -export const readExceptionListRoute = (router: IRouter): void => { +export const readExceptionListRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/read_list_index_route.ts b/x-pack/plugins/lists/server/routes/read_list_index_route.ts index 5524c1beeaa5..1cb6a63e08cf 100644 --- a/x-pack/plugins/lists/server/routes/read_list_index_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_index_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_INDEX } from '../../common/constants'; import { buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -13,7 +12,7 @@ import { listItemIndexExistSchema } from '../../common/schemas'; import { getListClient } from '.'; -export const readListIndexRoute = (router: IRouter): void => { +export const readListIndexRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/read_list_item_route.ts b/x-pack/plugins/lists/server/routes/read_list_item_route.ts index 99d34d0fd84a..677bc49c21a5 100644 --- a/x-pack/plugins/lists/server/routes/read_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { listItemArraySchema, listItemSchema, readListItemSchema } from '../../common/schemas'; @@ -13,7 +12,7 @@ import { validate } from '../../common/shared_imports'; import { getListClient } from '.'; -export const readListItemRoute = (router: IRouter): void => { +export const readListItemRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/read_list_route.ts b/x-pack/plugins/lists/server/routes/read_list_route.ts index da3cf73b5681..1854647460f9 100644 --- a/x-pack/plugins/lists/server/routes/read_list_route.ts +++ b/x-pack/plugins/lists/server/routes/read_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -13,7 +12,7 @@ import { listSchema, readListSchema } from '../../common/schemas'; import { getListClient } from '.'; -export const readListRoute = (router: IRouter): void => { +export const readListRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/read_privileges_route.ts b/x-pack/plugins/lists/server/routes/read_privileges_route.ts index 4a82f4c5e9cb..abb43ba43a18 100644 --- a/x-pack/plugins/lists/server/routes/read_privileges_route.ts +++ b/x-pack/plugins/lists/server/routes/read_privileges_route.ts @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; import { merge } from 'lodash/fp'; +import type { ListsPluginRouter } from '../types'; import { LIST_PRIVILEGES_URL } from '../../common/constants'; import { buildSiemResponse, readPrivileges, transformError } from '../siem_server_deps'; import { getListClient } from './utils'; -export const readPrivilegesRoute = (router: IRouter): void => { +export const readPrivilegesRoute = (router: ListsPluginRouter): void => { router.get( { options: { diff --git a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts index 8312f2fc87b9..fb2149b3c3d6 100644 --- a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { ENDPOINT_LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getExceptionListClient } from '.'; -export const updateEndpointListItemRoute = (router: IRouter): void => { +export const updateEndpointListItemRoute = (router: ListsPluginRouter): void => { router.put( { options: { diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts index 9ad563724b86..dea53f99810b 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { EXCEPTION_LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -18,7 +17,7 @@ import { updateExceptionListItemValidate } from '../../common/schemas/request/up import { getExceptionListClient } from '.'; -export const updateExceptionListItemRoute = (router: IRouter): void => { +export const updateExceptionListItemRoute = (router: ListsPluginRouter): void => { router.put( { options: { diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts index 47008e3b78fa..c3fa907c706d 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { EXCEPTION_LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -17,7 +16,7 @@ import { import { getErrorMessageExceptionList, getExceptionListClient } from './utils'; -export const updateExceptionListRoute = (router: IRouter): void => { +export const updateExceptionListRoute = (router: ListsPluginRouter): void => { router.put( { options: { diff --git a/x-pack/plugins/lists/server/routes/update_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_list_item_route.ts index 3490027b1274..d7aec97aace6 100644 --- a/x-pack/plugins/lists/server/routes/update_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_list_item_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_ITEM_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -13,7 +12,7 @@ import { listItemSchema, updateListItemSchema } from '../../common/schemas'; import { getListClient } from '.'; -export const updateListItemRoute = (router: IRouter): void => { +export const updateListItemRoute = (router: ListsPluginRouter): void => { router.put( { options: { diff --git a/x-pack/plugins/lists/server/routes/update_list_route.ts b/x-pack/plugins/lists/server/routes/update_list_route.ts index 8d7d08be4130..de8412bc3fdd 100644 --- a/x-pack/plugins/lists/server/routes/update_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_list_route.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; - +import type { ListsPluginRouter } from '../types'; import { LIST_URL } from '../../common/constants'; import { buildRouteValidation, buildSiemResponse, transformError } from '../siem_server_deps'; import { validate } from '../../common/shared_imports'; @@ -13,7 +12,7 @@ import { listSchema, updateListSchema } from '../../common/schemas'; import { getListClient } from '.'; -export const updateListRoute = (router: IRouter): void => { +export const updateListRoute = (router: ListsPluginRouter): void => { router.put( { options: { diff --git a/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts b/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts index ba01ca617fb8..b9b7beab6529 100644 --- a/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts +++ b/x-pack/plugins/lists/server/routes/utils/get_exception_list_client.ts @@ -4,12 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'kibana/server'; - +import type { ListsRequestHandlerContext } from '../../types'; import { ErrorWithStatusCode } from '../../error_with_status_code'; import { ExceptionListClient } from '../../services/exception_lists/exception_list_client'; -export const getExceptionListClient = (context: RequestHandlerContext): ExceptionListClient => { +export const getExceptionListClient = ( + context: ListsRequestHandlerContext +): ExceptionListClient => { const exceptionLists = context.lists?.getExceptionListClient(); if (exceptionLists == null) { throw new ErrorWithStatusCode('Exception lists is not found as a plugin', 404); diff --git a/x-pack/plugins/lists/server/routes/utils/get_list_client.ts b/x-pack/plugins/lists/server/routes/utils/get_list_client.ts index 6ad69fd994bf..bc06d753b7b6 100644 --- a/x-pack/plugins/lists/server/routes/utils/get_list_client.ts +++ b/x-pack/plugins/lists/server/routes/utils/get_list_client.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'kibana/server'; - import { ListClient } from '../../services/lists/list_client'; import { ErrorWithStatusCode } from '../../error_with_status_code'; +import type { ListsRequestHandlerContext } from '../../types'; -export const getListClient = (context: RequestHandlerContext): ListClient => { +export const getListClient = (context: ListsRequestHandlerContext): ListClient => { const lists = context.lists?.getListClient(); if (lists == null) { throw new ErrorWithStatusCode('Lists is not found as a plugin', 404); diff --git a/x-pack/plugins/lists/server/types.ts b/x-pack/plugins/lists/server/types.ts index 7d0a24ccddfd..70cad625245b 100644 --- a/x-pack/plugins/lists/server/types.ts +++ b/x-pack/plugins/lists/server/types.ts @@ -6,8 +6,9 @@ import { IContextProvider, + IRouter, LegacyAPICaller, - RequestHandler, + RequestHandlerContext, SavedObjectsClientContract, } from 'kibana/server'; @@ -17,7 +18,7 @@ import type { SpacesPluginStart } from '../../spaces/server'; import { ListClient } from './services/lists/list_client'; import { ExceptionListClient } from './services/exception_lists/exception_list_client'; -export type ContextProvider = IContextProvider, 'lists'>; +export type ContextProvider = IContextProvider; export type ListsPluginStart = void; export interface PluginsStart { security: SecurityPluginStart | undefined | null; @@ -40,15 +41,26 @@ export interface ListPluginSetup { getListClient: GetListClientType; } -export type ContextProviderReturn = Promise<{ +/** + * @public + */ +export interface ListsApiRequestHandlerContext { getListClient: () => ListClient; getExceptionListClient: () => ExceptionListClient; -}>; -declare module 'src/core/server' { - interface RequestHandlerContext { - lists?: { - getExceptionListClient: () => ExceptionListClient; - getListClient: () => ListClient; - }; - } } + +/** + * @internal + */ +export interface ListsRequestHandlerContext extends RequestHandlerContext { + lists?: ListsApiRequestHandlerContext; +} + +/** + * @internal + */ +export type ListsPluginRouter = IRouter; +/** + * @internal + */ +export type ContextProviderReturn = Promise; diff --git a/x-pack/plugins/logstash/server/plugin.ts b/x-pack/plugins/logstash/server/plugin.ts index 4a6d476551db..2c0a714b9691 100644 --- a/x-pack/plugins/logstash/server/plugin.ts +++ b/x-pack/plugins/logstash/server/plugin.ts @@ -16,6 +16,7 @@ import { PluginSetupContract as FeaturesPluginSetup } from '../../features/serve import { SecurityPluginSetup } from '../../security/server'; import { registerRoutes } from './routes'; +import type { LogstashRequestHandlerContext } from './types'; interface SetupDeps { licensing: LicensingPluginSetup; @@ -55,9 +56,12 @@ export class LogstashPlugin implements Plugin { start(core: CoreStart) { const esClient = core.elasticsearch.legacy.createClient('logstash'); - this.coreSetup!.http.registerRouteHandlerContext('logstash', async (context, request) => { - return { esClient: esClient.asScoped(request) }; - }); + this.coreSetup!.http.registerRouteHandlerContext( + 'logstash', + async (context, request) => { + return { esClient: esClient.asScoped(request) }; + } + ); } stop() { if (this.esClient) { diff --git a/x-pack/plugins/logstash/server/routes/cluster/load.ts b/x-pack/plugins/logstash/server/routes/cluster/load.ts index 18fe21f3da67..307e1ebdb55a 100644 --- a/x-pack/plugins/logstash/server/routes/cluster/load.ts +++ b/x-pack/plugins/logstash/server/routes/cluster/load.ts @@ -3,12 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; import { wrapRouteWithLicenseCheck } from '../../../../licensing/server'; import { Cluster } from '../../models/cluster'; import { checkLicense } from '../../lib/check_license'; +import type { LogstashPluginRouter } from '../../types'; -export function registerClusterLoadRoute(router: IRouter) { +export function registerClusterLoadRoute(router: LogstashPluginRouter) { router.get( { path: '/api/logstash/cluster', diff --git a/x-pack/plugins/logstash/server/routes/index.ts b/x-pack/plugins/logstash/server/routes/index.ts index 422afbf7d411..b62be3a566d4 100644 --- a/x-pack/plugins/logstash/server/routes/index.ts +++ b/x-pack/plugins/logstash/server/routes/index.ts @@ -3,8 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; import { SecurityPluginSetup } from '../../../security/server'; +import type { LogstashPluginRouter } from '../types'; import { registerClusterLoadRoute } from './cluster'; import { registerPipelineDeleteRoute, @@ -13,7 +13,7 @@ import { } from './pipeline'; import { registerPipelinesListRoute, registerPipelinesDeleteRoute } from './pipelines'; -export function registerRoutes(router: IRouter, security?: SecurityPluginSetup) { +export function registerRoutes(router: LogstashPluginRouter, security?: SecurityPluginSetup) { registerClusterLoadRoute(router); registerPipelineDeleteRoute(router); diff --git a/x-pack/plugins/logstash/server/routes/pipeline/delete.ts b/x-pack/plugins/logstash/server/routes/pipeline/delete.ts index d94b3b94b1df..e6f335c390ed 100644 --- a/x-pack/plugins/logstash/server/routes/pipeline/delete.ts +++ b/x-pack/plugins/logstash/server/routes/pipeline/delete.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; import { wrapRouteWithLicenseCheck } from '../../../../licensing/server'; - +import type { LogstashPluginRouter } from '../../types'; import { checkLicense } from '../../lib/check_license'; -export function registerPipelineDeleteRoute(router: IRouter) { +export function registerPipelineDeleteRoute(router: LogstashPluginRouter) { router.delete( { path: '/api/logstash/pipeline/{id}', diff --git a/x-pack/plugins/logstash/server/routes/pipeline/load.ts b/x-pack/plugins/logstash/server/routes/pipeline/load.ts index 69d16fb82d86..d26e901ca252 100644 --- a/x-pack/plugins/logstash/server/routes/pipeline/load.ts +++ b/x-pack/plugins/logstash/server/routes/pipeline/load.ts @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { LogstashPluginRouter } from '../../types'; import { Pipeline } from '../../models/pipeline'; import { wrapRouteWithLicenseCheck } from '../../../../licensing/server'; import { checkLicense } from '../../lib/check_license'; -export function registerPipelineLoadRoute(router: IRouter) { +export function registerPipelineLoadRoute(router: LogstashPluginRouter) { router.get( { path: '/api/logstash/pipeline/{id}', diff --git a/x-pack/plugins/logstash/server/routes/pipeline/save.ts b/x-pack/plugins/logstash/server/routes/pipeline/save.ts index e5f28bda1974..ddeb134ca604 100644 --- a/x-pack/plugins/logstash/server/routes/pipeline/save.ts +++ b/x-pack/plugins/logstash/server/routes/pipeline/save.ts @@ -5,14 +5,17 @@ */ import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; -import { IRouter } from 'src/core/server'; import { Pipeline } from '../../models/pipeline'; import { wrapRouteWithLicenseCheck } from '../../../../licensing/server'; import { SecurityPluginSetup } from '../../../../security/server'; import { checkLicense } from '../../lib/check_license'; +import type { LogstashPluginRouter } from '../../types'; -export function registerPipelineSaveRoute(router: IRouter, security?: SecurityPluginSetup) { +export function registerPipelineSaveRoute( + router: LogstashPluginRouter, + security?: SecurityPluginSetup +) { router.put( { path: '/api/logstash/pipeline/{id}', diff --git a/x-pack/plugins/logstash/server/routes/pipelines/delete.ts b/x-pack/plugins/logstash/server/routes/pipelines/delete.ts index 4eb3cae1d095..1dff01f8768b 100644 --- a/x-pack/plugins/logstash/server/routes/pipelines/delete.ts +++ b/x-pack/plugins/logstash/server/routes/pipelines/delete.ts @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import { schema } from '@kbn/config-schema'; -import { LegacyAPICaller, IRouter } from 'src/core/server'; +import { LegacyAPICaller } from 'src/core/server'; import { wrapRouteWithLicenseCheck } from '../../../../licensing/server'; import { checkLicense } from '../../lib/check_license'; +import type { LogstashPluginRouter } from '../../types'; async function deletePipelines(callWithRequest: LegacyAPICaller, pipelineIds: string[]) { const deletePromises = pipelineIds.map((pipelineId) => { @@ -29,7 +30,7 @@ async function deletePipelines(callWithRequest: LegacyAPICaller, pipelineIds: st }; } -export function registerPipelinesDeleteRoute(router: IRouter) { +export function registerPipelinesDeleteRoute(router: LogstashPluginRouter) { router.post( { path: '/api/logstash/pipelines/delete', @@ -42,7 +43,7 @@ export function registerPipelinesDeleteRoute(router: IRouter) { wrapRouteWithLicenseCheck( checkLicense, router.handleLegacyErrors(async (context, request, response) => { - const client = context.logstash!.esClient; + const client = context.logstash.esClient; const results = await deletePipelines(client.callAsCurrentUser, request.body.pipelineIds); return response.ok({ body: { results } }); diff --git a/x-pack/plugins/logstash/server/routes/pipelines/list.ts b/x-pack/plugins/logstash/server/routes/pipelines/list.ts index 78690d3091cb..e6e7c40eecae 100644 --- a/x-pack/plugins/logstash/server/routes/pipelines/list.ts +++ b/x-pack/plugins/logstash/server/routes/pipelines/list.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { LegacyAPICaller, IRouter } from 'src/core/server'; +import { LegacyAPICaller } from 'src/core/server'; +import type { LogstashPluginRouter } from '../../types'; import { wrapRouteWithLicenseCheck } from '../../../../licensing/server'; import { PipelineListItem } from '../../models/pipeline_list_item'; @@ -20,7 +21,7 @@ async function fetchPipelines(callWithRequest: LegacyAPICaller) { return await callWithRequest('transport.request', params); } -export function registerPipelinesListRoute(router: IRouter) { +export function registerPipelinesListRoute(router: LogstashPluginRouter) { router.get( { path: '/api/logstash/pipelines', diff --git a/x-pack/plugins/logstash/server/types.ts b/x-pack/plugins/logstash/server/types.ts index d9fd109b2094..1fb74d43f1f4 100644 --- a/x-pack/plugins/logstash/server/types.ts +++ b/x-pack/plugins/logstash/server/types.ts @@ -3,7 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { ILegacyScopedClusterClient } from 'src/core/server'; +import type { ILegacyScopedClusterClient, IRouter, RequestHandlerContext } from 'src/core/server'; +import type { LicensingApiRequestHandlerContext } from '../../licensing/server'; export interface PipelineListItemOptions { id: string; @@ -12,10 +13,17 @@ export interface PipelineListItemOptions { username: string; } -declare module 'src/core/server' { - interface RequestHandlerContext { - logstash?: { - esClient: ILegacyScopedClusterClient; - }; - } +/** + * @internal + */ +export interface LogstashRequestHandlerContext extends RequestHandlerContext { + logstash: { + esClient: ILegacyScopedClusterClient; + }; + licensing: LicensingApiRequestHandlerContext; } + +/** + * @internal + */ +export type LogstashPluginRouter = IRouter; diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.test.tsx b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.test.tsx index 1321593f015c..e029480bd861 100644 --- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.test.tsx +++ b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.test.tsx @@ -7,7 +7,10 @@ import { SCALING_TYPES, SOURCE_TYPES } from '../../../../common/constants'; import { BlendedVectorLayer } from './blended_vector_layer'; import { ESSearchSource } from '../../sources/es_search_source'; -import { ESGeoGridSourceDescriptor } from '../../../../common/descriptor_types'; +import { + AbstractESSourceDescriptor, + ESGeoGridSourceDescriptor, +} from '../../../../common/descriptor_types'; jest.mock('../../../kibana_services', () => { return { @@ -53,27 +56,12 @@ describe('getSource', () => { expect(source.cloneDescriptor().type).toBe(SOURCE_TYPES.ES_GEO_GRID); }); - test('cluster source applyGlobalQuery should be true when document source applyGlobalQuery is true', async () => { - const blendedVectorLayer = new BlendedVectorLayer({ - source: new ESSearchSource(documentSourceDescriptor), - layerDescriptor: BlendedVectorLayer.createDescriptor( - { - sourceDescriptor: documentSourceDescriptor, - __dataRequests: [clusteredDataRequest], - }, - mapColors - ), - }); - - const source = blendedVectorLayer.getSource(); - expect((source.cloneDescriptor() as ESGeoGridSourceDescriptor).applyGlobalQuery).toBe(true); - }); - - test('cluster source applyGlobalQuery should be false when document source applyGlobalQuery is false', async () => { + test('cluster source AbstractESSourceDescriptor properties should mirror document source AbstractESSourceDescriptor properties', async () => { const blendedVectorLayer = new BlendedVectorLayer({ source: new ESSearchSource({ ...documentSourceDescriptor, applyGlobalQuery: false, + applyGlobalTime: false, }), layerDescriptor: BlendedVectorLayer.createDescriptor( { @@ -85,7 +73,27 @@ describe('getSource', () => { }); const source = blendedVectorLayer.getSource(); - expect((source.cloneDescriptor() as ESGeoGridSourceDescriptor).applyGlobalQuery).toBe(false); + const sourceDescriptor = source.cloneDescriptor() as ESGeoGridSourceDescriptor; + const abstractEsSourceDescriptor: AbstractESSourceDescriptor = { + // Purposely grabbing properties instead of using spread operator + // to ensure type check will fail when new properties are added to AbstractESSourceDescriptor. + // In the event of type check failure, ensure test is updated with new property and that new property + // is correctly passed to clustered source descriptor. + type: sourceDescriptor.type, + id: sourceDescriptor.id, + indexPatternId: sourceDescriptor.indexPatternId, + geoField: sourceDescriptor.geoField, + applyGlobalQuery: sourceDescriptor.applyGlobalQuery, + applyGlobalTime: sourceDescriptor.applyGlobalTime, + }; + expect(abstractEsSourceDescriptor).toEqual({ + type: sourceDescriptor.type, + id: sourceDescriptor.id, + geoField: 'myGeoField', + indexPatternId: 'myIndexPattern', + applyGlobalQuery: false, + applyGlobalTime: false, + } as AbstractESSourceDescriptor); }); }); diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts index 825f6ed74777..5b33738a91a2 100644 --- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts @@ -62,6 +62,7 @@ function getClusterSource(documentSource: IESSource, documentStyle: IVectorStyle requestType: RENDER_AS.POINT, }); clusterSourceDescriptor.applyGlobalQuery = documentSource.getApplyGlobalQuery(); + clusterSourceDescriptor.applyGlobalTime = documentSource.getApplyGlobalTime(); clusterSourceDescriptor.metrics = [ { type: AGG_TYPE.COUNT, diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index 060ff4d46fa2..fe13e4f0ac2f 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -5,6 +5,7 @@ */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ +import { Map as MbMap } from 'mapbox-gl'; import { Query } from 'src/plugins/data/public'; import _ from 'lodash'; import React, { ReactElement } from 'react'; @@ -68,7 +69,7 @@ export interface ILayer { ownsMbLayerId(mbLayerId: string): boolean; ownsMbSourceId(mbSourceId: string): boolean; canShowTooltip(): boolean; - syncLayerWithMB(mbMap: unknown): void; + syncLayerWithMB(mbMap: MbMap): void; getLayerTypeIconName(): string; isDataLoaded(): boolean; getIndexPatternIds(): string[]; @@ -418,7 +419,7 @@ export class AbstractLayer implements ILayer { return false; } - syncLayerWithMB(mbMap: unknown) { + syncLayerWithMB(mbMap: MbMap) { throw new Error('Should implement AbstractLayer#syncLayerWithMB'); } diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx index 3693f55586b3..9c851dcedb3f 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_line_source/es_geo_line_source.tsx @@ -9,7 +9,12 @@ import React from 'react'; import { GeoJsonProperties } from 'geojson'; import { i18n } from '@kbn/i18n'; -import { FIELD_ORIGIN, SOURCE_TYPES, VECTOR_SHAPE_TYPE } from '../../../../common/constants'; +import { + EMPTY_FEATURE_COLLECTION, + FIELD_ORIGIN, + SOURCE_TYPES, + VECTOR_SHAPE_TYPE, +} from '../../../../common/constants'; import { getField, addFieldToDSL } from '../../../../common/elasticsearch_util'; import { ESGeoLineSourceDescriptor, @@ -216,6 +221,18 @@ export class ESGeoLineSource extends AbstractESAggSource { ); const totalEntities = _.get(entityResp, 'aggregations.totalEntities.value', 0); const areEntitiesTrimmed = entityBuckets.length >= MAX_TRACKS; + if (totalEntities === 0) { + return { + data: EMPTY_FEATURE_COLLECTION, + meta: { + areResultsTrimmed: false, + areEntitiesTrimmed: false, + entityCount: 0, + numTrimmedTracks: 0, + totalEntities: 0, + } as ESGeoLineSourceResponseMeta, + }; + } // // Fetch tracks diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx index 4dc765f1704a..820453f166a4 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx +++ b/x-pack/plugins/maps/public/connected_components/mb_map/mb_map.tsx @@ -332,7 +332,7 @@ export class MBMap extends Component { this.props.layerList, this.props.spatialFiltersLayer ); - this.props.layerList.forEach((layer) => layer.syncLayerWithMB(this.state.mbMap)); + this.props.layerList.forEach((layer) => layer.syncLayerWithMB(this.state.mbMap!)); syncLayerOrder(this.state.mbMap, this.props.spatialFiltersLayer, this.props.layerList); }; diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts index 9e85c7b04b26..4e9cb499cf70 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.test.ts @@ -135,6 +135,7 @@ describe('sortLayer', () => { { id: `${BRAVO_LAYER_ID}_circle`, type: 'circle' } as MbLayer, { id: `${SPATIAL_FILTERS_LAYER_ID}_fill`, type: 'fill' } as MbLayer, { id: `${SPATIAL_FILTERS_LAYER_ID}_circle`, type: 'circle' } as MbLayer, + { id: `gl-draw-polygon-fill-active.cold`, type: 'fill' } as MbLayer, { id: `${CHARLIE_LAYER_ID}_text`, type: 'symbol', @@ -158,6 +159,7 @@ describe('sortLayer', () => { 'alpha_text', 'alpha_circle', 'charlie_text', + 'gl-draw-polygon-fill-active.cold', 'SPATIAL_FILTERS_LAYER_ID_fill', 'SPATIAL_FILTERS_LAYER_ID_circle', ]); diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts index dda43269e32d..adf68ffb310b 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts +++ b/x-pack/plugins/maps/public/connected_components/mb_map/sort_layers.ts @@ -28,6 +28,10 @@ export function getIsTextLayer(mbLayer: MbLayer) { }); } +export function isGlDrawLayer(mbLayerId: string) { + return mbLayerId.startsWith('gl-draw'); +} + function doesMbLayerBelongToMapLayerAndClass( mapLayer: ILayer, mbLayer: MbLayer, @@ -118,6 +122,18 @@ export function syncLayerOrder(mbMap: MbMap, spatialFiltersLayer: ILayer, layerL } let beneathMbLayerId = getBottomMbLayerId(mbLayers, spatialFiltersLayer, LAYER_CLASS.ANY); + // Ensure gl-draw layers are on top of all layerList layers + const glDrawLayer = ({ + ownsMbLayerId: (mbLayerId: string) => { + return isGlDrawLayer(mbLayerId); + }, + } as unknown) as ILayer; + moveMapLayer(mbMap, mbLayers, glDrawLayer, LAYER_CLASS.ANY, beneathMbLayerId); + const glDrawBottomMbLayerId = getBottomMbLayerId(mbLayers, glDrawLayer, LAYER_CLASS.ANY); + if (glDrawBottomMbLayerId) { + beneathMbLayerId = glDrawBottomMbLayerId; + } + // Sort map layer labels [...layerList] .reverse() diff --git a/x-pack/plugins/maps/public/connected_components/mb_map/utils.js b/x-pack/plugins/maps/public/connected_components/mb_map/utils.js index f12f34061756..2f8852174c29 100644 --- a/x-pack/plugins/maps/public/connected_components/mb_map/utils.js +++ b/x-pack/plugins/maps/public/connected_components/mb_map/utils.js @@ -5,6 +5,7 @@ */ import { RGBAImage } from './image_utils'; +import { isGlDrawLayer } from './sort_layers'; export function removeOrphanedSourcesAndLayers(mbMap, layerList, spatialFilterLayer) { const mbStyle = mbMap.getStyle(); @@ -17,7 +18,7 @@ export function removeOrphanedSourcesAndLayers(mbMap, layerList, spatialFilterLa } // ignore gl-draw layers - if (mbLayer.id.startsWith('gl-draw')) { + if (isGlDrawLayer(mbLayer.id)) { return; } diff --git a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts index f3c3ec528c03..27fd78980710 100644 --- a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts +++ b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts @@ -292,8 +292,12 @@ export class SavedMap { const prevTitle = this._attributes.title; const prevDescription = this._attributes.description; + const prevTags = this._tags; this._attributes.title = newTitle; this._attributes.description = newDescription; + if (newTags) { + this._tags = newTags; + } this._syncAttributesWithStore(); let updatedMapEmbeddableInput: MapEmbeddableInput; @@ -316,6 +320,7 @@ export class SavedMap { // Error toast displayed by wrapAttributes this._attributes.title = prevTitle; this._attributes.description = prevDescription; + this._tags = prevTags; return; } diff --git a/x-pack/plugins/ml/common/types/file_datavisualizer.ts b/x-pack/plugins/ml/common/types/file_datavisualizer.ts index 9dc3896e9be4..b1967cfe83f3 100644 --- a/x-pack/plugins/ml/common/types/file_datavisualizer.ts +++ b/x-pack/plugins/ml/common/types/file_datavisualizer.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { ES_FIELD_TYPES } from '../../../../../src/plugins/data/common'; + export interface InputOverrides { [key: string]: string; } @@ -29,15 +31,27 @@ export interface FindFileStructureResponse { count: number; cardinality: number; top_hits: Array<{ count: number; value: any }>; + mean_value?: number; + median_value?: number; max_value?: number; min_value?: number; + earliest?: string; + latest?: string; }; }; sample_start: string; num_messages_analyzed: number; mappings: { - [fieldName: string]: { - type: string; + properties: { + [fieldName: string]: { + // including all possible Elasticsearch types + // since find_file_structure API can be enhanced to include new fields in the future + type: Exclude< + ES_FIELD_TYPES, + ES_FIELD_TYPES._ID | ES_FIELD_TYPES._INDEX | ES_FIELD_TYPES._SOURCE | ES_FIELD_TYPES._TYPE + >; + format?: string; + }; }; }; quote: string; diff --git a/x-pack/plugins/ml/common/types/ml_url_generator.ts b/x-pack/plugins/ml/common/types/ml_url_generator.ts index 3c70cf4c27b5..3ff57fc622da 100644 --- a/x-pack/plugins/ml/common/types/ml_url_generator.ts +++ b/x-pack/plugins/ml/common/types/ml_url_generator.ts @@ -42,11 +42,7 @@ export interface MlGenericUrlPageState extends MlIndexBasedSearchState { [key: string]: any; } -export interface DataVisualizerIndexBasedAppState { - pageIndex: number; - pageSize: number; - sortField: string; - sortDirection: string; +export interface DataVisualizerIndexBasedAppState extends Omit { searchString?: Query['query']; searchQuery?: Query['query']; searchQueryLanguage?: SearchQueryLanguage; @@ -57,6 +53,13 @@ export interface DataVisualizerIndexBasedAppState { showAllFields?: boolean; showEmptyFields?: boolean; } + +export interface DataVisualizerFileBasedAppState extends Omit { + visibleFieldTypes?: string[]; + visibleFieldNames?: string[]; + showDistributions?: boolean; +} + export type MlGenericUrlState = MLPageState< | typeof ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER | typeof ML_PAGES.ANOMALY_DETECTION_CREATE_JOB diff --git a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss index e07c8a7b8169..756804a0e6aa 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss +++ b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.scss @@ -22,6 +22,7 @@ .mlDataGridChart__legendBoolean { width: 100%; + min-width: $euiButtonMinWidth; td { text-align: center } } diff --git a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx index c499d1153a23..4eaf33c6f157 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/data_grid.tsx @@ -260,20 +260,22 @@ export const DataGrid: FC = memo( - - - {(copy: () => void) => ( - - )} - - + {props.copyToClipboard && props.copyToClipboardDescription && ( + + + {(copy: () => void) => ( + + )} + + + )} )} {errorCallout !== undefined && ( diff --git a/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx b/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx index bb52941f463f..2ecbe0601816 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx @@ -20,7 +20,7 @@ import { NON_AGGREGATABLE } from './common'; export const hoveredRow$ = new BehaviorSubject(null); -const BAR_COLOR = euiPaletteColorBlind()[0]; +export const BAR_COLOR = euiPaletteColorBlind()[0]; const BAR_COLOR_BLUR = euiPaletteColorBlind({ rotations: 2 })[10]; const MAX_CHART_COLUMNS = 20; diff --git a/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.tsx b/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.tsx index 0e98a23637f0..dec149cdec40 100644 --- a/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.tsx +++ b/x-pack/plugins/ml/public/application/components/field_title_bar/field_title_bar.tsx @@ -11,11 +11,15 @@ import { EuiText, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FieldTypeIcon } from '../field_type_icon'; -import { FieldVisConfig } from '../../datavisualizer/index_based/common'; import { getMLJobTypeAriaLabel } from '../../util/field_types_utils'; +import { + FieldVisConfig, + FileBasedFieldVisConfig, + isIndexBasedFieldVisConfig, +} from '../../datavisualizer/stats_table/types/field_vis_config'; interface Props { - card: FieldVisConfig; + card: FieldVisConfig | FileBasedFieldVisConfig; } export const FieldTitleBar: FC = ({ card }) => { @@ -30,13 +34,13 @@ export const FieldTitleBar: FC = ({ card }) => { if (card.fieldName === undefined) { classNames.push('document_count'); - } else if (card.isUnsupportedType === true) { + } else if (isIndexBasedFieldVisConfig(card) && card.isUnsupportedType === true) { classNames.push('type-other'); } else { classNames.push(card.type); } - if (card.isUnsupportedType !== true) { + if (isIndexBasedFieldVisConfig(card) && card.isUnsupportedType !== true) { // All the supported field types have aria labels. cardTitleAriaLabel.unshift(getMLJobTypeAriaLabel(card.type)!); } diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx index 5b175eb06a4a..edf038160e0a 100644 --- a/x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { FC, useEffect, useCallback, useState } from 'react'; +import React, { FC, useEffect, useCallback, useState, useRef } from 'react'; import { i18n } from '@kbn/i18n'; import { @@ -50,15 +50,24 @@ export const ModelSnapshotTable: FC = ({ job, refreshJobList }) => { const [closeJobModalVisible, setCloseJobModalVisible] = useState(null); const [combinedJobState, setCombinedJobState] = useState(null); + const isMounted = useRef(true); useEffect(() => { loadModelSnapshots(); + return () => { + isMounted.current = false; + }; }, []); - const loadModelSnapshots = useCallback(async () => { + async function loadModelSnapshots() { + if (isMounted.current === false) { + // table refresh can be triggered a while after a snapshot revert has been triggered. + // ensure the table is still visible before attempted to refresh it. + return; + } const { model_snapshots: ms } = await ml.getModelSnapshots(job.job_id); setSnapshots(ms); setSnapshotsLoaded(true); - }, [job]); + } const checkJobIsClosed = useCallback( async (snapshot: ModelSnapshot) => { @@ -107,13 +116,14 @@ export const ModelSnapshotTable: FC = ({ job, refreshJobList }) => { } }, []); - const closeRevertFlyout = useCallback((reload: boolean) => { + const closeRevertFlyout = useCallback(() => { setRevertSnapshot(null); - if (reload) { - loadModelSnapshots(); - // wait half a second before refreshing the jobs list - setTimeout(refreshJobList, 500); - } + }, []); + + const refresh = useCallback(() => { + loadModelSnapshots(); + // wait half a second before refreshing the jobs list + setTimeout(refreshJobList, 500); }, []); const columns: Array> = [ @@ -231,6 +241,7 @@ export const ModelSnapshotTable: FC = ({ job, refreshJobList }) => { snapshots={snapshots} job={job} closeFlyout={closeRevertFlyout} + refresh={refresh} /> )} diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx index 62f5623f6796..05e624c194e6 100644 --- a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx @@ -53,10 +53,17 @@ interface Props { snapshot: ModelSnapshot; snapshots: ModelSnapshot[]; job: CombinedJobWithStats; - closeFlyout(reload: boolean): void; + closeFlyout(): void; + refresh(): void; } -export const RevertModelSnapshotFlyout: FC = ({ snapshot, snapshots, job, closeFlyout }) => { +export const RevertModelSnapshotFlyout: FC = ({ + snapshot, + snapshots, + job, + closeFlyout, + refresh, +}) => { const { toasts } = useNotifications(); const { loadAnomalyDataForJob, loadEventRateForJob } = useMemo( () => chartLoaderProvider(mlResultsService), @@ -73,7 +80,6 @@ export const RevertModelSnapshotFlyout: FC = ({ snapshot, snapshots, job, const [eventRateData, setEventRateData] = useState([]); const [anomalies, setAnomalies] = useState([]); const [chartReady, setChartReady] = useState(false); - const [applying, setApplying] = useState(false); useEffect(() => { createChartData(); @@ -110,13 +116,6 @@ export const RevertModelSnapshotFlyout: FC = ({ snapshot, snapshots, job, setChartReady(true); }, [job]); - function closeWithReload() { - closeFlyout(true); - } - function closeWithoutReload() { - closeFlyout(false); - } - function showRevertModal() { setRevertModalVisible(true); } @@ -125,7 +124,6 @@ export const RevertModelSnapshotFlyout: FC = ({ snapshot, snapshots, job, } async function applyRevert() { - setApplying(true); const end = replay && runInRealTime === false ? job.data_counts.latest_record_timestamp : undefined; try { @@ -138,17 +136,19 @@ export const RevertModelSnapshotFlyout: FC = ({ snapshot, snapshots, job, })) : undefined; - await ml.jobs.revertModelSnapshot( - job.job_id, - currentSnapshot.snapshot_id, - replay, - end, - events - ); + ml.jobs + .revertModelSnapshot(job.job_id, currentSnapshot.snapshot_id, replay, end, events) + .then(() => { + toasts.addSuccess( + i18n.translate('xpack.ml.revertModelSnapshotFlyout.revertSuccessTitle', { + defaultMessage: 'Model snapshot revert successful', + }) + ); + refresh(); + }); hideRevertModal(); - closeWithReload(); + closeFlyout(); } catch (error) { - setApplying(false); toasts.addError(new Error(error.body.message), { title: i18n.translate('xpack.ml.revertModelSnapshotFlyout.revertErrorTitle', { defaultMessage: 'Model snapshot revert failed', @@ -166,7 +166,7 @@ export const RevertModelSnapshotFlyout: FC = ({ snapshot, snapshots, job, return ( <> - +
@@ -347,7 +347,7 @@ export const RevertModelSnapshotFlyout: FC = ({ snapshot, snapshots, job, - + = ({ snapshot, snapshots, job, defaultMessage: 'Apply', } )} - confirmButtonDisabled={applying} buttonColor="danger" defaultFocusedButton="confirm" - /> + > + + )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts index da8e272103f3..d74ed4447cfe 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts @@ -7,7 +7,10 @@ import { Direction, EuiBasicTableProps, Pagination, PropertySort } from '@elastic/eui'; import { useCallback, useMemo } from 'react'; import { ListingPageUrlState } from '../../../../../../../common/types/common'; -import { DataVisualizerIndexBasedAppState } from '../../../../../../../common/types/ml_url_generator'; +import { + DataVisualizerFileBasedAppState, + DataVisualizerIndexBasedAppState, +} from '../../../../../../../common/types/ml_url_generator'; const PAGE_SIZE_OPTIONS = [10, 25, 50]; @@ -38,7 +41,10 @@ interface UseTableSettingsReturnValue { export function useTableSettings( items: TypeOfItem[], - pageState: ListingPageUrlState | DataVisualizerIndexBasedAppState, + pageState: + | ListingPageUrlState + | DataVisualizerIndexBasedAppState + | DataVisualizerFileBasedAppState, updatePageState: (update: Partial) => void ): UseTableSettingsReturnValue { const { pageIndex, pageSize, sortField, sortDirection } = pageState; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/_index.scss b/x-pack/plugins/ml/public/application/datavisualizer/_index.scss index 081f8b971432..195eeea72baa 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/_index.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/_index.scss @@ -1,3 +1,3 @@ @import 'file_based/index'; @import 'index_based/index'; -@import 'stats_datagrid/index'; +@import 'stats_table/index'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/_index.scss b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/_index.scss index 42974d098bda..a7c3926407ea 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/_index.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/_index.scss @@ -1,7 +1,6 @@ @import 'file_datavisualizer_view/index'; @import 'results_view/index'; @import 'analysis_summary/index'; -@import 'fields_stats/index'; @import 'about_panel/index'; @import 'import_summary/index'; @import 'experimental_badge/index'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/field_data_card.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/expanded_row/file_based_expanded_row.tsx similarity index 52% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/field_data_card.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/expanded_row/file_based_expanded_row.tsx index a568356a06d2..77f31ae9c232 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/field_data_card.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/expanded_row/file_based_expanded_row.tsx @@ -4,45 +4,28 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiPanel } from '@elastic/eui'; -import React, { FC } from 'react'; - -import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types'; - -import { FieldVisConfig } from '../../common'; -import { FieldTitleBar } from '../../../../components/field_title_bar/index'; +import React from 'react'; import { BooleanContent, DateContent, GeoPointContent, IpContent, KeywordContent, - NotInDocsContent, - NumberContent, OtherContent, TextContent, -} from './content_types'; -import { LoadingIndicator } from './loading_indicator'; - -export interface FieldDataCardProps { - config: FieldVisConfig; -} + NumberContent, +} from '../../../stats_table/components/field_data_expanded_row'; +import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types'; +import type { FileBasedFieldVisConfig } from '../../../stats_table/types/field_vis_config'; -export const FieldDataCard: FC = ({ config }) => { - const { fieldName, loading, type, existsInDocs } = config; +export const FileBasedDataVisualizerExpandedRow = ({ item }: { item: FileBasedFieldVisConfig }) => { + const config = item; + const { type, fieldName } = config; function getCardContent() { - if (existsInDocs === false) { - return ; - } - switch (type) { case ML_JOB_FIELD_TYPES.NUMBER: - if (fieldName !== undefined) { - return ; - } else { - return null; - } + return ; case ML_JOB_FIELD_TYPES.BOOLEAN: return ; @@ -68,15 +51,11 @@ export const FieldDataCard: FC = ({ config }) => { } return ( - - -
- {loading === true ? : getCardContent()} -
-
+ {getCardContent()} +
); }; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/expanded_row/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/expanded_row/index.ts new file mode 100644 index 000000000000..0601f739ed81 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/expanded_row/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 { FileBasedDataVisualizerExpandedRow } from './file_based_expanded_row'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_data_row/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_data_row/index.ts new file mode 100644 index 000000000000..2a7eab9beb22 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_data_row/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 { FileBasedNumberContentPreview } from './number_content_preview'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_data_row/number_content_preview.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_data_row/number_content_preview.tsx new file mode 100644 index 000000000000..de6d129e0b46 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_data_row/number_content_preview.tsx @@ -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 React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { FileBasedFieldVisConfig } from '../../../stats_table/types'; + +export const FileBasedNumberContentPreview = ({ config }: { config: FileBasedFieldVisConfig }) => { + const stats = config.stats; + if ( + stats === undefined || + stats.min === undefined || + stats.median === undefined || + stats.max === undefined + ) + return null; + return ( + + + + + + + + + + + + + + + + + + + + {stats.min} + {stats.median} + {stats.max} + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_names_filter/field_names_filter.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_names_filter/field_names_filter.tsx new file mode 100644 index 000000000000..afc0a95e7f59 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_names_filter/field_names_filter.tsx @@ -0,0 +1,46 @@ +/* + * 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, { FC, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { MultiSelectPicker } from '../../../../components/multi_select_picker'; +import type { + FileBasedFieldVisConfig, + FileBasedUnknownFieldVisConfig, +} from '../../../stats_table/types/field_vis_config'; + +interface Props { + fields: Array; + setVisibleFieldNames(q: string[]): void; + visibleFieldNames: string[]; +} + +export const DataVisualizerFieldNamesFilter: FC = ({ + fields, + setVisibleFieldNames, + visibleFieldNames, +}) => { + const fieldNameTitle = useMemo( + () => + i18n.translate('xpack.ml.dataVisualizer.fileBased.fieldNameSelect', { + defaultMessage: 'Field name', + }), + [] + ); + const options = useMemo( + () => fields.filter((d) => d.fieldName !== undefined).map((d) => ({ value: d.fieldName! })), + [fields] + ); + + return ( + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_names_filter/index.ts similarity index 77% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/index.ts rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_names_filter/index.ts index 9b7939c90c71..1bd19e27d3b9 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/index.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_names_filter/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { FieldDataCard, FieldDataCardProps } from './field_data_card'; +export { DataVisualizerFieldNamesFilter } from './field_names_filter'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_types_filter/field_types_filter.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_types_filter/field_types_filter.tsx new file mode 100644 index 000000000000..f52a588844cd --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_types_filter/field_types_filter.tsx @@ -0,0 +1,79 @@ +/* + * 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, { FC, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { MultiSelectPicker, Option } from '../../../../components/multi_select_picker'; +import type { + FileBasedFieldVisConfig, + FileBasedUnknownFieldVisConfig, +} from '../../../stats_table/types/field_vis_config'; +import { FieldTypeIcon } from '../../../../components/field_type_icon'; +import { ML_JOB_FIELD_TYPES_OPTIONS } from '../../../index_based/components/search_panel/field_type_filter'; + +interface Props { + fields: Array; + setVisibleFieldTypes(q: string[]): void; + visibleFieldTypes: string[]; +} + +export const DataVisualizerFieldTypesFilter: FC = ({ + fields, + setVisibleFieldTypes, + visibleFieldTypes, +}) => { + const fieldNameTitle = useMemo( + () => + i18n.translate('xpack.ml.dataVisualizer.fileBased.fieldTypeSelect', { + defaultMessage: 'Field type', + }), + [] + ); + + const options = useMemo(() => { + const fieldTypesTracker = new Set(); + const fieldTypes: Option[] = []; + fields.forEach(({ type }) => { + if ( + type !== undefined && + !fieldTypesTracker.has(type) && + ML_JOB_FIELD_TYPES_OPTIONS[type] !== undefined + ) { + const item = ML_JOB_FIELD_TYPES_OPTIONS[type]; + + fieldTypesTracker.add(type); + fieldTypes.push({ + value: type, + name: ( + + {item.name} + {type && ( + + + + )} + + ), + }); + } + }); + return fieldTypes; + }, [fields]); + return ( + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_types_filter/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_types_filter/index.ts new file mode 100644 index 000000000000..1209fd94790c --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/field_types_filter/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 { DataVisualizerFieldTypesFilter } from './field_types_filter'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_index.scss b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_index.scss deleted file mode 100644 index fe6a232f016a..000000000000 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'fields_stats'; -@import 'field_stats_card'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/field_stats_card.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/field_stats_card.js deleted file mode 100644 index 2e9efa43f36b..000000000000 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/field_stats_card.js +++ /dev/null @@ -1,184 +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 { - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPanel, - EuiProgress, - EuiSpacer, - EuiText, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import { FieldTypeIcon } from '../../../../components/field_type_icon'; -import { DisplayValue } from '../../../../components/display_value'; -import { getMLJobTypeAriaLabel } from '../../../../util/field_types_utils'; - -export function FieldStatsCard({ field }) { - let type = field.type; - if (type === 'double' || type === 'long') { - type = 'number'; - } - - const typeAriaLabel = getMLJobTypeAriaLabel(type); - const cardTitleAriaLabel = [field.name]; - if (typeAriaLabel) { - cardTitleAriaLabel.unshift(typeAriaLabel); - } - - return ( - -
-
- -
- {field.name} -
-
- -
- {field.count > 0 && ( - -
- - - - - - - - - - - - - - - - - - {field.median_value && ( - -
-
- -
-
- -
-
- -
-
-
-
- -
-
- -
-
- -
-
-
- )} -
- - {field.top_hits && ( - - - -
-
- -
- {field.top_hits.map(({ count, value }) => { - const pcnt = Math.round((count / field.count) * 100 * 100) / 100; - return ( - - - - {value}  - - - - - - - - {pcnt}% - - - - ); - })} -
-
- )} -
- )} - {field.count === 0 && ( -
-
- -
-
- )} -
-
-
- ); -} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/fields_stats.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/fields_stats.js deleted file mode 100644 index 785dd7db260f..000000000000 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/fields_stats.js +++ /dev/null @@ -1,113 +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, { Component } from 'react'; - -import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui'; -import { FieldStatsCard } from './field_stats_card'; -import { getFieldNames } from './get_field_names'; -import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types'; -import { roundToDecimalPlace } from '../../../../formatters/round_to_decimal_place'; - -export class FieldsStats extends Component { - constructor(props) { - super(props); - - this.state = { - fields: [], - }; - } - - componentDidMount() { - this.setState({ - fields: createFields(this.props.results), - }); - } - - render() { - return ( -
- - {this.state.fields.map((f) => ( - - - - ))} - -
- ); - } -} - -function createFields(results) { - const { - mappings, - field_stats: fieldStats, - num_messages_analyzed: numMessagesAnalyzed, - timestamp_field: timestampField, - } = results; - - if (mappings && mappings.properties && fieldStats) { - const fieldNames = getFieldNames(results); - - return fieldNames.map((name) => { - if (fieldStats[name] !== undefined) { - const field = { name }; - const f = fieldStats[name]; - const m = mappings.properties[name]; - - // sometimes the timestamp field is not in the mappings, and so our - // collection of fields will be missing a time field with a type of date - if (name === timestampField && field.type === undefined) { - field.type = ML_JOB_FIELD_TYPES.DATE; - } - - if (f !== undefined) { - Object.assign(field, f); - } - - if (m !== undefined) { - field.type = m.type; - if (m.format !== undefined) { - field.format = m.format; - } - } - - const percent = (field.count / numMessagesAnalyzed) * 100; - field.percent = roundToDecimalPlace(percent); - - // round min, max, median, mean to 2dp. - if (field.median_value !== undefined) { - field.median_value = roundToDecimalPlace(field.median_value); - field.mean_value = roundToDecimalPlace(field.mean_value); - field.min_value = roundToDecimalPlace(field.min_value); - field.max_value = roundToDecimalPlace(field.max_value); - } - - return field; - } else { - // field is not in the field stats - // this could be the message field for a semi-structured log file or a - // field which the endpoint has not been able to work out any information for - const type = - mappings.properties[name] && mappings.properties[name].type === ML_JOB_FIELD_TYPES.TEXT - ? ML_JOB_FIELD_TYPES.TEXT - : ML_JOB_FIELD_TYPES.UNKNOWN; - - return { - name, - type, - mean_value: 0, - count: 0, - cardinality: 0, - percent: 0, - }; - } - }); - } - - return []; -} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/create_fields.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/create_fields.ts new file mode 100644 index 000000000000..6313656fc09c --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/create_fields.ts @@ -0,0 +1,126 @@ +/* + * 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 { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { getFieldNames, getSupportedFieldType } from './get_field_names'; +import { FileBasedFieldVisConfig } from '../../../stats_table/types'; +import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types'; +import { roundToDecimalPlace } from '../../../../formatters/round_to_decimal_place'; + +export function createFields(results: FindFileStructureResponse) { + const { + mappings, + field_stats: fieldStats, + num_messages_analyzed: numMessagesAnalyzed, + timestamp_field: timestampField, + } = results; + + let numericFieldsCount = 0; + + if (mappings && mappings.properties && fieldStats) { + const fieldNames = getFieldNames(results); + + const items = fieldNames.map((name) => { + if (fieldStats[name] !== undefined) { + const field: FileBasedFieldVisConfig = { + fieldName: name, + type: ML_JOB_FIELD_TYPES.UNKNOWN, + }; + const f = fieldStats[name]; + const m = mappings.properties[name]; + + // sometimes the timestamp field is not in the mappings, and so our + // collection of fields will be missing a time field with a type of date + if (name === timestampField && field.type === ML_JOB_FIELD_TYPES.UNKNOWN) { + field.type = ML_JOB_FIELD_TYPES.DATE; + } + + if (m !== undefined) { + field.type = getSupportedFieldType(m.type); + if (field.type === ML_JOB_FIELD_TYPES.NUMBER) { + numericFieldsCount += 1; + } + if (m.format !== undefined) { + field.format = m.format; + } + } + + let _stats = {}; + + // round min, max, median, mean to 2dp. + if (f.median_value !== undefined) { + _stats = { + ..._stats, + median: roundToDecimalPlace(f.median_value), + mean: roundToDecimalPlace(f.mean_value), + min: roundToDecimalPlace(f.min_value), + max: roundToDecimalPlace(f.max_value), + }; + } + if (f.cardinality !== undefined) { + _stats = { + ..._stats, + cardinality: f.cardinality, + count: f.count, + sampleCount: numMessagesAnalyzed, + }; + } + + if (f.top_hits !== undefined) { + if (field.type === ML_JOB_FIELD_TYPES.TEXT) { + _stats = { + ..._stats, + examples: f.top_hits.map((hit) => hit.value), + }; + } else { + _stats = { + ..._stats, + topValues: f.top_hits.map((hit) => ({ key: hit.value, doc_count: hit.count })), + }; + } + } + + if (field.type === ML_JOB_FIELD_TYPES.DATE) { + _stats = { + ..._stats, + earliest: f.earliest, + latest: f.latest, + }; + } + + field.stats = _stats; + return field; + } else { + // field is not in the field stats + // this could be the message field for a semi-structured log file or a + // field which the endpoint has not been able to work out any information for + const type = + mappings.properties[name] && mappings.properties[name].type === ML_JOB_FIELD_TYPES.TEXT + ? ML_JOB_FIELD_TYPES.TEXT + : ML_JOB_FIELD_TYPES.UNKNOWN; + + return { + fieldName: name, + type, + stats: { + mean: 0, + count: 0, + sampleCount: numMessagesAnalyzed, + cardinality: 0, + }, + }; + } + }); + + return { + fields: items, + totalFieldsCount: items.length, + totalMetricFieldsCount: numericFieldsCount, + }; + } + + return { fields: [], totalFieldsCount: 0, totalMetricFieldsCount: 0 }; +} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/fields_stats_grid.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/fields_stats_grid.tsx new file mode 100644 index 000000000000..e2911653ab41 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/fields_stats_grid.tsx @@ -0,0 +1,124 @@ +/* + * 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, { useMemo, FC } from 'react'; +import { EuiFlexGroup, EuiSpacer } from '@elastic/eui'; +import type { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { DataVisualizerTable, ItemIdToExpandedRowMap } from '../../../stats_table'; +import type { FileBasedFieldVisConfig } from '../../../stats_table/types/field_vis_config'; +import { FileBasedDataVisualizerExpandedRow } from '../expanded_row'; + +import { DataVisualizerFieldNamesFilter } from '../field_names_filter'; +import { DataVisualizerFieldTypesFilter } from '../field_types_filter'; +import { createFields } from './create_fields'; +import { filterFields } from './filter_fields'; +import { usePageUrlState } from '../../../../util/url_state'; +import { ML_PAGES } from '../../../../../../common/constants/ml_url_generator'; +import { + MetricFieldsCount, + TotalFieldsCount, +} from '../../../stats_table/components/field_count_stats'; +import type { DataVisualizerFileBasedAppState } from '../../../../../../common/types/ml_url_generator'; + +interface Props { + results: FindFileStructureResponse; +} +export const getDefaultDataVisualizerListState = (): Required => ({ + pageIndex: 0, + pageSize: 10, + sortField: 'fieldName', + sortDirection: 'asc', + visibleFieldTypes: [], + visibleFieldNames: [], + showDistributions: true, +}); + +function getItemIdToExpandedRowMap( + itemIds: string[], + items: FileBasedFieldVisConfig[] +): ItemIdToExpandedRowMap { + return itemIds.reduce((m: ItemIdToExpandedRowMap, fieldName: string) => { + const item = items.find((fieldVisConfig) => fieldVisConfig.fieldName === fieldName); + if (item !== undefined) { + m[fieldName] = ; + } + return m; + }, {} as ItemIdToExpandedRowMap); +} + +export const FieldsStatsGrid: FC = ({ results }) => { + const restorableDefaults = getDefaultDataVisualizerListState(); + const [ + dataVisualizerListState, + setDataVisualizerListState, + ] = usePageUrlState( + ML_PAGES.DATA_VISUALIZER_FILE, + restorableDefaults + ); + const visibleFieldTypes = + dataVisualizerListState.visibleFieldTypes ?? restorableDefaults.visibleFieldTypes; + const setVisibleFieldTypes = (values: string[]) => { + setDataVisualizerListState({ ...dataVisualizerListState, visibleFieldTypes: values }); + }; + + const visibleFieldNames = + dataVisualizerListState.visibleFieldNames ?? restorableDefaults.visibleFieldNames; + const setVisibleFieldNames = (values: string[]) => { + setDataVisualizerListState({ ...dataVisualizerListState, visibleFieldNames: values }); + }; + + const { fields, totalFieldsCount, totalMetricFieldsCount } = useMemo( + () => createFields(results), + [results, visibleFieldNames, visibleFieldTypes] + ); + const { filteredFields, visibleFieldsCount, visibleMetricsCount } = useMemo( + () => filterFields(fields, visibleFieldNames, visibleFieldTypes), + [results, visibleFieldNames, visibleFieldTypes] + ); + + const fieldsCountStats = { visibleFieldsCount, totalFieldsCount }; + const metricsStats = { visibleMetricsCount, totalMetricFieldsCount }; + + return ( +
+ + + + + + + + + + + + + + items={filteredFields} + pageState={dataVisualizerListState} + updatePageState={setDataVisualizerListState} + getItemIdToExpandedRowMap={getItemIdToExpandedRowMap} + /> +
+ ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/filter_fields.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/filter_fields.ts new file mode 100644 index 000000000000..9f3ec88507ae --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/filter_fields.ts @@ -0,0 +1,36 @@ +/* + * 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 { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types'; +import type { + FileBasedFieldVisConfig, + FileBasedUnknownFieldVisConfig, +} from '../../../stats_table/types/field_vis_config'; + +export function filterFields( + fields: Array, + visibleFieldNames: string[], + visibleFieldTypes: string[] +) { + let items = fields; + + if (visibleFieldTypes && visibleFieldTypes.length > 0) { + items = items.filter( + (config) => visibleFieldTypes.findIndex((field) => field === config.type) > -1 + ); + } + if (visibleFieldNames && visibleFieldNames.length > 0) { + items = items.filter((config) => { + return visibleFieldNames.findIndex((field) => field === config.fieldName) > -1; + }); + } + + return { + filteredFields: items, + visibleFieldsCount: items.length, + visibleMetricsCount: items.filter((d) => d.type === ML_JOB_FIELD_TYPES.NUMBER).length, + }; +} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/get_field_names.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/get_field_names.ts similarity index 53% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/get_field_names.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/get_field_names.ts index c423dc3c63e3..e2f73505f6cd 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/get_field_names.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/get_field_names.ts @@ -5,8 +5,11 @@ */ import { difference } from 'lodash'; - -export function getFieldNames(results) { +import type { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; +import { MlJobFieldType } from '../../../../../../common/types/field_types'; +import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types'; +import { ES_FIELD_TYPES } from '../../../../../../../../../src/plugins/data/common'; +export function getFieldNames(results: FindFileStructureResponse) { const { mappings, field_stats: fieldStats, column_names: columnNames } = results; // if columnNames exists (i.e delimited) use it for the field list @@ -29,3 +32,24 @@ export function getFieldNames(results) { } return tempFields; } + +export function getSupportedFieldType(type: string): MlJobFieldType { + switch (type) { + case ES_FIELD_TYPES.FLOAT: + case ES_FIELD_TYPES.HALF_FLOAT: + case ES_FIELD_TYPES.SCALED_FLOAT: + case ES_FIELD_TYPES.DOUBLE: + case ES_FIELD_TYPES.INTEGER: + case ES_FIELD_TYPES.LONG: + case ES_FIELD_TYPES.SHORT: + case ES_FIELD_TYPES.UNSIGNED_LONG: + return ML_JOB_FIELD_TYPES.NUMBER; + + case ES_FIELD_TYPES.DATE: + case ES_FIELD_TYPES.DATE_NANOS: + return ML_JOB_FIELD_TYPES.DATE; + + default: + return type as MlJobFieldType; + } +} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/index.ts new file mode 100644 index 000000000000..693d9578644a --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats_grid/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 { FieldsStatsGrid } from './fields_stats_grid'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js index 56b81e36f1e9..a376e64b7821 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js @@ -228,7 +228,6 @@ export class FileDataVisualizerView extends Component { }; setOverrides = (overrides) => { - console.log('setOverrides', overrides); this.setState( { loading: true, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.tsx index f9de03c119d2..12e60ea49142 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { FC } from 'react'; @@ -15,7 +14,6 @@ import { EuiPageBody, EuiPageContentHeader, EuiPanel, - EuiTabbedContent, EuiSpacer, EuiTitle, EuiFlexGroup, @@ -25,8 +23,7 @@ import { FindFileStructureResponse } from '../../../../../../common/types/file_d import { FileContents } from '../file_contents'; import { AnalysisSummary } from '../analysis_summary'; -// @ts-ignore -import { FieldsStats } from '../fields_stats'; +import { FieldsStatsGrid } from '../fields_stats_grid'; interface Props { data: string; @@ -45,16 +42,6 @@ export const ResultsView: FC = ({ showExplanationFlyout, disableButtons, }) => { - const tabs = [ - { - id: 'file-stats', - name: i18n.translate('xpack.ml.fileDatavisualizer.resultsView.fileStatsTabName', { - defaultMessage: 'File stats', - }), - content: , - }, - ]; - return ( @@ -103,7 +90,16 @@ export const ResultsView: FC = ({ - {}} /> + +

+ +

+
+ +
diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/_index.scss b/x-pack/plugins/ml/public/application/datavisualizer/index_based/_index.scss index 53943f8ada6e..95a523753dfc 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/_index.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/_index.scss @@ -1 +1 @@ -@import 'components/field_data_card/index'; +@import 'components/field_data_row/index'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/common/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/index_based/common/index.ts index 50278c300d10..35bec2eb6637 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/common/index.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/common/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { FieldVisConfig } from './field_vis_config'; export { FieldHistogramRequestConfig, FieldRequestConfig } from './request'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/expanded_row.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/expanded_row/expanded_row.tsx similarity index 75% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/expanded_row.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/expanded_row/expanded_row.tsx index cb83d6db83ed..7018f73ff6c3 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/expanded_row/expanded_row.tsx @@ -6,22 +6,23 @@ import React from 'react'; -import { FieldVisConfig } from '../index_based/common'; +import { FieldVisConfig } from '../../../stats_table/types'; import { BooleanContent, DateContent, GeoPointContent, IpContent, KeywordContent, - NotInDocsContent, + NumberContent, OtherContent, TextContent, -} from '../index_based/components/field_data_card/content_types'; -import { NumberContent } from './components/field_data_expanded_row/number_content'; -import { ML_JOB_FIELD_TYPES } from '../../../../common/constants/field_types'; -import { LoadingIndicator } from '../index_based/components/field_data_card/loading_indicator'; +} from '../../../stats_table/components/field_data_expanded_row'; -export const DataVisualizerFieldExpandedRow = ({ item }: { item: FieldVisConfig }) => { +import { ML_JOB_FIELD_TYPES } from '../../../../../../common/constants/field_types'; +import { LoadingIndicator } from '../field_data_row/loading_indicator'; +import { NotInDocsContent } from '../field_data_row/content_types'; + +export const IndexBasedDataVisualizerExpandedRow = ({ item }: { item: FieldVisConfig }) => { const config = item; const { loading, type, existsInDocs, fieldName } = config; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/expanded_row/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/expanded_row/index.ts new file mode 100644 index 000000000000..3b393d96c97e --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/expanded_row/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 { IndexBasedDataVisualizerExpandedRow } from './expanded_row'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_count_panel/field_count_panel.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_count_panel/field_count_panel.tsx index 61bf244fbbcd..1996ca585147 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_count_panel/field_count_panel.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_count_panel/field_count_panel.tsx @@ -4,19 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiNotificationBadge, EuiSwitch, EuiText } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSwitch } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { FC } from 'react'; +import { + MetricFieldsCount, + TotalFieldsCount, +} from '../../../stats_table/components/field_count_stats'; +import type { + TotalFieldsCountProps, + MetricFieldsCountProps, +} from '../../../stats_table/components/field_count_stats'; -interface Props { - metricsStats?: { - visibleMetricFields: number; - totalMetricFields: number; - }; - fieldsCountStats?: { - visibleFieldsCount: number; - totalFieldsCount: number; - }; +interface Props extends TotalFieldsCountProps, MetricFieldsCountProps { showEmptyFields: boolean; toggleShowEmptyFields: () => void; } @@ -33,83 +33,8 @@ export const FieldCountPanel: FC = ({ style={{ marginLeft: 4 }} data-test-subj="mlDataVisualizerFieldCountPanel" > - {fieldsCountStats && ( - - - -
- -
-
-
- - - - {fieldsCountStats.visibleFieldsCount} - - - - - - - -
- )} - - {metricsStats && ( - - - -
- -
-
-
- - - {metricsStats.visibleMetricFields} - - - - - - - -
- )} - + + = 0.1) { - return `${value}%`; - } else { - return '< 0.1%'; - } -} - -export const BooleanContent: FC = ({ config }) => { - const { stats } = config; - if (stats === undefined) return null; - const { count, trueCount, falseCount } = stats; - if (count === undefined || trueCount === undefined || falseCount === undefined) return null; - - return ( -
- - - - - - - - - -
- ); -}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/geo_point_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/geo_point_content.tsx deleted file mode 100644 index 0c10cf1e6adc..000000000000 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/geo_point_content.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 React, { FC } from 'react'; - -import { FieldDataCardProps } from '../field_data_card'; -import { ExamplesList } from '../examples_list'; - -export const GeoPointContent: FC = ({ config }) => { - // TODO - adjust server-side query to get examples using: - - // GET /filebeat-apache-2019.01.30/_search - // { - // "size":10, - // "_source": false, - // "docvalue_fields": ["source.geo.location"], - // "query": { - // "bool":{ - // "must":[ - // { - // "exists":{ - // "field":"source.geo.location" - // } - // } - // ] - // } - // } - // } - - const { stats } = config; - if (stats?.examples === undefined) return null; - - return ( -
- -
- ); -}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/ip_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/ip_content.tsx deleted file mode 100644 index 4b54e86cdc49..000000000000 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/ip_content.tsx +++ /dev/null @@ -1,34 +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, { FC } from 'react'; -import { EuiSpacer } from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n/react'; - -import { FieldDataCardProps } from '../field_data_card'; -import { TopValues } from '../top_values'; -import { ExpandedRowFieldHeader } from '../../../../stats_datagrid/components/expanded_row_field_header'; - -export const IpContent: FC = ({ config }) => { - const { stats, fieldFormat } = config; - if (stats === undefined) return null; - const { count, sampleCount, cardinality } = stats; - if (count === undefined || sampleCount === undefined || cardinality === undefined) return null; - - return ( -
- - - - - -
- ); -}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/keyword_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/keyword_content.tsx deleted file mode 100644 index 18c4fb190a12..000000000000 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/keyword_content.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { FC } from 'react'; -import { EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { FieldDataCardProps } from '../field_data_card'; -import { TopValues } from '../top_values'; -import { ExpandedRowFieldHeader } from '../../../../stats_datagrid/components/expanded_row_field_header'; - -export const KeywordContent: FC = ({ config }) => { - const { stats, fieldFormat } = config; - - return ( -
- - - - - -
- ); -}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/number_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/number_content.tsx deleted file mode 100644 index 782880105da2..000000000000 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/number_content.tsx +++ /dev/null @@ -1,200 +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, { FC, Fragment, useEffect, useState } from 'react'; -import { - EuiButtonGroup, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiSpacer, - EuiText, -} from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; - -import { FieldDataCardProps } from '../field_data_card'; -import { DisplayValue } from '../../../../../components/display_value'; -import { kibanaFieldFormat } from '../../../../../formatters/kibana_field_format'; -import { numberAsOrdinal } from '../../../../../formatters/number_as_ordinal'; -import { roundToDecimalPlace } from '../../../../../formatters/round_to_decimal_place'; -import { - MetricDistributionChart, - MetricDistributionChartData, - buildChartDataFromStats, -} from '../metric_distribution_chart'; -import { TopValues } from '../top_values'; - -const DETAILS_MODE = { - DISTRIBUTION: 'distribution', - TOP_VALUES: 'top_values', -} as const; - -type DetailsModeType = typeof DETAILS_MODE[keyof typeof DETAILS_MODE]; - -const METRIC_DISTRIBUTION_CHART_WIDTH = 325; -const METRIC_DISTRIBUTION_CHART_HEIGHT = 210; -const DEFAULT_TOP_VALUES_THRESHOLD = 100; - -export const NumberContent: FC = ({ config }) => { - const { stats, fieldFormat } = config; - - useEffect(() => { - const chartData = buildChartDataFromStats(stats, METRIC_DISTRIBUTION_CHART_WIDTH); - setDistributionChartData(chartData); - }, []); - const [detailsMode, setDetailsMode] = useState( - stats?.cardinality ?? 0 <= DEFAULT_TOP_VALUES_THRESHOLD - ? DETAILS_MODE.TOP_VALUES - : DETAILS_MODE.DISTRIBUTION - ); - const defaultChartData: MetricDistributionChartData[] = []; - const [distributionChartData, setDistributionChartData] = useState(defaultChartData); - - if (stats === undefined) return null; - const { count, sampleCount, cardinality, min, median, max, distribution } = stats; - if (count === undefined || sampleCount === undefined) return null; - - const docsPercent = roundToDecimalPlace((count / sampleCount) * 100); - - const detailsOptions = [ - { - id: DETAILS_MODE.TOP_VALUES, - label: i18n.translate('xpack.ml.fieldDataCard.cardNumber.details.topValuesLabel', { - defaultMessage: 'Top values', - }), - }, - { - id: DETAILS_MODE.DISTRIBUTION, - label: i18n.translate('xpack.ml.fieldDataCard.cardNumber.details.distributionOfValuesLabel', { - defaultMessage: 'Distribution', - }), - }, - ]; - - return ( -
-
- - -   - - -
- -
- - -   - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - setDetailsMode(optionId as DetailsModeType)} - legend={i18n.translate( - 'xpack.ml.fieldDataCard.cardNumber.selectMetricDetailsDisplayAriaLabel', - { - defaultMessage: 'Select display option for metric details', - } - )} - data-test-subj="mlFieldDataCardDetailsSelect" - isFullWidth={true} - buttonSize="compressed" - /> - - {distribution && detailsMode === DETAILS_MODE.DISTRIBUTION && ( - - - - - - - - - - - - - - - )} - {detailsMode === DETAILS_MODE.TOP_VALUES && ( - - - - - - )} -
- ); -}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/other_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/other_content.tsx deleted file mode 100644 index 065d7d40c23e..000000000000 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/other_content.tsx +++ /dev/null @@ -1,83 +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, { FC, Fragment } from 'react'; -import { EuiIcon, EuiSpacer, EuiText } from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n/react'; - -import { FieldDataCardProps } from '../field_data_card'; -import { roundToDecimalPlace } from '../../../../../formatters/round_to_decimal_place'; -import { ExamplesList } from '../examples_list'; - -export const OtherContent: FC = ({ config }) => { - const { stats, type, aggregatable } = config; - if (stats === undefined) return null; - - const { count, sampleCount, cardinality, examples } = stats; - if ( - count === undefined || - sampleCount === undefined || - cardinality === undefined || - examples === undefined - ) - return null; - - const docsPercent = roundToDecimalPlace((count / sampleCount) * 100); - - return ( -
-
- - - -
- {aggregatable === true && ( - - -
- - -   - - -
- - - -
- - -   - - -
-
- )} - - -
- ); -}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/text_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/text_content.tsx deleted file mode 100644 index d54d2237c660..000000000000 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/text_content.tsx +++ /dev/null @@ -1,62 +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, { FC, Fragment } from 'react'; -import { EuiCallOut, EuiSpacer } from '@elastic/eui'; - -import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; - -import { FieldDataCardProps } from '../field_data_card'; -import { ExamplesList } from '../examples_list'; - -export const TextContent: FC = ({ config }) => { - const { stats } = config; - if (stats === undefined) return null; - - const { examples } = stats; - if (examples === undefined) return null; - - const numExamples = examples.length; - - return ( -
- {numExamples > 0 && } - {numExamples === 0 && ( - - - - _source, - }} - /> - - - - copy_to, - sourceParam: _source, - includesParam: includes, - excludesParam: excludes, - }} - /> - - - )} -
- ); -}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_index.scss b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/_index.scss similarity index 55% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_index.scss rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/_index.scss index e7c155d2554b..38327dc51bd9 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_index.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/_index.scss @@ -1,2 +1 @@ -@import 'field_data_card'; @import 'top_values/top_values'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/document_count_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/content_types/document_count_content.tsx similarity index 90% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/document_count_content.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/content_types/document_count_content.tsx index e7b9604c1c06..2df1b6799214 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/document_count_content.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/content_types/document_count_content.tsx @@ -6,11 +6,11 @@ import React, { FC } from 'react'; -import type { FieldDataCardProps } from '../field_data_card'; +import type { FieldDataRowProps } from '../../../../stats_table/types/field_data_row'; import { DocumentCountChart, DocumentCountChartPoint } from '../document_count_chart'; import { TotalCountHeader } from '../../total_count_header'; -export interface Props extends FieldDataCardProps { +export interface Props extends FieldDataRowProps { totalCount: number; } diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/content_types/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/content_types/index.ts new file mode 100644 index 000000000000..dd1f38b4a134 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/content_types/index.ts @@ -0,0 +1,8 @@ +/* + * 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 { DocumentCountContent } from './document_count_content'; +export { NotInDocsContent } from './not_in_docs_content'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/not_in_docs_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/content_types/not_in_docs_content.tsx similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/not_in_docs_content.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/content_types/not_in_docs_content.tsx diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/document_count_chart.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/document_count_chart/document_count_chart.tsx similarity index 98% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/document_count_chart.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/document_count_chart/document_count_chart.tsx index 6a02cb6acebd..f0ac9b7e67d3 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/document_count_chart.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/document_count_chart/document_count_chart.tsx @@ -25,7 +25,6 @@ export interface DocumentCountChartPoint { interface Props { width?: number; - height?: number; chartPoints: DocumentCountChartPoint[]; timeRangeEarliest: number; timeRangeLatest: number; @@ -35,7 +34,6 @@ const SPEC_ID = 'document_count'; export const DocumentCountChart: FC = ({ width, - height, chartPoints, timeRangeEarliest, timeRangeLatest, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/document_count_chart/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/document_count_chart/index.ts rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/document_count_chart/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/examples_list/examples_list.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/examples_list/examples_list.tsx similarity index 93% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/examples_list/examples_list.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/examples_list/examples_list.tsx index 5591e6f9b541..1e8f7586258d 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/examples_list/examples_list.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/examples_list/examples_list.tsx @@ -9,7 +9,7 @@ import React, { FC } from 'react'; import { EuiListGroup, EuiListGroupItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ExpandedRowFieldHeader } from '../../../../stats_datagrid/components/expanded_row_field_header'; +import { ExpandedRowFieldHeader } from '../../../../stats_table/components/expanded_row_field_header'; interface Props { examples: Array; } diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/examples_list/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/examples_list/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/examples_list/index.ts rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/examples_list/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/loading_indicator/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/loading_indicator/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/loading_indicator/index.ts rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/loading_indicator/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/loading_indicator/loading_indicator.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/loading_indicator/loading_indicator.tsx similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/loading_indicator/loading_indicator.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/loading_indicator/loading_indicator.tsx diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/top_values/_top_values.scss b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/top_values/_top_values.scss similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/top_values/_top_values.scss rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/top_values/_top_values.scss diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/top_values/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/top_values/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/top_values/index.ts rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/top_values/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/top_values/top_values.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/top_values/top_values.tsx similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/top_values/top_values.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_row/top_values/top_values.tsx diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx index af3c1a0e7c16..8064e08c9f0f 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/search_panel/search_panel.tsx @@ -157,8 +157,6 @@ export const SearchPanel: FC = ({ setVisibleFieldTypes={setVisibleFieldTypes} visibleFieldTypes={visibleFieldTypes} /> - -
); }; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx b/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx index 9819bf451e42..e5b243d52403 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/index_based/page.tsx @@ -45,17 +45,23 @@ import { getToastNotifications } from '../../util/dependency_cache'; import { usePageUrlState, useUrlState } from '../../util/url_state'; import { ActionsPanel } from './components/actions_panel'; import { SearchPanel } from './components/search_panel'; -import { DocumentCountContent } from './components/field_data_card/content_types/document_count_content'; -import { DataVisualizerDataGrid } from '../stats_datagrid'; +import { DocumentCountContent } from './components/field_data_row/content_types/document_count_content'; +import { DataVisualizerTable, ItemIdToExpandedRowMap } from '../stats_table'; import { FieldCountPanel } from './components/field_count_panel'; import { ML_PAGES } from '../../../../common/constants/ml_url_generator'; import { DataLoader } from './data_loader'; -import type { FieldRequestConfig, FieldVisConfig } from './common'; +import type { FieldRequestConfig } from './common'; import type { DataVisualizerIndexBasedAppState } from '../../../../common/types/ml_url_generator'; import type { OverallStats } from '../../../../common/types/datavisualizer'; import { MlJobFieldType } from '../../../../common/types/field_types'; import { HelpMenu } from '../../components/help_menu'; import { useMlKibana } from '../../contexts/kibana'; +import { IndexBasedDataVisualizerExpandedRow } from './components/expanded_row'; +import { FieldVisConfig } from '../stats_table/types'; +import type { + MetricFieldsStats, + TotalFieldsStats, +} from '../stats_table/components/field_count_stats'; interface DataVisualizerPageState { overallStats: OverallStats; @@ -106,6 +112,19 @@ export const getDefaultDataVisualizerListState = (): Required { + const item = items.find((fieldVisConfig) => fieldVisConfig.fieldName === fieldName); + if (item !== undefined) { + m[fieldName] = ; + } + return m; + }, {} as ItemIdToExpandedRowMap); +} + export const Page: FC = () => { const mlContext = useMlContext(); const restorableDefaults = getDefaultDataVisualizerListState(); @@ -228,9 +247,7 @@ export const Page: FC = () => { const [documentCountStats, setDocumentCountStats] = useState(defaults.documentCountStats); const [metricConfigs, setMetricConfigs] = useState(defaults.metricConfigs); const [metricsLoaded, setMetricsLoaded] = useState(defaults.metricsLoaded); - const [metricsStats, setMetricsStats] = useState< - undefined | { visibleMetricFields: number; totalMetricFields: number } - >(); + const [metricsStats, setMetricsStats] = useState(); const [nonMetricConfigs, setNonMetricConfigs] = useState(defaults.nonMetricConfigs); const [nonMetricsLoaded, setNonMetricsLoaded] = useState(defaults.nonMetricsLoaded); @@ -537,8 +554,8 @@ export const Page: FC = () => { }); setMetricsStats({ - totalMetricFields: allMetricFields.length, - visibleMetricFields: metricFieldsToShow.length, + totalMetricFieldsCount: allMetricFields.length, + visibleMetricsCount: metricFieldsToShow.length, }); setMetricConfigs(configs); } @@ -642,7 +659,7 @@ export const Page: FC = () => { return combinedConfigs; }, [nonMetricConfigs, metricConfigs, visibleFieldTypes, visibleFieldNames]); - const fieldsCountStats = useMemo(() => { + const fieldsCountStats: TotalFieldsStats | undefined = useMemo(() => { let _visibleFieldsCount = 0; let _totalFieldsCount = 0; Object.keys(overallStats).forEach((key) => { @@ -736,10 +753,11 @@ export const Page: FC = () => { metricsStats={metricsStats} /> - items={configs} pageState={dataVisualizerListState} updatePageState={setDataVisualizerListState} + getItemIdToExpandedRowMap={getItemIdToExpandedRowMap} />
diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_field_data_card.scss b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/_field_data_row.scss similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_field_data_card.scss rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/_field_data_row.scss diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/_index.scss b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/_index.scss similarity index 87% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/_index.scss rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/_index.scss index e9ecfc8b1910..6e7e66db9e03 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/_index.scss +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/_index.scss @@ -1,4 +1,5 @@ @import 'components/field_data_expanded_row/number_content'; +@import 'components/field_count_stats/index'; .mlDataVisualizerFieldExpandedRow { padding-left: $euiSize * 4; @@ -35,6 +36,7 @@ } } .mlDataVisualizerSummaryTable { + max-width: 350px; .euiTableRow > .euiTableRowCell { border-bottom: 0; } @@ -42,4 +44,7 @@ display: none; } } + .mlDataVisualizerSummaryTableWrapper { + max-width: 350px; + } } diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/expanded_row_field_header/expanded_row_field_header.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/expanded_row_field_header/expanded_row_field_header.tsx similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/expanded_row_field_header/expanded_row_field_header.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/expanded_row_field_header/expanded_row_field_header.tsx diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/expanded_row_field_header/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/expanded_row_field_header/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/expanded_row_field_header/index.ts rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/expanded_row_field_header/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/_index.scss b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/_index.scss new file mode 100644 index 000000000000..7154d0da2c09 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/_index.scss @@ -0,0 +1,3 @@ +.mlDataVisualizerFieldCountContainer { + max-width: 300px; +} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/index.ts new file mode 100644 index 000000000000..15c9c92f51cf --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/index.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 { TotalFieldsCount, TotalFieldsCountProps, TotalFieldsStats } from './total_fields_count'; +export { + MetricFieldsCount, + MetricFieldsCountProps, + MetricFieldsStats, +} from './metric_fields_count'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/metric_fields_count.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/metric_fields_count.tsx new file mode 100644 index 000000000000..327a3e611296 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/metric_fields_count.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiNotificationBadge, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { FC } from 'react'; + +export interface MetricFieldsStats { + visibleMetricsCount: number; + totalMetricFieldsCount: number; +} +export interface MetricFieldsCountProps { + metricsStats?: MetricFieldsStats; +} + +export const MetricFieldsCount: FC = ({ metricsStats }) => { + if ( + !metricsStats || + metricsStats.visibleMetricsCount === undefined || + metricsStats.totalMetricFieldsCount === undefined + ) + return null; + return ( + <> + {metricsStats && ( + + + +
+ +
+
+
+ + + {metricsStats.visibleMetricsCount} + + + + + + + +
+ )} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/total_fields_count.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/total_fields_count.tsx new file mode 100644 index 000000000000..c90770dbf8c5 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_count_stats/total_fields_count.tsx @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiNotificationBadge, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { FC } from 'react'; + +export interface TotalFieldsStats { + visibleFieldsCount: number; + totalFieldsCount: number; +} + +export interface TotalFieldsCountProps { + fieldsCountStats?: TotalFieldsStats; +} + +export const TotalFieldsCount: FC = ({ fieldsCountStats }) => { + if ( + !fieldsCountStats || + fieldsCountStats.visibleFieldsCount === undefined || + fieldsCountStats.totalFieldsCount === undefined + ) + return null; + + return ( + + + +
+ +
+
+
+ + + + {fieldsCountStats.visibleFieldsCount} + + + + + + + +
+ ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_index.scss b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/_index.scss similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_index.scss rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/_index.scss diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_number_content.scss b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/_number_content.scss similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/_number_content.scss rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/_number_content.scss diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/boolean_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/boolean_content.tsx new file mode 100644 index 000000000000..a75920dd09b3 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/boolean_content.tsx @@ -0,0 +1,143 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC, ReactNode, useMemo } from 'react'; +import { EuiBasicTable, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { Axis, BarSeries, Chart, Settings } from '@elastic/charts'; + +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import type { FieldDataRowProps } from '../../types/field_data_row'; +import { ExpandedRowFieldHeader } from '../expanded_row_field_header'; +import { getTFPercentage } from '../../utils'; +import { roundToDecimalPlace } from '../../../../formatters/round_to_decimal_place'; +import { useDataVizChartTheme } from '../../hooks'; +import { DocumentStatsTable } from './document_stats'; + +function getPercentLabel(value: number): string { + if (value === 0) { + return '0%'; + } + if (value >= 0.1) { + return `${roundToDecimalPlace(value)}%`; + } else { + return '< 0.1%'; + } +} + +function getFormattedValue(value: number, totalCount: number): string { + const percentage = (value / totalCount) * 100; + return `${value} (${getPercentLabel(percentage)})`; +} + +const BOOLEAN_DISTRIBUTION_CHART_HEIGHT = 100; + +export const BooleanContent: FC = ({ config }) => { + const fieldFormat = 'fieldFormat' in config ? config.fieldFormat : undefined; + const formattedPercentages = useMemo(() => getTFPercentage(config), [config]); + const theme = useDataVizChartTheme(); + if (!formattedPercentages) return null; + + const { trueCount, falseCount, count } = formattedPercentages; + const summaryTableItems = [ + { + function: 'true', + display: ( + + ), + value: getFormattedValue(trueCount, count), + }, + { + function: 'false', + display: ( + + ), + value: getFormattedValue(falseCount, count), + }, + ]; + const summaryTableColumns = [ + { + name: '', + render: (summaryItem: { display: ReactNode }) => summaryItem.display, + width: '75px', + }, + { + field: 'value', + name: '', + render: (v: string) => {v}, + }, + ]; + + const summaryTableTitle = i18n.translate( + 'xpack.ml.fieldDataCardExpandedRow.booleanContent.summaryTableTitle', + { + defaultMessage: 'Summary', + } + ); + + return ( + + + + + {summaryTableTitle} + + + + + + + + + + + getFormattedValue(d, count)} + /> + + + + + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/date_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/date_content.tsx similarity index 58% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/date_content.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/date_content.tsx index 7651d20249c9..8d122df62838 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/date_content.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/date_content.tsx @@ -5,14 +5,15 @@ */ import React, { FC, ReactNode } from 'react'; -import { EuiBasicTable } from '@elastic/eui'; +import { EuiBasicTable, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; // @ts-ignore import { formatDate } from '@elastic/eui/lib/services/format'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { FieldDataCardProps } from '../field_data_card'; -import { ExpandedRowFieldHeader } from '../../../../stats_datagrid/components/expanded_row_field_header'; +import type { FieldDataRowProps } from '../../types/field_data_row'; +import { ExpandedRowFieldHeader } from '../expanded_row_field_header'; +import { DocumentStatsTable } from './document_stats'; const TIME_FORMAT = 'MMM D YYYY, HH:mm:ss.SSS'; interface SummaryTableItem { function: string; @@ -20,7 +21,7 @@ interface SummaryTableItem { value: number | string | undefined | null; } -export const DateContent: FC = ({ config }) => { +export const DateContent: FC = ({ config }) => { const { stats } = config; if (stats === undefined) return null; @@ -38,7 +39,7 @@ export const DateContent: FC = ({ config }) => { defaultMessage="earliest" /> ), - value: formatDate(earliest, TIME_FORMAT), + value: typeof earliest === 'string' ? earliest : formatDate(earliest, TIME_FORMAT), }, { function: 'latest', @@ -48,7 +49,7 @@ export const DateContent: FC = ({ config }) => { defaultMessage="latest" /> ), - value: formatDate(latest, TIME_FORMAT), + value: typeof latest === 'string' ? latest : formatDate(latest, TIME_FORMAT), }, ]; const summaryTableColumns = [ @@ -65,17 +66,20 @@ export const DateContent: FC = ({ config }) => { ]; return ( - <> - {summaryTableTitle} - - className={'mlDataVisualizerSummaryTable'} - data-test-subj={'mlDateSummaryTable'} - compressed - items={summaryTableItems} - columns={summaryTableColumns} - tableCaption={summaryTableTitle} - tableLayout="auto" - /> - + + + + {summaryTableTitle} + + className={'mlDataVisualizerSummaryTable'} + data-test-subj={'mlDateSummaryTable'} + compressed + items={summaryTableItems} + columns={summaryTableColumns} + tableCaption={summaryTableTitle} + tableLayout="auto" + /> + + ); }; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/document_stats.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/document_stats.tsx new file mode 100644 index 000000000000..177ac722166f --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/document_stats.tsx @@ -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 { FormattedMessage } from '@kbn/i18n/react'; +import React, { FC, ReactNode } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiBasicTable, EuiFlexItem } from '@elastic/eui'; +import { ExpandedRowFieldHeader } from '../expanded_row_field_header'; +import { FieldDataRowProps } from '../../types'; + +const metaTableColumns = [ + { + name: '', + render: (metaItem: { display: ReactNode }) => metaItem.display, + width: '75px', + }, + { + field: 'value', + name: '', + render: (v: string) => {v}, + }, +]; + +const metaTableTitle = i18n.translate( + 'xpack.ml.fieldDataCardExpandedRow.documentStatsTable.metaTableTitle', + { + defaultMessage: 'Documents stats', + } +); + +export const DocumentStatsTable: FC = ({ config }) => { + if ( + config?.stats === undefined || + config.stats.cardinality === undefined || + config.stats.count === undefined || + config.stats.sampleCount === undefined + ) + return null; + const { cardinality, count, sampleCount } = config.stats; + const metaTableItems = [ + { + function: 'count', + display: ( + + ), + value: count, + }, + { + function: 'percentage', + display: ( + + ), + value: `${(count / sampleCount) * 100}%`, + }, + { + function: 'distinctValues', + display: ( + + ), + value: cardinality, + }, + ]; + + return ( + + {metaTableTitle} + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/geo_point_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/geo_point_content.tsx new file mode 100644 index 000000000000..993c7a94f5e0 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/geo_point_content.tsx @@ -0,0 +1,35 @@ +/* + * 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, { FC } from 'react'; + +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import type { FieldDataRowProps } from '../../types/field_data_row'; +import { ExamplesList } from '../../../index_based/components/field_data_row/examples_list'; +import { DocumentStatsTable } from './document_stats'; +import { TopValues } from '../../../index_based/components/field_data_row/top_values'; + +export const GeoPointContent: FC = ({ config }) => { + const { stats } = config; + if (stats === undefined || (stats?.examples === undefined && stats?.topValues === undefined)) + return null; + + return ( + + + {Array.isArray(stats.examples) && ( + + + + )} + {Array.isArray(stats.topValues) && ( + + + + )} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/index.ts similarity index 83% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/index.ts rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/index.ts index 230be246eb4e..c6cd50f6bc2e 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/content_types/index.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/index.ts @@ -6,11 +6,9 @@ export { BooleanContent } from './boolean_content'; export { DateContent } from './date_content'; -export { DocumentCountContent } from './document_count_content'; export { GeoPointContent } from './geo_point_content'; export { KeywordContent } from './keyword_content'; export { IpContent } from './ip_content'; -export { NotInDocsContent } from './not_in_docs_content'; export { NumberContent } from './number_content'; export { OtherContent } from './other_content'; export { TextContent } from './text_content'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/ip_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/ip_content.tsx new file mode 100644 index 000000000000..79492bb44a2d --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/ip_content.tsx @@ -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 React, { FC } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n/react'; + +import type { FieldDataRowProps } from '../../types/field_data_row'; +import { TopValues } from '../../../index_based/components/field_data_row/top_values'; +import { ExpandedRowFieldHeader } from '../expanded_row_field_header'; +import { DocumentStatsTable } from './document_stats'; + +export const IpContent: FC = ({ config }) => { + const { stats } = config; + if (stats === undefined) return null; + const { count, sampleCount, cardinality } = stats; + if (count === undefined || sampleCount === undefined || cardinality === undefined) return null; + const fieldFormat = 'fieldFormat' in config ? config.fieldFormat : undefined; + + return ( + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/keyword_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/keyword_content.tsx new file mode 100644 index 000000000000..634f5b55513a --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/keyword_content.tsx @@ -0,0 +1,35 @@ +/* + * 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, { FC } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import type { FieldDataRowProps } from '../../types/field_data_row'; +import { TopValues } from '../../../index_based/components/field_data_row/top_values'; +import { ExpandedRowFieldHeader } from '../expanded_row_field_header'; +import { DocumentStatsTable } from './document_stats'; + +export const KeywordContent: FC = ({ config }) => { + const { stats } = config; + const fieldFormat = 'fieldFormat' in config ? config.fieldFormat : undefined; + + return ( + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/number_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/number_content.tsx similarity index 89% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/number_content.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/number_content.tsx index c3ba6d23f6ba..d05de26d3c5d 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_expanded_row/number_content.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/number_content.tsx @@ -9,17 +9,17 @@ import { EuiBasicTable, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui' import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; - -import { FieldDataCardProps } from '../../../index_based/components/field_data_card'; +import type { FieldDataRowProps } from '../../types/field_data_row'; import { kibanaFieldFormat } from '../../../../formatters/kibana_field_format'; import { numberAsOrdinal } from '../../../../formatters/number_as_ordinal'; import { MetricDistributionChart, MetricDistributionChartData, buildChartDataFromStats, -} from '../../../index_based/components/field_data_card/metric_distribution_chart'; -import { TopValues } from '../../../index_based/components/field_data_card/top_values'; +} from '../metric_distribution_chart'; +import { TopValues } from '../../../index_based/components/field_data_row/top_values'; import { ExpandedRowFieldHeader } from '../expanded_row_field_header'; +import { DocumentStatsTable } from './document_stats'; const METRIC_DISTRIBUTION_CHART_WIDTH = 325; const METRIC_DISTRIBUTION_CHART_HEIGHT = 200; @@ -30,8 +30,8 @@ interface SummaryTableItem { value: number | string | undefined | null; } -export const NumberContent: FC = ({ config }) => { - const { stats, fieldFormat } = config; +export const NumberContent: FC = ({ config }) => { + const { stats } = config; useEffect(() => { const chartData = buildChartDataFromStats(stats, METRIC_DISTRIBUTION_CHART_WIDTH); @@ -43,6 +43,7 @@ export const NumberContent: FC = ({ config }) => { if (stats === undefined) return null; const { min, median, max, distribution } = stats; + const fieldFormat = 'fieldFormat' in config ? config.fieldFormat : undefined; const summaryTableItems = [ { @@ -96,8 +97,9 @@ export const NumberContent: FC = ({ config }) => { } ); return ( - - + + + {summaryTableTitle} className={'mlDataVisualizerSummaryTable'} @@ -105,8 +107,10 @@ export const NumberContent: FC = ({ config }) => { items={summaryTableItems} columns={summaryTableColumns} tableCaption={summaryTableTitle} + data-test-subj={'mlNumberSummaryTable'} /> + {stats && ( diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/other_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/other_content.tsx new file mode 100644 index 000000000000..a6d7398990cd --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/other_content.tsx @@ -0,0 +1,22 @@ +/* + * 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, { FC } from 'react'; +import { EuiFlexGroup } from '@elastic/eui'; +import type { FieldDataRowProps } from '../../types/field_data_row'; +import { ExamplesList } from '../../../index_based/components/field_data_row/examples_list'; +import { DocumentStatsTable } from './document_stats'; + +export const OtherContent: FC = ({ config }) => { + const { stats } = config; + if (stats === undefined) return null; + return ( + + + {Array.isArray(stats.examples) && } + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/text_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/text_content.tsx new file mode 100644 index 000000000000..55639ecc5761 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_expanded_row/text_content.tsx @@ -0,0 +1,64 @@ +/* + * 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, { FC, Fragment } from 'react'; +import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; + +import type { FieldDataRowProps } from '../../types/field_data_row'; +import { ExamplesList } from '../../../index_based/components/field_data_row/examples_list'; + +export const TextContent: FC = ({ config }) => { + const { stats } = config; + if (stats === undefined) return null; + + const { examples } = stats; + if (examples === undefined) return null; + + const numExamples = examples.length; + + return ( + + + {numExamples > 0 && } + {numExamples === 0 && ( + + + + _source, + }} + /> + + + + copy_to, + sourceParam: _source, + includesParam: includes, + excludesParam: excludes, + }} + /> + + + )} + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/boolean_content_preview.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/boolean_content_preview.tsx new file mode 100644 index 000000000000..e1a8a2f0dbeb --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/boolean_content_preview.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, { FC, useMemo } from 'react'; +import { EuiDataGridColumn } from '@elastic/eui'; +import { FieldDataRowProps } from '../../types'; +import { getTFPercentage } from '../../utils'; +import { ColumnChart } from '../../../../components/data_grid/column_chart'; +import { OrdinalChartData } from '../../../../components/data_grid/use_column_chart'; + +export const BooleanContentPreview: FC = ({ config }) => { + const chartData = useMemo(() => { + const results = getTFPercentage(config); + if (results) { + const data = [ + { key: 'true', key_as_string: 'true', doc_count: results.trueCount }, + { key: 'false', key_as_string: 'false', doc_count: results.falseCount }, + ]; + return { id: config.fieldName, cardinality: 2, data, type: 'boolean' } as OrdinalChartData; + } + }, [config]); + if (!chartData || config.fieldName === undefined) return null; + + const columnType: EuiDataGridColumn = { + id: config.fieldName, + schema: undefined, + }; + const dataTestSubj = `mlDataGridChart-${config.fieldName}`; + + return ( + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_row/distinct_values.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/distinct_values.tsx similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_row/distinct_values.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/distinct_values.tsx diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_row/document_stats.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/document_stats.tsx similarity index 86% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_row/document_stats.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/document_stats.tsx index 9421b7d9f51e..9c89d74fa751 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_row/document_stats.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/document_stats.tsx @@ -7,10 +7,10 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from '@elastic/eui'; import React from 'react'; -import { FieldDataCardProps } from '../../../index_based/components/field_data_card'; +import type { FieldDataRowProps } from '../../types/field_data_row'; import { roundToDecimalPlace } from '../../../../formatters/round_to_decimal_place'; -export const DocumentStat = ({ config }: FieldDataCardProps) => { +export const DocumentStat = ({ config }: FieldDataRowProps) => { const { stats } = config; if (stats === undefined) return null; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/index.ts new file mode 100644 index 000000000000..2f1a958e657f --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/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 { BooleanContentPreview } from './boolean_content_preview'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_row/number_content_preview.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/number_content_preview.tsx similarity index 91% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_row/number_content_preview.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/number_content_preview.tsx index 13070aeac6a4..3a84ae644cb4 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_row/number_content_preview.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/number_content_preview.tsx @@ -7,18 +7,22 @@ import React, { FC, useEffect, useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import classNames from 'classnames'; -import { FieldDataCardProps } from '../../../index_based/components/field_data_card'; import { MetricDistributionChart, MetricDistributionChartData, buildChartDataFromStats, -} from '../../../index_based/components/field_data_card/metric_distribution_chart'; +} from '../metric_distribution_chart'; import { formatSingleValue } from '../../../../formatters/format_value'; +import { FieldVisConfig } from '../../types'; const METRIC_DISTRIBUTION_CHART_WIDTH = 150; const METRIC_DISTRIBUTION_CHART_HEIGHT = 80; -export const NumberContentPreview: FC = ({ config }) => { +export interface NumberContentPreviewProps { + config: FieldVisConfig; +} + +export const IndexBasedNumberContentPreview: FC = ({ config }) => { const { stats, fieldFormat, fieldName } = config; const defaultChartData: MetricDistributionChartData[] = []; const [distributionChartData, setDistributionChartData] = useState(defaultChartData); diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_row/top_values_preview.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/top_values_preview.tsx similarity index 89% rename from x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_row/top_values_preview.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/top_values_preview.tsx index 52607ee71f25..3ae9147e475b 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_datagrid/components/field_data_row/top_values_preview.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/top_values_preview.tsx @@ -6,12 +6,12 @@ import React, { FC } from 'react'; import { EuiDataGridColumn } from '@elastic/eui'; -import { FieldDataCardProps } from '../../../index_based/components/field_data_card'; +import type { FieldDataRowProps } from '../../types/field_data_row'; import { ColumnChart } from '../../../../components/data_grid/column_chart'; import { ChartData } from '../../../../components/data_grid'; import { OrdinalDataItem } from '../../../../components/data_grid/use_column_chart'; -export const TopValuesPreview: FC = ({ config }) => { +export const TopValuesPreview: FC = ({ config }) => { const { stats } = config; if (stats === undefined) return null; const { topValues, cardinality } = stats; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/metric_distribution_chart/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/metric_distribution_chart/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/metric_distribution_chart/index.ts rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/metric_distribution_chart/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/metric_distribution_chart/metric_distribution_chart.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/metric_distribution_chart/metric_distribution_chart.tsx similarity index 60% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/metric_distribution_chart/metric_distribution_chart.tsx rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/metric_distribution_chart/metric_distribution_chart.tsx index 1abc49743807..786ebd9866cc 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/metric_distribution_chart/metric_distribution_chart.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/metric_distribution_chart/metric_distribution_chart.tsx @@ -19,13 +19,10 @@ import { TooltipValueFormatter, } from '@elastic/charts'; -import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; -import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; - import { MetricDistributionChartTooltipHeader } from './metric_distribution_chart_tooltip_header'; -import { useUiSettings } from '../../../../../contexts/kibana/use_ui_settings_context'; -import { kibanaFieldFormat } from '../../../../../formatters/kibana_field_format'; -import type { ChartTooltipValue } from '../../../../../components/chart_tooltip/chart_tooltip_service'; +import { kibanaFieldFormat } from '../../../../formatters/kibana_field_format'; +import type { ChartTooltipValue } from '../../../../components/chart_tooltip/chart_tooltip_service'; +import { useDataVizChartTheme } from '../../hooks'; export interface MetricDistributionChartData { x: number; @@ -59,9 +56,7 @@ export const MetricDistributionChart: FC = ({ defaultMessage: 'distribution', }); - const IS_DARK_THEME = useUiSettings().get('theme:darkMode'); - const themeName = IS_DARK_THEME ? darkTheme : lightTheme; - const AREA_SERIES_COLOR = themeName.euiColorVis0; + const theme = useDataVizChartTheme(); const headerFormatter: TooltipValueFormatter = (tooltipData: ChartTooltipValue) => { const xValue = tooltipData.value; @@ -81,47 +76,7 @@ export const MetricDistributionChart: FC = ({ return (
- + ) => void; -} +const FIELD_NAME = 'fieldName'; export type ItemIdToExpandedRowMap = Record; -function getItemIdToExpandedRowMap( - itemIds: string[], - items: FieldVisConfig[] -): ItemIdToExpandedRowMap { - return itemIds.reduce((m: ItemIdToExpandedRowMap, fieldName: string) => { - const item = items.find((fieldVisConfig) => fieldVisConfig[FIELD_NAME] === fieldName); - if (item !== undefined) { - m[fieldName] = ; - } - return m; - }, {} as ItemIdToExpandedRowMap); +type DataVisualizerTableItem = FieldVisConfig | FileBasedFieldVisConfig; +interface DataVisualizerTableProps { + items: T[]; + pageState: DataVisualizerIndexBasedAppState | DataVisualizerFileBasedAppState; + updatePageState: ( + update: Partial + ) => void; + getItemIdToExpandedRowMap: (itemIds: string[], items: T[]) => ItemIdToExpandedRowMap; } -export const DataVisualizerDataGrid = ({ +export const DataVisualizerTable = ({ items, pageState, updatePageState, -}: DataVisualizerDataGrid) => { + getItemIdToExpandedRowMap, +}: DataVisualizerTableProps) => { const [expandedRowItemIds, setExpandedRowItemIds] = useState([]); const [expandAll, toggleExpandAll] = useState(false); - const { onTableChange, pagination, sorting } = useTableSettings( + const { onTableChange, pagination, sorting } = useTableSettings( items, pageState, updatePageState ); - const showDistributions: boolean = pageState.showDistributions ?? true; + const showDistributions: boolean = + ('showDistributions' in pageState && pageState.showDistributions) ?? true; const toggleShowDistribution = () => { updatePageState({ ...pageState, @@ -73,7 +76,7 @@ export const DataVisualizerDataGrid = ({ }); }; - function toggleDetails(item: FieldVisConfig) { + function toggleDetails(item: DataVisualizerTableItem) { if (item.fieldName === undefined) return; const index = expandedRowItemIds.indexOf(item.fieldName); if (index !== -1) { @@ -87,7 +90,7 @@ export const DataVisualizerDataGrid = ({ } const columns = useMemo(() => { - const expanderColumn: EuiTableComputedColumnType = { + const expanderColumn: EuiTableComputedColumnType = { name: ( { + render: (item: DataVisualizerTableItem) => { if (item.fieldName === undefined) return null; const direction = expandedRowItemIds.includes(item.fieldName) ? 'arrowUp' : 'arrowDown'; return ( @@ -167,8 +170,10 @@ export const DataVisualizerDataGrid = ({ name: i18n.translate('xpack.ml.datavisualizer.dataGrid.documentsCountColumnName', { defaultMessage: 'Documents (%)', }), - render: (value: number | undefined, item: FieldVisConfig) => , - sortable: (item: FieldVisConfig) => item?.stats?.count, + render: (value: number | undefined, item: DataVisualizerTableItem) => ( + + ), + sortable: (item: DataVisualizerTableItem) => item?.stats?.count, align: LEFT_ALIGNMENT as HorizontalAlignment, 'data-test-subj': 'mlDataVisualizerTableColumnDocumentsCount', }, @@ -203,15 +208,27 @@ export const DataVisualizerDataGrid = ({ />
), - render: (item: FieldVisConfig) => { + render: (item: DataVisualizerTableItem) => { if (item === undefined || showDistributions === false) return null; - if (item.type === 'keyword' && item.stats?.topValues !== undefined) { + if ( + (item.type === ML_JOB_FIELD_TYPES.KEYWORD || item.type === ML_JOB_FIELD_TYPES.IP) && + item.stats?.topValues !== undefined + ) { return ; } - if (item.type === 'number' && item.stats?.distribution !== undefined) { - return ; + if (item.type === ML_JOB_FIELD_TYPES.NUMBER) { + if (isIndexBasedFieldVisConfig(item) && item.stats?.distribution !== undefined) { + return ; + } else { + return ; + } } + + if (item.type === ML_JOB_FIELD_TYPES.BOOLEAN) { + return ; + } + return null; }, align: LEFT_ALIGNMENT as HorizontalAlignment, @@ -230,7 +247,7 @@ export const DataVisualizerDataGrid = ({ return ( - + className={'mlDataVisualizer'} items={items} itemId={FIELD_NAME} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/hooks/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/hooks/index.ts new file mode 100644 index 000000000000..787bd71fce48 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/hooks/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 { useDataVizChartTheme } from './use_data_viz_chart_theme'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/hooks/use_data_viz_chart_theme.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/hooks/use_data_viz_chart_theme.ts new file mode 100644 index 000000000000..14e83da0546a --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/hooks/use_data_viz_chart_theme.ts @@ -0,0 +1,54 @@ +/* + * 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 type { PartialTheme } from '@elastic/charts'; +import { useMemo } from 'react'; +import { useCurrentEuiTheme } from '../../../components/color_range_legend'; +export const useDataVizChartTheme = (): PartialTheme => { + const { euiTheme } = useCurrentEuiTheme(); + const chartTheme = useMemo(() => { + const AREA_SERIES_COLOR = euiTheme.euiColorVis0; + return { + axes: { + tickLabel: { + fontSize: parseInt(euiTheme.euiFontSizeXS, 10), + fontFamily: euiTheme.euiFontFamily, + fontStyle: 'italic', + }, + }, + background: { color: 'transparent' }, + chartMargins: { + left: 0, + right: 0, + top: 0, + bottom: 0, + }, + chartPaddings: { + left: 0, + right: 0, + top: 4, + bottom: 0, + }, + scales: { barsPadding: 0.1 }, + colors: { + vizColors: [AREA_SERIES_COLOR], + }, + areaSeriesStyle: { + line: { + strokeWidth: 1, + visible: true, + }, + point: { + visible: false, + radius: 0, + opacity: 0, + }, + area: { visible: true, opacity: 1 }, + }, + }; + }, [euiTheme]); + return chartTheme; +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/index.ts new file mode 100644 index 000000000000..f903113a39ca --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/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 { DataVisualizerTable, ItemIdToExpandedRowMap } from './data_visualizer_stats_table'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/types/field_data_row.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/types/field_data_row.ts new file mode 100644 index 000000000000..4f52534d9d75 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/types/field_data_row.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 type { FieldVisConfig, FileBasedFieldVisConfig } from './field_vis_config'; + +export interface FieldDataRowProps { + config: FieldVisConfig | FileBasedFieldVisConfig; +} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/index_based/common/field_vis_config.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/types/field_vis_config.ts similarity index 69% rename from x-pack/plugins/ml/public/application/datavisualizer/index_based/common/field_vis_config.ts rename to x-pack/plugins/ml/public/application/datavisualizer/stats_table/types/field_vis_config.ts index 478310774279..a35765dd9b15 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/index_based/common/field_vis_config.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/types/field_vis_config.ts @@ -50,7 +50,7 @@ export interface FieldVisStats { max?: number; median?: number; min?: number; - topValues?: Array<{ key: number; doc_count: number }>; + topValues?: Array<{ key: number | string; doc_count: number }>; topValuesSampleSize?: number; topValuesSamplerShardSize?: number; examples?: Array; @@ -70,3 +70,28 @@ export interface FieldVisConfig { fieldFormat?: any; isUnsupportedType?: boolean; } + +export interface FileBasedFieldVisConfig { + type: MlJobFieldType; + fieldName?: string; + stats?: FieldVisStats; + format?: string; +} + +export interface FileBasedUnknownFieldVisConfig { + fieldName: string; + type: 'text' | 'unknown'; + stats: { mean: number; count: number; sampleCount: number; cardinality: number }; +} + +export function isFileBasedFieldVisConfig( + field: FieldVisConfig | FileBasedFieldVisConfig +): field is FileBasedFieldVisConfig { + return !field.hasOwnProperty('existsInDocs'); +} + +export function isIndexBasedFieldVisConfig( + field: FieldVisConfig | FileBasedFieldVisConfig +): field is FieldVisConfig { + return field.hasOwnProperty('existsInDocs'); +} diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/types/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/types/index.ts new file mode 100644 index 000000000000..439d4f037ca1 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/types/index.ts @@ -0,0 +1,15 @@ +/* + * 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 { FieldDataRowProps } from './field_data_row'; +export { + FieldVisConfig, + FileBasedFieldVisConfig, + FieldVisStats, + MetricFieldVisStats, + isFileBasedFieldVisConfig, + isIndexBasedFieldVisConfig, +} from './field_vis_config'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/utils.ts b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/utils.ts new file mode 100644 index 000000000000..ead30b9498a6 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/utils.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 { FileBasedFieldVisConfig } from './types'; + +export const getTFPercentage = (config: FileBasedFieldVisConfig) => { + const { stats } = config; + if (stats === undefined) return null; + const { count } = stats; + // use stats from index based config + let { trueCount, falseCount } = stats; + + // use stats from file based find structure results + if (stats.trueCount === undefined || stats.falseCount === undefined) { + if (config?.stats?.topValues) { + config.stats.topValues.forEach((doc) => { + if (doc.doc_count !== undefined) { + if (doc.key.toString().toLowerCase() === 'false') { + falseCount = doc.doc_count; + } + if (doc.key.toString().toLowerCase() === 'true') { + trueCount = doc.doc_count; + } + } + }); + } + } + if (count === undefined || trueCount === undefined || falseCount === undefined) return null; + return { + count, + trueCount, + falseCount, + }; +}; diff --git a/x-pack/plugins/ml/public/application/formatters/round_to_decimal_place.ts b/x-pack/plugins/ml/public/application/formatters/round_to_decimal_place.ts index 5a030d7619e9..88a82da5ed9d 100644 --- a/x-pack/plugins/ml/public/application/formatters/round_to_decimal_place.ts +++ b/x-pack/plugins/ml/public/application/formatters/round_to_decimal_place.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -export function roundToDecimalPlace(num: number, dp: number = 2): number | string { +export function roundToDecimalPlace(num?: number, dp: number = 2): number | string { + if (num === undefined) return ''; if (num % 1 === 0) { // no decimal place return num; diff --git a/x-pack/plugins/ml/server/saved_objects/service.ts b/x-pack/plugins/ml/server/saved_objects/service.ts index e4833b42b6a2..9de82269429a 100644 --- a/x-pack/plugins/ml/server/saved_objects/service.ts +++ b/x-pack/plugins/ml/server/saved_objects/service.ts @@ -131,8 +131,10 @@ export function jobSavedObjectServiceFactory( type: jobType, }); + // * space cannot be used in a delete call, so use undefined which + // is the same as specifying the default space await internalSavedObjectsClient.delete(ML_SAVED_OBJECT_TYPE, id, { - namespace, + namespace: namespace === '*' ? undefined : namespace, force: true, }); } diff --git a/x-pack/plugins/monitoring/common/types/es.ts b/x-pack/plugins/monitoring/common/types/es.ts index 725ff214ae79..728cd3d73a34 100644 --- a/x-pack/plugins/monitoring/common/types/es.ts +++ b/x-pack/plugins/monitoring/common/types/es.ts @@ -4,6 +4,31 @@ * you may not use this file except in compliance with the Elastic License. */ +export interface ElasticsearchResponse { + hits?: { + hits: ElasticsearchResponseHit[]; + total: { + value: number; + }; + }; + aggregations?: any; +} + +export interface ElasticsearchResponseHit { + _index: string; + _source: ElasticsearchSource; + inner_hits?: { + [field: string]: { + hits?: { + hits: ElasticsearchResponseHit[]; + total: { + value: number; + }; + }; + }; + }; +} + export interface ElasticsearchSourceKibanaStats { timestamp?: string; kibana?: { @@ -34,9 +59,94 @@ export interface ElasticsearchSourceLogstashPipelineVertex { }; } -export interface ElasticsearchSource { +export interface ElasticsearchNodeStats { + indices?: { + docs?: { + count?: number; + }; + store?: { + size_in_bytes?: number; + size?: { + bytes?: number; + }; + }; + }; + fs?: { + total?: { + available_in_bytes?: number; + total_in_bytes?: number; + }; + summary?: { + available?: { + bytes?: number; + }; + total?: { + bytes?: number; + }; + }; + }; + jvm?: { + mem?: { + heap_used_percent?: number; + heap?: { + used?: { + pct?: number; + }; + }; + }; + }; +} + +export interface ElasticsearchLegacySource { timestamp: string; + cluster_uuid: string; + cluster_stats?: { + nodes?: { + count?: { + total?: number; + }; + jvm?: { + max_uptime_in_millis?: number; + mem?: { + heap_used_in_bytes?: number; + heap_max_in_bytes?: number; + }; + }; + versions?: string[]; + }; + indices?: { + count?: number; + docs?: { + count?: number; + }; + shards?: { + total?: number; + }; + store?: { + size_in_bytes?: number; + }; + }; + }; + cluster_state?: { + status?: string; + nodes?: { + [nodeUuid: string]: {}; + }; + master_node?: boolean; + }; + source_node?: { + id?: string; + uuid?: string; + attributes?: {}; + transport_address?: string; + name?: string; + type?: string; + }; kibana_stats?: ElasticsearchSourceKibanaStats; + license?: { + status?: string; + type?: string; + }; logstash_state?: { pipeline?: { representation?: { @@ -108,4 +218,98 @@ export interface ElasticsearchSource { }; }; }; + stack_stats?: { + xpack?: { + ccr?: { + enabled?: boolean; + available?: boolean; + }; + }; + }; + job_stats?: { + job_id?: number; + state?: string; + data_counts?: { + processed_record_count?: number; + }; + model_size_stats?: { + model_bytes?: number; + }; + forecasts_stats?: { + total?: number; + }; + node?: { + id?: number; + name?: string; + }; + }; + index_stats?: { + index?: string; + primaries?: { + docs?: { + count?: number; + }; + store?: { + size_in_bytes?: number; + }; + indexing?: { + index_total?: number; + }; + }; + total?: { + store?: { + size_in_bytes?: number; + }; + search?: { + query_total?: number; + }; + }; + }; + node_stats?: ElasticsearchNodeStats; + service?: { + address?: string; + }; + shard?: { + index?: string; + shard?: string; + primary?: boolean; + relocating_node?: string; + node?: string; + }; + ccr_stats?: { + leader_index?: string; + follower_index?: string; + shard_id?: number; + read_exceptions?: Array<{ + exception?: { + type?: string; + }; + }>; + time_since_last_read_millis?: number; + }; + index_recovery?: { + shards?: ElasticsearchIndexRecoveryShard[]; + }; +} + +export interface ElasticsearchIndexRecoveryShard { + start_time_in_millis: number; + stop_time_in_millis: number; +} + +export interface ElasticsearchMetricbeatNode { + stats?: ElasticsearchNodeStats; +} + +export interface ElasticsearchMetricbeatSource { + elasticsearch?: { + node?: ElasticsearchLegacySource['source_node'] & ElasticsearchMetricbeatNode; + }; +} + +export type ElasticsearchSource = ElasticsearchLegacySource & ElasticsearchMetricbeatSource; + +export interface ElasticsearchModifiedSource extends ElasticsearchSource { + ccs?: string; + isSupported?: boolean; } diff --git a/x-pack/plugins/monitoring/kibana.json b/x-pack/plugins/monitoring/kibana.json index 501b84dd8825..d7784465d451 100644 --- a/x-pack/plugins/monitoring/kibana.json +++ b/x-pack/plugins/monitoring/kibana.json @@ -19,7 +19,6 @@ "triggersActionsUi", "alerts", "actions", - "encryptedSavedObjects", "encryptedSavedObjects" ], "server": true, diff --git a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx index 6d7751d91b76..e656c0ab253e 100644 --- a/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/ccr_read_exceptions_alert/index.tsx @@ -37,7 +37,7 @@ export function createCCRReadExceptionsAlertType(): AlertTypeModel ( diff --git a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx index d2cec006b1b1..9b207457683f 100644 --- a/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/cpu_usage_alert/cpu_usage_alert.tsx @@ -16,7 +16,7 @@ export function createCpuUsageAlertType(): AlertTypeModel ( diff --git a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx index bea399ee89f6..aeb9bab2aae9 100644 --- a/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/disk_usage_alert/index.tsx @@ -18,7 +18,7 @@ export function createDiskUsageAlertType(): AlertTypeModel ( diff --git a/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx b/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx index d50e9c3a5c28..4a3532ad6124 100644 --- a/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/legacy_alert/legacy_alert.tsx @@ -18,7 +18,7 @@ export function createLegacyAlertTypes(): AlertTypeModel[] { description: LEGACY_ALERT_DETAILS[legacyAlert].description, iconClass: 'bell', documentationUrl(docLinks) { - return `${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/cluster-alerts.html`; + return `${docLinks.links.monitoring.alertsCluster}`; }, alertParamsExpression: () => ( diff --git a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx index 0428e4e7c733..b484cd9a975f 100644 --- a/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx +++ b/x-pack/plugins/monitoring/public/alerts/memory_usage_alert/index.tsx @@ -18,7 +18,7 @@ export function createMemoryUsageAlertType(): AlertTypeModel ( diff --git a/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx index fdb89033c4e2..18a4990eeaaa 100644 --- a/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx +++ b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx @@ -16,7 +16,7 @@ export function createMissingMonitoringDataAlertType(): AlertTypeModel { description: ALERT_DETAILS[ALERT_MISSING_MONITORING_DATA].description, iconClass: 'bell', documentationUrl(docLinks) { - return `${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/kibana-alerts.html#kibana-alerts-missing-monitoring-data`; + return `${docLinks.links.monitoring.alertsKibanaMissingData}`; }, alertParamsExpression: (props: any) => ( ( <> diff --git a/x-pack/plugins/monitoring/server/lib/apm/_get_time_of_last_event.ts b/x-pack/plugins/monitoring/server/lib/apm/_get_time_of_last_event.ts index fc103959381b..68f16cf23b47 100644 --- a/x-pack/plugins/monitoring/server/lib/apm/_get_time_of_last_event.ts +++ b/x-pack/plugins/monitoring/server/lib/apm/_get_time_of_last_event.ts @@ -8,7 +8,8 @@ import { createApmQuery } from './create_apm_query'; // @ts-ignore import { ApmClusterMetric } from '../metrics'; -import { LegacyRequest, ElasticsearchResponse } from '../../types'; +import { LegacyRequest } from '../../types'; +import { ElasticsearchResponse } from '../../../common/types/es'; export async function getTimeOfLastEvent({ req, @@ -58,5 +59,5 @@ export async function getTimeOfLastEvent({ }; const response = await callWithRequest(req, 'search', params); - return response.hits?.hits.length ? response.hits?.hits[0]._source.timestamp : undefined; + return response.hits?.hits.length ? response.hits?.hits[0]?._source.timestamp : undefined; } diff --git a/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.ts b/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.ts index 7d471d528595..7bc36d559ac3 100644 --- a/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.ts +++ b/x-pack/plugins/monitoring/server/lib/apm/get_apm_info.ts @@ -14,7 +14,8 @@ import { getDiffCalculation } from '../beats/_beats_stats'; // @ts-ignore import { ApmMetric } from '../metrics'; import { getTimeOfLastEvent } from './_get_time_of_last_event'; -import { LegacyRequest, ElasticsearchResponse } from '../../types'; +import { LegacyRequest } from '../../types'; +import { ElasticsearchResponse } from '../../../common/types/es'; export function handleResponse(response: ElasticsearchResponse, apmUuid: string) { if (!response.hits || response.hits.hits.length === 0) { diff --git a/x-pack/plugins/monitoring/server/lib/apm/get_apms.ts b/x-pack/plugins/monitoring/server/lib/apm/get_apms.ts index 7677677ea5e7..4dbd32c88976 100644 --- a/x-pack/plugins/monitoring/server/lib/apm/get_apms.ts +++ b/x-pack/plugins/monitoring/server/lib/apm/get_apms.ts @@ -14,7 +14,8 @@ import { createApmQuery } from './create_apm_query'; import { calculateRate } from '../calculate_rate'; // @ts-ignore import { getDiffCalculation } from './_apm_stats'; -import { LegacyRequest, ElasticsearchResponse, ElasticsearchResponseHit } from '../../types'; +import { LegacyRequest } from '../../types'; +import { ElasticsearchResponse, ElasticsearchResponseHit } from '../../../common/types/es'; export function handleResponse(response: ElasticsearchResponse, start: number, end: number) { const initial = { ids: new Set(), beats: [] }; diff --git a/x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.ts b/x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.ts index 80b5efda4047..0bfc4b85c966 100644 --- a/x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.ts +++ b/x-pack/plugins/monitoring/server/lib/beats/get_beat_summary.ts @@ -5,7 +5,8 @@ */ import { upperFirst } from 'lodash'; -import { LegacyRequest, ElasticsearchResponse } from '../../types'; +import { LegacyRequest } from '../../types'; +import { ElasticsearchResponse } from '../../../common/types/es'; // @ts-ignore import { checkParam } from '../error_missing_required'; // @ts-ignore diff --git a/x-pack/plugins/monitoring/server/lib/beats/get_beats.ts b/x-pack/plugins/monitoring/server/lib/beats/get_beats.ts index aa5ef81a8de3..cd474f77d42c 100644 --- a/x-pack/plugins/monitoring/server/lib/beats/get_beats.ts +++ b/x-pack/plugins/monitoring/server/lib/beats/get_beats.ts @@ -14,7 +14,8 @@ import { createBeatsQuery } from './create_beats_query'; import { calculateRate } from '../calculate_rate'; // @ts-ignore import { getDiffCalculation } from './_beats_stats'; -import { ElasticsearchResponse, LegacyRequest } from '../../types'; +import { LegacyRequest } from '../../types'; +import { ElasticsearchResponse } from '../../../common/types/es'; interface Beat { uuid: string | undefined; diff --git a/x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.js b/x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.ts similarity index 79% rename from x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.js rename to x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.ts index a1674b2f5eb3..248d1604ee20 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/flag_supported_clusters.ts @@ -4,17 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { set } from '@elastic/safer-lodash-set'; -import { get, find } from 'lodash'; +// @ts-ignore import { checkParam } from '../error_missing_required'; import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../common/constants'; +import { ElasticsearchResponse, ElasticsearchModifiedSource } from '../../../common/types/es'; +import { LegacyRequest } from '../../types'; async function findSupportedBasicLicenseCluster( - req, - clusters, - kbnIndexPattern, - kibanaUuid, - serverLog + req: LegacyRequest, + clusters: ElasticsearchModifiedSource[], + kbnIndexPattern: string, + kibanaUuid: string, + serverLog: (message: string) => void ) { checkParam(kbnIndexPattern, 'kbnIndexPattern in cluster/findSupportedBasicLicenseCluster'); @@ -25,7 +26,7 @@ async function findSupportedBasicLicenseCluster( const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring'); const gte = req.payload.timeRange.min; const lte = req.payload.timeRange.max; - const kibanaDataResult = await callWithRequest(req, 'search', { + const kibanaDataResult: ElasticsearchResponse = (await callWithRequest(req, 'search', { index: kbnIndexPattern, size: 1, ignoreUnavailable: true, @@ -42,11 +43,13 @@ async function findSupportedBasicLicenseCluster( }, }, }, - }); - const supportedClusterUuid = get(kibanaDataResult, 'hits.hits[0]._source.cluster_uuid'); - const supportedCluster = find(clusters, { cluster_uuid: supportedClusterUuid }); - // only this basic cluster is supported - set(supportedCluster, 'isSupported', true); + })) as ElasticsearchResponse; + const supportedClusterUuid = kibanaDataResult.hits?.hits[0]?._source.cluster_uuid ?? undefined; + for (const cluster of clusters) { + if (cluster.cluster_uuid === supportedClusterUuid) { + cluster.isSupported = true; + } + } serverLog( `Found basic license admin cluster UUID for Monitoring UI support: ${supportedClusterUuid}.` @@ -69,12 +72,12 @@ async function findSupportedBasicLicenseCluster( * Non-Basic license clusters and any cluster in a single-cluster environment * are also flagged as supported in this method. */ -export function flagSupportedClusters(req, kbnIndexPattern) { +export function flagSupportedClusters(req: LegacyRequest, kbnIndexPattern: string) { checkParam(kbnIndexPattern, 'kbnIndexPattern in cluster/flagSupportedClusters'); const config = req.server.config(); - const serverLog = (msg) => req.getLogger('supported-clusters').debug(msg); - const flagAllSupported = (clusters) => { + const serverLog = (message: string) => req.getLogger('supported-clusters').debug(message); + const flagAllSupported = (clusters: ElasticsearchModifiedSource[]) => { clusters.forEach((cluster) => { if (cluster.license) { cluster.isSupported = true; @@ -83,7 +86,7 @@ export function flagSupportedClusters(req, kbnIndexPattern) { return clusters; }; - return async function (clusters) { + return async function (clusters: ElasticsearchModifiedSource[]) { // Standalone clusters are automatically supported in the UI so ignore those for // our calculations here let linkedClusterCount = 0; @@ -110,7 +113,7 @@ export function flagSupportedClusters(req, kbnIndexPattern) { // if all linked are basic licenses if (linkedClusterCount === basicLicenseCount) { - const kibanaUuid = config.get('server.uuid'); + const kibanaUuid = config.get('server.uuid') as string; return await findSupportedBasicLicenseCluster( req, clusters, diff --git a/x-pack/plugins/monitoring/server/lib/cluster/get_cluster_license.js b/x-pack/plugins/monitoring/server/lib/cluster/get_cluster_license.ts similarity index 70% rename from x-pack/plugins/monitoring/server/lib/cluster/get_cluster_license.js rename to x-pack/plugins/monitoring/server/lib/cluster/get_cluster_license.ts index bd84fbb66f96..9f3106f7c04a 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/get_cluster_license.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_cluster_license.ts @@ -4,12 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get } from 'lodash'; +// @ts-ignore import { checkParam } from '../error_missing_required'; +// @ts-ignore import { createQuery } from '../create_query'; +// @ts-ignore import { ElasticsearchMetric } from '../metrics'; +import { ElasticsearchResponse } from '../../../common/types/es'; +import { LegacyRequest } from '../../types'; -export function getClusterLicense(req, esIndexPattern, clusterUuid) { +export function getClusterLicense(req: LegacyRequest, esIndexPattern: string, clusterUuid: string) { checkParam(esIndexPattern, 'esIndexPattern in getClusterLicense'); const params = { @@ -28,7 +32,7 @@ export function getClusterLicense(req, esIndexPattern, clusterUuid) { }; const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring'); - return callWithRequest(req, 'search', params).then((response) => { - return get(response, 'hits.hits[0]._source.license', {}); + return callWithRequest(req, 'search', params).then((response: ElasticsearchResponse) => { + return response.hits?.hits[0]?._source.license; }); } diff --git a/x-pack/plugins/monitoring/server/lib/cluster/get_cluster_status.js b/x-pack/plugins/monitoring/server/lib/cluster/get_cluster_status.ts similarity index 53% rename from x-pack/plugins/monitoring/server/lib/cluster/get_cluster_status.js rename to x-pack/plugins/monitoring/server/lib/cluster/get_cluster_status.ts index cef06bb473c3..3184893d6c63 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/get_cluster_status.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_cluster_status.ts @@ -3,20 +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 { get } from 'lodash'; +import { ElasticsearchSource } from '../../../common/types/es'; /* * @param cluster {Object} clusterStats from getClusterStatus * @param unassignedShards {Object} shardStats from getShardStats * @return top-level cluster summary data */ -export function getClusterStatus(cluster, shardStats) { - const clusterStats = get(cluster, 'cluster_stats', {}); - const clusterNodes = get(clusterStats, 'nodes', {}); - const clusterIndices = get(clusterStats, 'indices', {}); +export function getClusterStatus(cluster: ElasticsearchSource, shardStats: unknown) { + const clusterStats = cluster.cluster_stats ?? {}; + const clusterNodes = clusterStats.nodes ?? {}; + const clusterIndices = clusterStats.indices ?? {}; - const clusterTotalShards = get(clusterIndices, 'shards.total', 0); + const clusterTotalShards = clusterIndices.shards?.total ?? 0; let unassignedShardsTotal = 0; const unassignedShards = get(shardStats, 'indicesTotals.unassigned'); if (unassignedShards !== undefined) { @@ -26,17 +26,17 @@ export function getClusterStatus(cluster, shardStats) { const totalShards = clusterTotalShards + unassignedShardsTotal; return { - status: get(cluster, 'cluster_state.status', 'unknown'), + status: cluster.cluster_state?.status ?? 'unknown', // index-based stats - indicesCount: get(clusterIndices, 'count', 0), - documentCount: get(clusterIndices, 'docs.count', 0), - dataSize: get(clusterIndices, 'store.size_in_bytes', 0), + indicesCount: clusterIndices.count ?? 0, + documentCount: clusterIndices.docs?.count ?? 0, + dataSize: clusterIndices.store?.size_in_bytes ?? 0, // node-based stats - nodesCount: get(clusterNodes, 'count.total', 0), - upTime: get(clusterNodes, 'jvm.max_uptime_in_millis', 0), - version: get(clusterNodes, 'versions', null), - memUsed: get(clusterNodes, 'jvm.mem.heap_used_in_bytes', 0), - memMax: get(clusterNodes, 'jvm.mem.heap_max_in_bytes', 0), + nodesCount: clusterNodes.count?.total ?? 0, + upTime: clusterNodes.jvm?.max_uptime_in_millis ?? 0, + version: clusterNodes.versions ?? null, + memUsed: clusterNodes.jvm?.mem?.heap_used_in_bytes ?? 0, + memMax: clusterNodes.jvm?.mem?.heap_max_in_bytes ?? 0, unassignedShards: unassignedShardsTotal, totalShards, }; diff --git a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_state.js b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_state.ts similarity index 82% rename from x-pack/plugins/monitoring/server/lib/cluster/get_clusters_state.js rename to x-pack/plugins/monitoring/server/lib/cluster/get_clusters_state.ts index fa5526728086..c752f218f962 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_state.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_state.ts @@ -4,8 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get, find } from 'lodash'; +import { find } from 'lodash'; +// @ts-ignore import { checkParam } from '../error_missing_required'; +import { ElasticsearchResponse, ElasticsearchModifiedSource } from '../../../common/types/es'; +import { LegacyRequest } from '../../types'; /** * Augment the {@clusters} with their cluster state's from the {@code response}. @@ -15,11 +18,14 @@ import { checkParam } from '../error_missing_required'; * @param {Array} clusters Array of clusters to be augmented * @return {Array} Always {@code clusters}. */ -export function handleResponse(response, clusters) { - const hits = get(response, 'hits.hits', []); +export function handleResponse( + response: ElasticsearchResponse, + clusters: ElasticsearchModifiedSource[] +) { + const hits = response.hits?.hits ?? []; hits.forEach((hit) => { - const currentCluster = get(hit, '_source', {}); + const currentCluster = hit._source; if (currentCluster) { const cluster = find(clusters, { cluster_uuid: currentCluster.cluster_uuid }); @@ -39,7 +45,11 @@ export function handleResponse(response, clusters) { * * If there is no cluster state available for any cluster, then it will be returned without any cluster state information. */ -export function getClustersState(req, esIndexPattern, clusters) { +export function getClustersState( + req: LegacyRequest, + esIndexPattern: string, + clusters: ElasticsearchModifiedSource[] +) { checkParam(esIndexPattern, 'esIndexPattern in cluster/getClustersHealth'); const clusterUuids = clusters diff --git a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_stats.js b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_stats.ts similarity index 83% rename from x-pack/plugins/monitoring/server/lib/cluster/get_clusters_stats.js rename to x-pack/plugins/monitoring/server/lib/cluster/get_clusters_stats.ts index 8ddd33837f56..609c8fb2089d 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_stats.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_stats.ts @@ -4,12 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get } from 'lodash'; +// @ts-ignore import { checkParam } from '../error_missing_required'; +// @ts-ignore import { createQuery } from '../create_query'; +// @ts-ignore import { ElasticsearchMetric } from '../metrics'; +// @ts-ignore import { parseCrossClusterPrefix } from '../ccs_utils'; import { getClustersState } from './get_clusters_state'; +import { ElasticsearchResponse, ElasticsearchModifiedSource } from '../../../common/types/es'; +import { LegacyRequest } from '../../types'; /** * This will fetch the cluster stats and cluster state as a single object per cluster. @@ -19,10 +24,10 @@ import { getClustersState } from './get_clusters_state'; * @param {String} clusterUuid (optional) If not undefined, getClusters will filter for a single cluster * @return {Promise} A promise containing an array of clusters. */ -export function getClustersStats(req, esIndexPattern, clusterUuid) { +export function getClustersStats(req: LegacyRequest, esIndexPattern: string, clusterUuid: string) { return ( fetchClusterStats(req, esIndexPattern, clusterUuid) - .then((response) => handleClusterStats(response, req.server)) + .then((response) => handleClusterStats(response)) // augment older documents (e.g., from 2.x - 5.4) with their cluster_state .then((clusters) => getClustersState(req, esIndexPattern, clusters)) ); @@ -36,7 +41,7 @@ export function getClustersStats(req, esIndexPattern, clusterUuid) { * @param {String} clusterUuid (optional) - if not undefined, getClusters filters for a single clusterUuid * @return {Promise} Object representing each cluster. */ -function fetchClusterStats(req, esIndexPattern, clusterUuid) { +function fetchClusterStats(req: LegacyRequest, esIndexPattern: string, clusterUuid: string) { checkParam(esIndexPattern, 'esIndexPattern in getClusters'); const config = req.server.config(); @@ -81,15 +86,15 @@ function fetchClusterStats(req, esIndexPattern, clusterUuid) { * @param {Object} response The response from Elasticsearch. * @return {Array} Objects representing each cluster. */ -export function handleClusterStats(response) { - const hits = get(response, 'hits.hits', []); +export function handleClusterStats(response: ElasticsearchResponse) { + const hits = response?.hits?.hits ?? []; return hits .map((hit) => { - const cluster = get(hit, '_source'); + const cluster = hit._source as ElasticsearchModifiedSource; if (cluster) { - const indexName = get(hit, '_index', ''); + const indexName = hit._index; const ccs = parseCrossClusterPrefix(indexName); // use CCS whenever we come across it so that we can avoid talking to other monitoring clusters whenever possible diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/ccr.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/ccr.ts similarity index 72% rename from x-pack/plugins/monitoring/server/lib/elasticsearch/ccr.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch/ccr.ts index 0f0ba49f229b..ec7ccd5ddb9a 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/ccr.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/ccr.ts @@ -4,19 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get } from 'lodash'; import moment from 'moment'; +// @ts-ignore import { checkParam } from '../error_missing_required'; +// @ts-ignore import { ElasticsearchMetric } from '../metrics'; +// @ts-ignore import { createQuery } from '../create_query'; +import { ElasticsearchResponse } from '../../../common/types/es'; +import { LegacyRequest } from '../../types'; -export function handleResponse(response) { - const isEnabled = get(response, 'hits.hits[0]._source.stack_stats.xpack.ccr.enabled'); - const isAvailable = get(response, 'hits.hits[0]._source.stack_stats.xpack.ccr.available'); +export function handleResponse(response: ElasticsearchResponse) { + const isEnabled = response.hits?.hits[0]?._source.stack_stats?.xpack?.ccr?.enabled ?? undefined; + const isAvailable = + response.hits?.hits[0]?._source.stack_stats?.xpack?.ccr?.available ?? undefined; return isEnabled && isAvailable; } -export async function checkCcrEnabled(req, esIndexPattern) { +export async function checkCcrEnabled(req: LegacyRequest, esIndexPattern: string) { checkParam(esIndexPattern, 'esIndexPattern in getNodes'); const start = moment.utc(req.payload.timeRange.min).valueOf(); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.ts similarity index 81% rename from x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.ts index 00e750b17d57..31a58651ecd3 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.ts @@ -5,9 +5,14 @@ */ import moment from 'moment'; import _ from 'lodash'; +// @ts-ignore import { checkParam } from '../error_missing_required'; +// @ts-ignore import { createQuery } from '../create_query'; +// @ts-ignore import { ElasticsearchMetric } from '../metrics'; +import { ElasticsearchResponse, ElasticsearchIndexRecoveryShard } from '../../../common/types/es'; +import { LegacyRequest } from '../../types'; /** * Filter out shard activity that we do not care about. @@ -20,8 +25,8 @@ import { ElasticsearchMetric } from '../metrics'; * @param {Number} startMs Start time in milliseconds of the polling window * @returns {boolean} true to keep */ -export function filterOldShardActivity(startMs) { - return (activity) => { +export function filterOldShardActivity(startMs: number) { + return (activity: ElasticsearchIndexRecoveryShard) => { // either it's still going and there is no stop time, or the stop time happened after we started looking for one return !_.isNumber(activity.stop_time_in_millis) || activity.stop_time_in_millis >= startMs; }; @@ -35,9 +40,9 @@ export function filterOldShardActivity(startMs) { * @param {Date} start The start time from the request payload (expected to be of type {@code Date}) * @returns {Object[]} An array of shards representing active shard activity from {@code _source.index_recovery.shards}. */ -export function handleLastRecoveries(resp, start) { - if (resp.hits.hits.length === 1) { - const data = _.get(resp.hits.hits[0], '_source.index_recovery.shards', []).filter( +export function handleLastRecoveries(resp: ElasticsearchResponse, start: number) { + if (resp.hits?.hits.length === 1) { + const data = (resp.hits?.hits[0]?._source.index_recovery?.shards ?? []).filter( filterOldShardActivity(moment.utc(start).valueOf()) ); data.sort((a, b) => b.start_time_in_millis - a.start_time_in_millis); @@ -47,7 +52,7 @@ export function handleLastRecoveries(resp, start) { return []; } -export function getLastRecovery(req, esIndexPattern) { +export function getLastRecovery(req: LegacyRequest, esIndexPattern: string) { checkParam(esIndexPattern, 'esIndexPattern in elasticsearch/getLastRecovery'); const start = req.payload.timeRange.min; diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.ts similarity index 79% rename from x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.ts index 71f3633406c9..29f5a38ca3a2 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_ml_jobs.ts @@ -4,22 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import Bluebird from 'bluebird'; -import { includes, get } from 'lodash'; +import { includes } from 'lodash'; +// @ts-ignore import { checkParam } from '../error_missing_required'; +// @ts-ignore import { createQuery } from '../create_query'; +// @ts-ignore import { ElasticsearchMetric } from '../metrics'; import { ML_SUPPORTED_LICENSES } from '../../../common/constants'; +import { ElasticsearchResponse, ElasticsearchSource } from '../../../common/types/es'; +import { LegacyRequest } from '../../types'; /* * Get a listing of jobs along with some metric data to use for the listing */ -export function handleResponse(response) { - const hits = get(response, 'hits.hits', []); - return hits.map((hit) => get(hit, '_source.job_stats')); +export function handleResponse(response: ElasticsearchResponse) { + const hits = response.hits?.hits; + return hits?.map((hit) => hit._source.job_stats) ?? []; } -export function getMlJobs(req, esIndexPattern) { +export function getMlJobs(req: LegacyRequest, esIndexPattern: string) { checkParam(esIndexPattern, 'esIndexPattern in getMlJobs'); const config = req.server.config(); @@ -56,8 +60,12 @@ export function getMlJobs(req, esIndexPattern) { * cardinality isn't guaranteed to be accurate is the issue * but it will be as long as the precision threshold is >= the actual value */ -export function getMlJobsForCluster(req, esIndexPattern, cluster) { - const license = get(cluster, 'license', {}); +export function getMlJobsForCluster( + req: LegacyRequest, + esIndexPattern: string, + cluster: ElasticsearchSource +) { + const license = cluster.license ?? {}; if (license.status === 'active' && includes(ML_SUPPORTED_LICENSES, license.type)) { // ML is supported @@ -80,11 +88,11 @@ export function getMlJobsForCluster(req, esIndexPattern, cluster) { const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring'); - return callWithRequest(req, 'search', params).then((response) => { - return get(response, 'aggregations.jobs_count.value', 0); + return callWithRequest(req, 'search', params).then((response: ElasticsearchResponse) => { + return response.aggregations.jobs_count.value ?? 0; }); } // ML is not supported - return Bluebird.resolve(null); + return Promise.resolve(null); } diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_index_summary.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_index_summary.ts similarity index 73% rename from x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_index_summary.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_index_summary.ts index 6a0935f2b2d6..3257c5ac3608 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_index_summary.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_index_summary.ts @@ -5,22 +5,27 @@ */ import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; +// @ts-ignore import { checkParam } from '../../error_missing_required'; +// @ts-ignore import { createQuery } from '../../create_query'; +// @ts-ignore import { ElasticsearchMetric } from '../../metrics'; -import { i18n } from '@kbn/i18n'; +import { ElasticsearchResponse } from '../../../../common/types/es'; +import { LegacyRequest } from '../../../types'; -export function handleResponse(shardStats, indexUuid) { - return (response) => { - const indexStats = get(response, 'hits.hits[0]._source.index_stats'); - const primaries = get(indexStats, 'primaries'); - const total = get(indexStats, 'total'); +export function handleResponse(shardStats: any, indexUuid: string) { + return (response: ElasticsearchResponse) => { + const indexStats = response.hits?.hits[0]?._source.index_stats; + const primaries = indexStats?.primaries; + const total = indexStats?.total; const stats = { - documents: get(primaries, 'docs.count'), + documents: primaries?.docs?.count, dataSize: { - primaries: get(primaries, 'store.size_in_bytes'), - total: get(total, 'store.size_in_bytes'), + primaries: primaries?.store?.size_in_bytes, + total: total?.store?.size_in_bytes, }, }; @@ -55,10 +60,15 @@ export function handleResponse(shardStats, indexUuid) { } export function getIndexSummary( - req, - esIndexPattern, - shardStats, - { clusterUuid, indexUuid, start, end } + req: LegacyRequest, + esIndexPattern: string, + shardStats: any, + { + clusterUuid, + indexUuid, + start, + end, + }: { clusterUuid: string; indexUuid: string; start: number; end: number } ) { checkParam(esIndexPattern, 'esIndexPattern in elasticsearch/getIndexSummary'); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_indices.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_indices.ts similarity index 72% rename from x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_indices.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_indices.ts index efea687ef803..bf19fcf8978e 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_indices.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/indices/get_indices.ts @@ -5,43 +5,54 @@ */ import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; +// @ts-ignore import { checkParam } from '../../error_missing_required'; +// @ts-ignore import { ElasticsearchMetric } from '../../metrics'; +// @ts-ignore import { createQuery } from '../../create_query'; +// @ts-ignore import { calculateRate } from '../../calculate_rate'; +// @ts-ignore import { getUnassignedShards } from '../shards'; -import { i18n } from '@kbn/i18n'; - -export function handleResponse(resp, min, max, shardStats) { +import { ElasticsearchResponse } from '../../../../common/types/es'; +import { LegacyRequest } from '../../../types'; + +export function handleResponse( + resp: ElasticsearchResponse, + min: number, + max: number, + shardStats: any +) { // map the hits - const hits = get(resp, 'hits.hits', []); + const hits = resp?.hits?.hits ?? []; return hits.map((hit) => { - const stats = get(hit, '_source.index_stats'); - const earliestStats = get(hit, 'inner_hits.earliest.hits.hits[0]._source.index_stats'); + const stats = hit._source.index_stats; + const earliestStats = hit.inner_hits?.earliest?.hits?.hits[0]?._source.index_stats; const rateOptions = { - hitTimestamp: get(hit, '_source.timestamp'), - earliestHitTimestamp: get(hit, 'inner_hits.earliest.hits.hits[0]._source.timestamp'), + hitTimestamp: hit._source.timestamp, + earliestHitTimestamp: hit.inner_hits?.earliest?.hits?.hits[0]?._source.timestamp, timeWindowMin: min, timeWindowMax: max, }; - const earliestIndexingHit = get(earliestStats, 'primaries.indexing'); + const earliestIndexingHit = earliestStats?.primaries?.indexing; const { rate: indexRate } = calculateRate({ - latestTotal: get(stats, 'primaries.indexing.index_total'), - earliestTotal: get(earliestIndexingHit, 'index_total'), + latestTotal: stats?.primaries?.indexing?.index_total, + earliestTotal: earliestIndexingHit?.index_total, ...rateOptions, }); - const earliestSearchHit = get(earliestStats, 'total.search'); + const earliestSearchHit = earliestStats?.total?.search; const { rate: searchRate } = calculateRate({ - latestTotal: get(stats, 'total.search.query_total'), - earliestTotal: get(earliestSearchHit, 'query_total'), + latestTotal: stats?.total?.search?.query_total, + earliestTotal: earliestSearchHit?.query_total, ...rateOptions, }); - const shardStatsForIndex = get(shardStats, ['indices', stats.index]); - + const shardStatsForIndex = get(shardStats, ['indices', stats?.index ?? '']); let status; let statusSort; let unassignedShards; @@ -65,10 +76,10 @@ export function handleResponse(resp, min, max, shardStats) { } return { - name: stats.index, + name: stats?.index, status, - doc_count: get(stats, 'primaries.docs.count'), - data_size: get(stats, 'total.store.size_in_bytes'), + doc_count: stats?.primaries?.docs?.count, + data_size: stats?.total?.store?.size_in_bytes, index_rate: indexRate, search_rate: searchRate, unassigned_shards: unassignedShards, @@ -78,9 +89,14 @@ export function handleResponse(resp, min, max, shardStats) { } export function buildGetIndicesQuery( - esIndexPattern, - clusterUuid, - { start, end, size, showSystemIndices = false } + esIndexPattern: string, + clusterUuid: string, + { + start, + end, + size, + showSystemIndices = false, + }: { start: number; end: number; size: number; showSystemIndices: boolean } ) { const filters = []; if (!showSystemIndices) { @@ -134,7 +150,12 @@ export function buildGetIndicesQuery( }; } -export function getIndices(req, esIndexPattern, showSystemIndices = false, shardStats) { +export function getIndices( + req: LegacyRequest, + esIndexPattern: string, + showSystemIndices: boolean = false, + shardStats: any +) { checkParam(esIndexPattern, 'esIndexPattern in elasticsearch/getIndices'); const { min: start, max: end } = req.payload.timeRange; @@ -145,7 +166,7 @@ export function getIndices(req, esIndexPattern, showSystemIndices = false, shard start, end, showSystemIndices, - size: config.get('monitoring.ui.max_bucket_size'), + size: parseInt(config.get('monitoring.ui.max_bucket_size') || '', 10), }); const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring'); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_node_summary.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_node_summary.ts similarity index 59% rename from x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_node_summary.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_node_summary.ts index 06f5d5488a1a..bf7471d77b61 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_node_summary.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_node_summary.ts @@ -5,35 +5,49 @@ */ import { get } from 'lodash'; +import { i18n } from '@kbn/i18n'; +// @ts-ignore import { checkParam } from '../../error_missing_required'; +// @ts-ignore import { createQuery } from '../../create_query'; +// @ts-ignore import { ElasticsearchMetric } from '../../metrics'; +// @ts-ignore import { getDefaultNodeFromId } from './get_default_node_from_id'; +// @ts-ignore import { calculateNodeType } from './calculate_node_type'; +// @ts-ignore import { getNodeTypeClassLabel } from './get_node_type_class_label'; -import { i18n } from '@kbn/i18n'; +import { + ElasticsearchSource, + ElasticsearchResponse, + ElasticsearchLegacySource, + ElasticsearchMetricbeatNode, +} from '../../../../common/types/es'; +import { LegacyRequest } from '../../../types'; -export function handleResponse(clusterState, shardStats, nodeUuid) { - return (response) => { +export function handleResponse( + clusterState: ElasticsearchSource['cluster_state'], + shardStats: any, + nodeUuid: string +) { + return (response: ElasticsearchResponse) => { let nodeSummary = {}; - const nodeStatsHits = get(response, 'hits.hits', []); - const nodes = nodeStatsHits.map((hit) => - get(hit, '_source.elasticsearch.node', hit._source.source_node) - ); // using [0] value because query results are sorted desc per timestamp + const nodeStatsHits = response.hits?.hits ?? []; + const nodes: Array< + ElasticsearchLegacySource['source_node'] | ElasticsearchMetricbeatNode + > = nodeStatsHits.map((hit) => hit._source.elasticsearch?.node || hit._source.source_node); // using [0] value because query results are sorted desc per timestamp const node = nodes[0] || getDefaultNodeFromId(nodeUuid); const sourceStats = - get(response, 'hits.hits[0]._source.elasticsearch.node.stats') || - get(response, 'hits.hits[0]._source.node_stats'); - const clusterNode = get(clusterState, ['nodes', nodeUuid]); + response.hits?.hits[0]?._source.elasticsearch?.node?.stats || + response.hits?.hits[0]?._source.node_stats; + const clusterNode = + clusterState && clusterState.nodes ? clusterState.nodes[nodeUuid] : undefined; const stats = { resolver: nodeUuid, - node_ids: nodes.map((node) => node.id || node.uuid), + node_ids: nodes.map((_node) => node.id || node.uuid), attributes: node.attributes, - transport_address: get( - response, - 'hits.hits[0]._source.service.address', - node.transport_address - ), + transport_address: response.hits?.hits[0]?._source.service?.address || node.transport_address, name: node.name, type: node.type, }; @@ -48,22 +62,19 @@ export function handleResponse(clusterState, shardStats, nodeUuid) { nodeSummary = { type: nodeType, - nodeTypeLabel: nodeTypeLabel, - nodeTypeClass: nodeTypeClass, + nodeTypeLabel, + nodeTypeClass, totalShards: _shardStats.shardCount, indexCount: _shardStats.indexCount, - documents: get(sourceStats, 'indices.docs.count'), + documents: sourceStats?.indices?.docs?.count, dataSize: - get(sourceStats, 'indices.store.size_in_bytes') || - get(sourceStats, 'indices.store.size.bytes'), + sourceStats?.indices?.store?.size_in_bytes || sourceStats?.indices?.store?.size?.bytes, freeSpace: - get(sourceStats, 'fs.total.available_in_bytes') || - get(sourceStats, 'fs.summary.available.bytes'), + sourceStats?.fs?.total?.available_in_bytes || sourceStats?.fs?.summary?.available?.bytes, totalSpace: - get(sourceStats, 'fs.total.total_in_bytes') || get(sourceStats, 'fs.summary.total.bytes'), + sourceStats?.fs?.total?.total_in_bytes || sourceStats?.fs?.summary?.total?.bytes, usedHeap: - get(sourceStats, 'jvm.mem.heap_used_percent') || - get(sourceStats, 'jvm.mem.heap.used.pct'), + sourceStats?.jvm?.mem?.heap_used_percent || sourceStats?.jvm?.mem?.heap?.used?.pct, status: i18n.translate('xpack.monitoring.es.nodes.onlineStatusLabel', { defaultMessage: 'Online', }), @@ -89,11 +100,16 @@ export function handleResponse(clusterState, shardStats, nodeUuid) { } export function getNodeSummary( - req, - esIndexPattern, - clusterState, - shardStats, - { clusterUuid, nodeUuid, start, end } + req: LegacyRequest, + esIndexPattern: string, + clusterState: ElasticsearchSource['cluster_state'], + shardStats: any, + { + clusterUuid, + nodeUuid, + start, + end, + }: { clusterUuid: string; nodeUuid: string; start: number; end: number } ) { checkParam(esIndexPattern, 'esIndexPattern in elasticsearch/getNodeSummary'); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/get_nodes.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/get_nodes.ts similarity index 87% rename from x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/get_nodes.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/get_nodes.ts index ac4fcea6150a..1e412d2303cc 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/get_nodes.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/get_nodes.ts @@ -5,13 +5,21 @@ */ import moment from 'moment'; +// @ts-ignore import { checkParam } from '../../../error_missing_required'; +// @ts-ignore import { createQuery } from '../../../create_query'; +// @ts-ignore import { calculateAuto } from '../../../calculate_auto'; +// @ts-ignore import { ElasticsearchMetric } from '../../../metrics'; +// @ts-ignore import { getMetricAggs } from './get_metric_aggs'; import { handleResponse } from './handle_response'; +// @ts-ignore import { LISTING_METRICS_NAMES, LISTING_METRICS_PATHS } from './nodes_listing_metrics'; +import { LegacyRequest } from '../../../../types'; +import { ElasticsearchModifiedSource } from '../../../../../common/types/es'; /* Run an aggregation on node_stats to get stat data for the selected time * range for all the active nodes. Every option is a key to a configuration @@ -30,7 +38,13 @@ import { LISTING_METRICS_NAMES, LISTING_METRICS_PATHS } from './nodes_listing_me * @param {Object} nodesShardCount: per-node information about shards * @return {Array} node info combined with metrics for each node from handle_response */ -export async function getNodes(req, esIndexPattern, pageOfNodes, clusterStats, nodesShardCount) { +export async function getNodes( + req: LegacyRequest, + esIndexPattern: string, + pageOfNodes: Array<{ uuid: string }>, + clusterStats: ElasticsearchModifiedSource, + nodesShardCount: { nodes: { [nodeId: string]: { shardCount: number } } } +) { checkParam(esIndexPattern, 'esIndexPattern in getNodes'); const start = moment.utc(req.payload.timeRange.min).valueOf(); @@ -45,7 +59,7 @@ export async function getNodes(req, esIndexPattern, pageOfNodes, clusterStats, n const min = start; const bucketSize = Math.max( - config.get('monitoring.ui.min_interval_seconds'), + parseInt(config.get('monitoring.ui.min_interval_seconds') as string, 10), calculateAuto(100, duration).asSeconds() ); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/handle_response.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/handle_response.ts similarity index 57% rename from x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/handle_response.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/handle_response.ts index 3f82e8ec3e64..3e248a06318d 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/handle_response.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/handle_response.ts @@ -6,8 +6,11 @@ import { get } from 'lodash'; import { mapNodesInfo } from './map_nodes_info'; +// @ts-ignore import { mapNodesMetrics } from './map_nodes_metrics'; +// @ts-ignore import { uncovertMetricNames } from '../../convert_metric_names'; +import { ElasticsearchResponse, ElasticsearchModifiedSource } from '../../../../../common/types/es'; /* * Process the response from the get_nodes query @@ -18,18 +21,18 @@ import { uncovertMetricNames } from '../../convert_metric_names'; * @return {Array} node info combined with metrics for each node */ export function handleResponse( - response, - clusterStats, - nodesShardCount, - pageOfNodes, + response: ElasticsearchResponse, + clusterStats: ElasticsearchModifiedSource | undefined, + nodesShardCount: { nodes: { [nodeId: string]: { shardCount: number } } } | undefined, + pageOfNodes: Array<{ uuid: string }>, timeOptions = {} ) { if (!get(response, 'hits.hits')) { return []; } - const nodeHits = get(response, 'hits.hits', []); - const nodesInfo = mapNodesInfo(nodeHits, clusterStats, nodesShardCount); + const nodeHits = response.hits?.hits ?? []; + const nodesInfo: { [key: string]: any } = mapNodesInfo(nodeHits, clusterStats, nodesShardCount); /* * Every node bucket is an object with a field for nodeId and fields for @@ -37,19 +40,29 @@ export function handleResponse( * with a sub-object for all the metrics buckets */ const nodeBuckets = get(response, 'aggregations.nodes.buckets', []); - const metricsForNodes = nodeBuckets.reduce((accum, { key: nodeId, by_date: byDate }) => { - return { - ...accum, - [nodeId]: uncovertMetricNames(byDate), - }; - }, {}); - const nodesMetrics = mapNodesMetrics(metricsForNodes, nodesInfo, timeOptions); // summarize the metrics of online nodes + const metricsForNodes = nodeBuckets.reduce( + ( + accum: { [nodeId: string]: any }, + { key: nodeId, by_date: byDate }: { key: string; by_date: any } + ) => { + return { + ...accum, + [nodeId]: uncovertMetricNames(byDate), + }; + }, + {} + ); + const nodesMetrics: { [key: string]: any } = mapNodesMetrics( + metricsForNodes, + nodesInfo, + timeOptions + ); // summarize the metrics of online nodes // nodesInfo is the source of truth for the nodeIds, where nodesMetrics will lack metrics for offline nodes const nodes = pageOfNodes.map((node) => ({ ...node, - ...nodesInfo[node.uuid], - ...nodesMetrics[node.uuid], + ...(nodesInfo && nodesInfo[node.uuid] ? nodesInfo[node.uuid] : {}), + ...(nodesMetrics && nodesMetrics[node.uuid] ? nodesMetrics[node.uuid] : {}), resolver: node.uuid, })); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/map_nodes_info.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/map_nodes_info.js deleted file mode 100644 index 317c1cddf57a..000000000000 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/map_nodes_info.js +++ /dev/null @@ -1,46 +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 { get, isUndefined } from 'lodash'; -import { calculateNodeType, getNodeTypeClassLabel } from '../'; - -/** - * @param {Array} nodeHits: info about each node from the hits in the get_nodes query - * @param {Object} clusterStats: cluster stats from cluster state document - * @param {Object} nodesShardCount: per-node information about shards - * @return {Object} summarized info about each node keyed by nodeId - */ -export function mapNodesInfo(nodeHits, clusterStats, nodesShardCount) { - const clusterState = get(clusterStats, 'cluster_state', { nodes: {} }); - - return nodeHits.reduce((prev, node) => { - const sourceNode = get(node, '_source.source_node') || get(node, '_source.elasticsearch.node'); - - const calculatedNodeType = calculateNodeType(sourceNode, get(clusterState, 'master_node')); - const { nodeType, nodeTypeLabel, nodeTypeClass } = getNodeTypeClassLabel( - sourceNode, - calculatedNodeType - ); - const isOnline = !isUndefined(get(clusterState, ['nodes', sourceNode.uuid || sourceNode.id])); - - return { - ...prev, - [sourceNode.uuid || sourceNode.id]: { - name: sourceNode.name, - transport_address: sourceNode.transport_address, - type: nodeType, - isOnline, - nodeTypeLabel: nodeTypeLabel, - nodeTypeClass: nodeTypeClass, - shardCount: get( - nodesShardCount, - `nodes[${sourceNode.uuid || sourceNode.id}].shardCount`, - 0 - ), - }, - }; - }, {}); -} diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/map_nodes_info.ts b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/map_nodes_info.ts new file mode 100644 index 000000000000..a2e80c9ca1d9 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/nodes/get_nodes/map_nodes_info.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 { isUndefined } from 'lodash'; +// @ts-ignore +import { calculateNodeType } from '../calculate_node_type'; +// @ts-ignore +import { getNodeTypeClassLabel } from '../get_node_type_class_label'; +import { + ElasticsearchResponseHit, + ElasticsearchModifiedSource, +} from '../../../../../common/types/es'; + +/** + * @param {Array} nodeHits: info about each node from the hits in the get_nodes query + * @param {Object} clusterStats: cluster stats from cluster state document + * @param {Object} nodesShardCount: per-node information about shards + * @return {Object} summarized info about each node keyed by nodeId + */ +export function mapNodesInfo( + nodeHits: ElasticsearchResponseHit[], + clusterStats?: ElasticsearchModifiedSource, + nodesShardCount?: { nodes: { [nodeId: string]: { shardCount: number } } } +) { + const clusterState = clusterStats?.cluster_state ?? { nodes: {} }; + + return nodeHits.reduce((prev, node) => { + const sourceNode = node._source.source_node || node._source.elasticsearch?.node; + + const calculatedNodeType = calculateNodeType(sourceNode, clusterState.master_node); + const { nodeType, nodeTypeLabel, nodeTypeClass } = getNodeTypeClassLabel( + sourceNode, + calculatedNodeType + ); + const uuid = sourceNode?.uuid ?? sourceNode?.id ?? undefined; + if (!uuid) { + return prev; + } + const isOnline = !isUndefined(clusterState.nodes ? clusterState.nodes[uuid] : undefined); + + return { + ...prev, + [uuid]: { + name: sourceNode?.name, + transport_address: sourceNode?.transport_address, + type: nodeType, + isOnline, + nodeTypeLabel, + nodeTypeClass, + shardCount: nodesShardCount?.nodes[uuid]?.shardCount ?? 0, + }, + }; + }, {}); +} diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_allocation.js b/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_allocation.ts similarity index 75% rename from x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_allocation.js rename to x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_allocation.ts index 40412c03b0ef..ed37b56b7ad0 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_allocation.js +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/shards/get_shard_allocation.ts @@ -4,22 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get } from 'lodash'; +// @ts-ignore import { checkParam } from '../../error_missing_required'; +// @ts-ignore import { createQuery } from '../../create_query'; +// @ts-ignore import { ElasticsearchMetric } from '../../metrics'; +import { ElasticsearchResponse, ElasticsearchLegacySource } from '../../../../common/types/es'; +import { LegacyRequest } from '../../../types'; -export function handleResponse(response) { - const hits = get(response, 'hits.hits'); +export function handleResponse(response: ElasticsearchResponse) { + const hits = response.hits?.hits; if (!hits) { return []; } // deduplicate any shards from earlier days with the same cluster state state_uuid - const uniqueShards = new Set(); + const uniqueShards = new Set(); // map into object with shard and source properties - return hits.reduce((shards, hit) => { + return hits.reduce((shards: Array, hit) => { const shard = hit._source.shard; if (shard) { @@ -37,9 +41,13 @@ export function handleResponse(response) { } export function getShardAllocation( - req, - esIndexPattern, - { shardFilter, stateUuid, showSystemIndices = false } + req: LegacyRequest, + esIndexPattern: string, + { + shardFilter, + stateUuid, + showSystemIndices = false, + }: { shardFilter: any; stateUuid: string; showSystemIndices: boolean } ) { checkParam(esIndexPattern, 'esIndexPattern in elasticsearch/getShardAllocation'); diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/verify_alerting_security.ts b/x-pack/plugins/monitoring/server/lib/elasticsearch/verify_alerting_security.ts index c8aa730dd477..aff7c4edb517 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/verify_alerting_security.ts +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/verify_alerting_security.ts @@ -43,7 +43,7 @@ export class AlertingSecurity { return { isSufficientlySecure: !isSecurityEnabled || (isSecurityEnabled && isTLSEnabled), - hasPermanentEncryptionKey: !encryptedSavedObjects?.usingEphemeralEncryptionKey, + hasPermanentEncryptionKey: Boolean(encryptedSavedObjects), }; }; } diff --git a/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts b/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts index 0e8903908a55..4a0c598eec30 100644 --- a/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts +++ b/x-pack/plugins/monitoring/server/lib/kibana/get_kibana_info.ts @@ -9,10 +9,11 @@ import { merge } from 'lodash'; import { checkParam } from '../error_missing_required'; // @ts-ignore import { calculateAvailability } from '../calculate_availability'; -import { LegacyRequest, ElasticsearchResponse } from '../../types'; +import { LegacyRequest } from '../../types'; +import { ElasticsearchResponse } from '../../../common/types/es'; export function handleResponse(resp: ElasticsearchResponse) { - const source = resp.hits?.hits[0]._source.kibana_stats; + const source = resp.hits?.hits[0]?._source.kibana_stats; const kibana = source?.kibana; return merge(kibana, { availability: calculateAvailability(source?.timestamp), diff --git a/x-pack/plugins/monitoring/server/lib/logstash/get_node_info.ts b/x-pack/plugins/monitoring/server/lib/logstash/get_node_info.ts index ead876460778..cdf227742400 100644 --- a/x-pack/plugins/monitoring/server/lib/logstash/get_node_info.ts +++ b/x-pack/plugins/monitoring/server/lib/logstash/get_node_info.ts @@ -9,7 +9,8 @@ import { merge } from 'lodash'; import { checkParam } from '../error_missing_required'; // @ts-ignore import { calculateAvailability } from '../calculate_availability'; -import { LegacyRequest, ElasticsearchResponse } from '../../types'; +import { LegacyRequest } from '../../types'; +import { ElasticsearchResponse } from '../../../common/types/es'; export function handleResponse(resp: ElasticsearchResponse) { const source = resp.hits?.hits[0]?._source?.logstash_stats; diff --git a/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline_state_document.ts b/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline_state_document.ts index 96419ceb4cc7..1556b44f7380 100644 --- a/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline_state_document.ts +++ b/x-pack/plugins/monitoring/server/lib/logstash/get_pipeline_state_document.ts @@ -8,7 +8,8 @@ import { createQuery } from '../create_query'; // @ts-ignore import { LogstashMetric } from '../metrics'; -import { LegacyRequest, ElasticsearchResponse } from '../../types'; +import { LegacyRequest } from '../../types'; +import { ElasticsearchResponse } from '../../../common/types/es'; export async function getPipelineStateDocument( req: LegacyRequest, diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index c7b3f13dd695..a78c50722368 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -12,7 +12,6 @@ import { TypeOf } from '@kbn/config-schema'; import { Logger, PluginInitializerContext, - RequestHandlerContext, KibanaRequest, KibanaResponseFactory, CoreSetup, @@ -47,6 +46,7 @@ import { PluginsSetup, PluginsStart, LegacyRequest, + RequestHandlerContextMonitoringPlugin, } from './types'; import { Globals } from './static_globals'; @@ -90,7 +90,7 @@ export class Plugin { .pipe(first()) .toPromise(); - const router = core.http.createRouter(); + const router = core.http.createRouter(); this.legacyShimDependencies = { router, instanceUuid: this.initializerContext.env.instanceUuid, @@ -307,7 +307,7 @@ export class Plugin { route: (options: any) => { const method = options.method; const handler = async ( - context: RequestHandlerContext, + context: RequestHandlerContextMonitoringPlugin, req: KibanaRequest, res: KibanaResponseFactory ) => { diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.ts similarity index 72% rename from x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.js rename to x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.ts index 9f69ea1465c2..6f41455ebca6 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.js +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr.ts @@ -7,11 +7,15 @@ import { schema } from '@kbn/config-schema'; import moment from 'moment'; import { get, groupBy } from 'lodash'; +// @ts-ignore import { handleError } from '../../../../lib/errors/handle_error'; +// @ts-ignore import { prefixIndexPattern } from '../../../../lib/ccs_utils'; import { INDEX_PATTERN_ELASTICSEARCH } from '../../../../../common/constants'; +import { ElasticsearchResponse, ElasticsearchSource } from '../../../../../common/types/es'; +import { LegacyRequest } from '../../../../types'; -function getBucketScript(max, min) { +function getBucketScript(max: string, min: string) { return { bucket_script: { buckets_path: { @@ -23,7 +27,13 @@ function getBucketScript(max, min) { }; } -function buildRequest(req, config, esIndexPattern) { +function buildRequest( + req: LegacyRequest, + config: { + get: (key: string) => string | undefined; + }, + esIndexPattern: string +) { const min = moment.utc(req.payload.timeRange.min).valueOf(); const max = moment.utc(req.payload.timeRange.max).valueOf(); const maxBucketSize = config.get('monitoring.ui.max_bucket_size'); @@ -168,7 +178,12 @@ function buildRequest(req, config, esIndexPattern) { }; } -export function ccrRoute(server) { +export function ccrRoute(server: { + route: (p: any) => void; + config: () => { + get: (key: string) => string | undefined; + }; +}) { server.route({ method: 'POST', path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/ccr', @@ -186,14 +201,14 @@ export function ccrRoute(server) { }), }, }, - async handler(req) { + async handler(req: LegacyRequest) { const config = server.config(); const ccs = req.payload.ccs; const esIndexPattern = prefixIndexPattern(config, INDEX_PATTERN_ELASTICSEARCH, ccs); try { const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring'); - const response = await callWithRequest( + const response: ElasticsearchResponse = await callWithRequest( req, 'search', buildRequest(req, config, esIndexPattern) @@ -203,50 +218,72 @@ export function ccrRoute(server) { return { data: [] }; } - const fullStats = get(response, 'hits.hits').reduce((accum, hit) => { - const innerHits = get(hit, 'inner_hits.by_shard.hits.hits'); - const innerHitsSource = innerHits.map((innerHit) => get(innerHit, '_source.ccr_stats')); - const grouped = groupBy( - innerHitsSource, - (stat) => `${stat.follower_index}:${stat.shard_id}` - ); + const fullStats: { + [key: string]: Array>; + } = + response.hits?.hits.reduce((accum, hit) => { + const innerHits = hit.inner_hits?.by_shard.hits?.hits ?? []; + const innerHitsSource = innerHits.map( + (innerHit) => + innerHit._source.ccr_stats as NonNullable + ); + const grouped = groupBy( + innerHitsSource, + (stat) => `${stat.follower_index}:${stat.shard_id}` + ); - return { - ...accum, - ...grouped, - }; - }, {}); + return { + ...accum, + ...grouped, + }; + }, {}) ?? {}; - const buckets = get(response, 'aggregations.by_follower_index.buckets'); - const data = buckets.reduce((accum, bucket) => { + const buckets = response.aggregations.by_follower_index.buckets; + const data = buckets.reduce((accum: any, bucket: any) => { const leaderIndex = get(bucket, 'leader_index.buckets[0].key'); const remoteCluster = get( bucket, 'leader_index.buckets[0].remote_cluster.buckets[0].key' ); const follows = remoteCluster ? `${leaderIndex} on ${remoteCluster}` : leaderIndex; - const stat = { + const stat: { + [key: string]: any; + shards: Array<{ + error?: string; + opsSynced: number; + syncLagTime: number; + syncLagOps: number; + }>; + } = { id: bucket.key, index: bucket.key, follows, + shards: [], + error: undefined, + opsSynced: undefined, + syncLagTime: undefined, + syncLagOps: undefined, }; - stat.shards = get(bucket, 'by_shard_id.buckets').reduce((accum, shardBucket) => { - const fullStat = get(fullStats[`${bucket.key}:${shardBucket.key}`], '[0]', {}); - const shardStat = { - shardId: shardBucket.key, - error: fullStat.read_exceptions.length - ? fullStat.read_exceptions[0].exception.type - : null, - opsSynced: get(shardBucket, 'ops_synced.value'), - syncLagTime: fullStat.time_since_last_read_millis, - syncLagOps: get(shardBucket, 'lag_ops.value'), - syncLagOpsLeader: get(shardBucket, 'leader_lag_ops.value'), - syncLagOpsFollower: get(shardBucket, 'follower_lag_ops.value'), - }; - accum.push(shardStat); - return accum; - }, []); + stat.shards = get(bucket, 'by_shard_id.buckets').reduce( + (accum2: any, shardBucket: any) => { + const fullStat = fullStats[`${bucket.key}:${shardBucket.key}`][0] ?? {}; + const shardStat = { + shardId: shardBucket.key, + error: fullStat.read_exceptions?.length + ? fullStat.read_exceptions[0].exception?.type + : null, + opsSynced: get(shardBucket, 'ops_synced.value'), + syncLagTime: fullStat.time_since_last_read_millis, + syncLagOps: get(shardBucket, 'lag_ops.value'), + syncLagOpsLeader: get(shardBucket, 'leader_lag_ops.value'), + syncLagOpsFollower: get(shardBucket, 'follower_lag_ops.value'), + }; + accum2.push(shardStat); + return accum2; + }, + [] + ); stat.error = (stat.shards.find((shard) => shard.error) || {}).error; stat.opsSynced = stat.shards.reduce((sum, { opsSynced }) => sum + opsSynced, 0); diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr_shard.js b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr_shard.ts similarity index 82% rename from x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr_shard.js rename to x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr_shard.ts index 92458a31c6bd..fd834cff29aa 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr_shard.js +++ b/x-pack/plugins/monitoring/server/routes/api/v1/elasticsearch/ccr_shard.ts @@ -4,15 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get } from 'lodash'; import moment from 'moment'; import { schema } from '@kbn/config-schema'; +// @ts-ignore import { handleError } from '../../../../lib/errors/handle_error'; +// @ts-ignore import { prefixIndexPattern } from '../../../../lib/ccs_utils'; +// @ts-ignore import { getMetrics } from '../../../../lib/details/get_metrics'; import { INDEX_PATTERN_ELASTICSEARCH } from '../../../../../common/constants'; +import { ElasticsearchResponse } from '../../../../../common/types/es'; +import { LegacyRequest } from '../../../../types'; -function getFormattedLeaderIndex(leaderIndex) { +function getFormattedLeaderIndex(leaderIndex: string) { let leader = leaderIndex; if (leader.includes(':')) { const leaderSplit = leader.split(':'); @@ -21,7 +25,7 @@ function getFormattedLeaderIndex(leaderIndex) { return leader; } -async function getCcrStat(req, esIndexPattern, filters) { +async function getCcrStat(req: LegacyRequest, esIndexPattern: string, filters: unknown[]) { const min = moment.utc(req.payload.timeRange.min).valueOf(); const max = moment.utc(req.payload.timeRange.max).valueOf(); @@ -68,7 +72,7 @@ async function getCcrStat(req, esIndexPattern, filters) { return await callWithRequest(req, 'search', params); } -export function ccrShardRoute(server) { +export function ccrShardRoute(server: { route: (p: any) => void; config: () => {} }) { server.route({ method: 'POST', path: '/api/monitoring/v1/clusters/{clusterUuid}/elasticsearch/ccr/{index}/shard/{shardId}', @@ -88,7 +92,7 @@ export function ccrShardRoute(server) { }), }, }, - async handler(req) { + async handler(req: LegacyRequest) { const config = server.config(); const index = req.params.index; const shardId = req.params.shardId; @@ -120,7 +124,7 @@ export function ccrShardRoute(server) { ]; try { - const [metrics, ccrResponse] = await Promise.all([ + const [metrics, ccrResponse]: [unknown, ElasticsearchResponse] = await Promise.all([ getMetrics( req, esIndexPattern, @@ -133,18 +137,15 @@ export function ccrShardRoute(server) { getCcrStat(req, esIndexPattern, filters), ]); - const stat = get(ccrResponse, 'hits.hits[0]._source.ccr_stats', {}); - const oldestStat = get( - ccrResponse, - 'hits.hits[0].inner_hits.oldest.hits.hits[0]._source.ccr_stats', - {} - ); + const stat = ccrResponse.hits?.hits[0]?._source.ccr_stats ?? {}; + const oldestStat = + ccrResponse.hits?.hits[0].inner_hits?.oldest.hits?.hits[0]?._source.ccr_stats ?? {}; return { metrics, stat, - formattedLeader: getFormattedLeaderIndex(stat.leader_index), - timestamp: get(ccrResponse, 'hits.hits[0]._source.timestamp'), + formattedLeader: getFormattedLeaderIndex(stat.leader_index ?? ''), + timestamp: ccrResponse.hits?.hits[0]?._source.timestamp, oldestStat, }; } catch (err) { diff --git a/x-pack/plugins/monitoring/server/types.ts b/x-pack/plugins/monitoring/server/types.ts index 47ac3beb8c39..fbdd01e56c65 100644 --- a/x-pack/plugins/monitoring/server/types.ts +++ b/x-pack/plugins/monitoring/server/types.ts @@ -4,10 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ import { Observable } from 'rxjs'; -import { IRouter, ILegacyClusterClient, Logger, ILegacyCustomClusterClient } from 'kibana/server'; +import type { + IRouter, + ILegacyClusterClient, + Logger, + ILegacyCustomClusterClient, + RequestHandlerContext, +} from 'kibana/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { LicenseFeature, ILicense } from '../../licensing/server'; -import { PluginStartContract as ActionsPluginsStartContact } from '../../actions/server'; +import type { + PluginStartContract as ActionsPluginsStartContact, + ActionsApiRequestHandlerContext, +} from '../../actions/server'; +import type { AlertingApiRequestHandlerContext } from '../../alerts/server'; import { PluginStartContract as AlertingPluginStartContract, PluginSetupContract as AlertingPluginSetupContract, @@ -17,7 +27,6 @@ import { LicensingPluginSetup } from '../../licensing/server'; import { PluginSetupContract as FeaturesPluginSetupContract } from '../../features/server'; import { EncryptedSavedObjectsPluginSetup } from '../../encrypted_saved_objects/server'; import { CloudSetup } from '../../cloud/server'; -import { ElasticsearchSource } from '../common/types/es'; export interface MonitoringLicenseService { refresh: () => Promise; @@ -43,6 +52,11 @@ export interface PluginsSetup { cloud?: CloudSetup; } +export interface RequestHandlerContextMonitoringPlugin extends RequestHandlerContext { + actions?: ActionsApiRequestHandlerContext; + alerting?: AlertingApiRequestHandlerContext; +} + export interface PluginsStart { alerts: AlertingPluginStartContract; actions: ActionsPluginsStartContact; @@ -54,7 +68,7 @@ export interface MonitoringCoreConfig { export interface RouteDependencies { cluster: ILegacyCustomClusterClient; - router: IRouter; + router: IRouter; licenseService: MonitoringLicenseService; encryptedSavedObjects?: EncryptedSavedObjectsPluginSetup; logger: Logger; @@ -67,7 +81,7 @@ export interface MonitoringCore { } export interface LegacyShimDependencies { - router: IRouter; + router: IRouter; instanceUuid: string; esDataClient: ILegacyClusterClient; kibanaStatsCollector: any; @@ -118,26 +132,3 @@ export interface LegacyServer { }; }; } - -export interface ElasticsearchResponse { - hits?: { - hits: ElasticsearchResponseHit[]; - total: { - value: number; - }; - }; -} - -export interface ElasticsearchResponseHit { - _source: ElasticsearchSource; - inner_hits?: { - [field: string]: { - hits?: { - hits: ElasticsearchResponseHit[]; - total: { - value: number; - }; - }; - }; - }; -} diff --git a/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts b/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts index f57e1a774a8e..6fcd780d5af2 100644 --- a/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts +++ b/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts @@ -3,15 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { - CoreSetup, - PluginInitializerContext, - KibanaRequest, - RequestHandlerContext, -} from 'kibana/server'; +import { CoreSetup, PluginInitializerContext, KibanaRequest } from 'kibana/server'; import { PromiseReturnType } from '../../../typings/common'; import { createAnnotationsClient } from './create_annotations_client'; import { registerAnnotationAPIs } from './register_annotation_apis'; +import type { ObservabilityRequestHandlerContext } from '../../types'; interface Params { index: string; @@ -36,7 +32,10 @@ export async function bootstrapAnnotations({ index, core, context }: Params) { }); return { - getScopedAnnotationsClient: (requestContext: RequestHandlerContext, request: KibanaRequest) => { + getScopedAnnotationsClient: ( + requestContext: ObservabilityRequestHandlerContext, + request: KibanaRequest + ) => { return createAnnotationsClient({ index, apiCaller: requestContext.core.elasticsearch.legacy.client.callAsCurrentUser, diff --git a/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts b/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts index 5d0fdc65117b..8f0b53b5a3df 100644 --- a/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts +++ b/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts @@ -15,6 +15,7 @@ import { } from '../../../common/annotations'; import { ScopedAnnotationsClient } from './bootstrap_annotations'; import { createAnnotationsClient } from './create_annotations_client'; +import type { ObservabilityRequestHandlerContext } from '../../types'; const unknowns = schema.object({}, { unknowns: 'allow' }); @@ -30,8 +31,12 @@ export function registerAnnotationAPIs({ function wrapRouteHandler>( types: TType, handler: (params: { data: t.TypeOf; client: ScopedAnnotationsClient }) => Promise - ): RequestHandler { - return async (...args: Parameters) => { + ): RequestHandler { + return async ( + ...args: Parameters< + RequestHandler + > + ) => { const [context, request, response] = args; const rt = types; @@ -79,7 +84,7 @@ export function registerAnnotationAPIs({ }; } - const router = core.http.createRouter(); + const router = core.http.createRouter(); router.post( { diff --git a/x-pack/plugins/observability/server/types.ts b/x-pack/plugins/observability/server/types.ts new file mode 100644 index 000000000000..ee4761a94dcc --- /dev/null +++ b/x-pack/plugins/observability/server/types.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. + */ +import type { IRouter, RequestHandlerContext } from 'src/core/server'; +import type { LicensingApiRequestHandlerContext } from '../../licensing/server'; + +/** + * @internal + */ +export interface ObservabilityRequestHandlerContext extends RequestHandlerContext { + licensing: LicensingApiRequestHandlerContext; +} + +/** + * @internal + */ +export type ObservabilityPluginRouter = IRouter; diff --git a/x-pack/plugins/reporting/server/core.ts b/x-pack/plugins/reporting/server/core.ts index 2c1ac95d6e82..b227b3ba5d0d 100644 --- a/x-pack/plugins/reporting/server/core.ts +++ b/x-pack/plugins/reporting/server/core.ts @@ -10,7 +10,6 @@ import { first, map, take } from 'rxjs/operators'; import { BasePath, ElasticsearchServiceSetup, - IRouter, KibanaRequest, SavedObjectsClientContract, SavedObjectsServiceStart, @@ -27,10 +26,11 @@ import { checkLicense, getExportTypesRegistry, LevelLogger } from './lib'; import { ESQueueInstance } from './lib/create_queue'; import { screenshotsObservableFactory, ScreenshotsObservableFn } from './lib/screenshots'; import { ReportingStore } from './lib/store'; +import { ReportingPluginRouter } from './types'; export interface ReportingInternalSetup { basePath: Pick; - router: IRouter; + router: ReportingPluginRouter; features: FeaturesPluginSetup; elasticsearch: ElasticsearchServiceSetup; licensing: LicensingPluginSetup; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts index 96653b157366..24516210efc5 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/create_job.ts @@ -6,7 +6,6 @@ import { notFound, notImplemented } from '@hapi/boom'; import { get } from 'lodash'; -import { RequestHandlerContext } from 'src/core/server'; import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; import { CsvFromSavedObjectRequest } from '../../routes/generate_from_savedobject_immediate'; import { CreateJobFnFactory } from '../../types'; @@ -18,10 +17,11 @@ import { SavedObjectServiceError, VisObjectAttributesJSON, } from './types'; +import type { ReportingRequestHandlerContext } from '../../types'; export type ImmediateCreateJobFn = ( jobParams: JobParamsPanelCsv, - context: RequestHandlerContext, + context: ReportingRequestHandlerContext, req: CsvFromSavedObjectRequest ) => Promise; diff --git a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts index 5e95eec99871..f8df8c8f16a6 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_from_savedobject/execute_job.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; +import { KibanaRequest } from 'src/core/server'; import { CancellationToken } from '../../../common'; import { CONTENT_TYPE_CSV, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; import { TaskRunResult } from '../../lib/tasks'; @@ -12,6 +12,7 @@ import { RunTaskFnFactory } from '../../types'; import { createGenerateCsv } from '../csv/generate_csv'; import { getGenerateCsvParams } from './lib/get_csv_job'; import { JobPayloadPanelCsv } from './types'; +import type { ReportingRequestHandlerContext } from '../../types'; /* * ImmediateExecuteFn receives the job doc payload because the payload was @@ -20,7 +21,7 @@ import { JobPayloadPanelCsv } from './types'; export type ImmediateExecuteFn = ( jobId: null, job: JobPayloadPanelCsv, - context: RequestHandlerContext, + context: ReportingRequestHandlerContext, req: KibanaRequest ) => Promise; diff --git a/x-pack/plugins/reporting/server/lib/enqueue_job.ts b/x-pack/plugins/reporting/server/lib/enqueue_job.ts index 305247e6f863..daa662478d82 100644 --- a/x-pack/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/plugins/reporting/server/lib/enqueue_job.ts @@ -4,18 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; +import { KibanaRequest } from 'src/core/server'; import { ReportingCore } from '../'; import { durationToNumber } from '../../common/schema_utils'; import { BaseParams, ReportingUser } from '../types'; import { LevelLogger } from './'; import { Report } from './store'; +import type { ReportingRequestHandlerContext } from '../types'; export type EnqueueJobFn = ( exportTypeId: string, jobParams: BaseParams, user: ReportingUser, - context: RequestHandlerContext, + context: ReportingRequestHandlerContext, request: KibanaRequest ) => Promise; @@ -36,7 +37,7 @@ export function enqueueJobFactory( exportTypeId: string, jobParams: BaseParams, user: ReportingUser, - context: RequestHandlerContext, + context: ReportingRequestHandlerContext, request: KibanaRequest ) { const exportType = reporting.getExportTypesRegistry().getById(exportTypeId); diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index 6a93a35bfcc8..05556f050e21 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -16,15 +16,10 @@ import { registerRoutes } from './routes'; import { setFieldFormats } from './services'; import { ReportingSetup, ReportingSetupDeps, ReportingStart, ReportingStartDeps } from './types'; import { registerReportingUsageCollector } from './usage'; +import type { ReportingRequestHandlerContext } from './types'; const kbToBase64Length = (kb: number) => Math.floor((kb * 1024 * 8) / 6); -declare module 'src/core/server' { - interface RequestHandlerContext { - reporting?: ReportingStart | null; - } -} - export class ReportingPlugin implements Plugin { private readonly initializerContext: PluginInitializerContext; @@ -39,6 +34,7 @@ export class ReportingPlugin public setup(core: CoreSetup, plugins: ReportingSetupDeps) { // prevent throwing errors in route handlers about async deps not being initialized + // @ts-expect-error null is not assignable to object. use a boolean property to ensure reporting API is enabled. core.http.registerRouteHandlerContext(PLUGIN_ID, () => { if (this.reportingCore.pluginIsStarted()) { return {}; // ReportingStart contract @@ -73,7 +69,7 @@ export class ReportingPlugin const { features, licensing, security, spaces } = plugins; const { initializerContext: initContext, reportingCore } = this; - const router = http.createRouter(); + const router = http.createRouter(); const basePath = http.basePath; reportingCore.pluginSetup({ diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts b/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts index 71ca0661a42a..65519dab9a97 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/browser.test.ts @@ -12,6 +12,7 @@ import supertest from 'supertest'; import { ReportingCore } from '../..'; import { createMockLevelLogger, createMockReportingCore } from '../../test_helpers'; import { registerDiagnoseBrowser } from './browser'; +import type { ReportingRequestHandlerContext } from '../../types'; jest.mock('child_process'); jest.mock('readline'); @@ -47,7 +48,11 @@ describe('POST /diagnose/browser', () => { beforeEach(async () => { ({ server, httpSetup } = await setupServer(reportingSymbol)); - httpSetup.registerRouteHandlerContext(reportingSymbol, 'reporting', () => ({})); + httpSetup.registerRouteHandlerContext( + reportingSymbol, + 'reporting', + () => ({}) + ); const mockSetupDeps = ({ elasticsearch: { diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/config.test.ts b/x-pack/plugins/reporting/server/routes/diagnostic/config.test.ts index a112d04f38c7..ef84ee068e8a 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/config.test.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/config.test.ts @@ -10,6 +10,7 @@ import supertest from 'supertest'; import { ReportingCore } from '../..'; import { createMockReportingCore, createMockLevelLogger } from '../../test_helpers'; import { registerDiagnoseConfig } from './config'; +import type { ReportingRequestHandlerContext } from '../../types'; type SetupServerReturn = UnwrapPromise>; @@ -25,7 +26,11 @@ describe('POST /diagnose/config', () => { beforeEach(async () => { ({ server, httpSetup } = await setupServer(reportingSymbol)); - httpSetup.registerRouteHandlerContext(reportingSymbol, 'reporting', () => ({})); + httpSetup.registerRouteHandlerContext( + reportingSymbol, + 'reporting', + () => ({}) + ); mockSetupDeps = ({ elasticsearch: { diff --git a/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.test.ts b/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.test.ts index 287da0d2ed5e..37a8412e4269 100644 --- a/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.test.ts +++ b/x-pack/plugins/reporting/server/routes/diagnostic/screenshot.test.ts @@ -10,6 +10,7 @@ import supertest from 'supertest'; import { ReportingCore } from '../..'; import { createMockReportingCore, createMockLevelLogger } from '../../test_helpers'; import { registerDiagnoseScreenshot } from './screenshot'; +import type { ReportingRequestHandlerContext } from '../../types'; jest.mock('../../export_types/png/lib/generate_png'); @@ -44,7 +45,11 @@ describe('POST /diagnose/screenshot', () => { beforeEach(async () => { ({ server, httpSetup } = await setupServer(reportingSymbol)); - httpSetup.registerRouteHandlerContext(reportingSymbol, 'reporting', () => ({})); + httpSetup.registerRouteHandlerContext( + reportingSymbol, + 'reporting', + () => ({}) + ); const mockSetupDeps = ({ elasticsearch: { diff --git a/x-pack/plugins/reporting/server/routes/generation.test.ts b/x-pack/plugins/reporting/server/routes/generation.test.ts index 867af75c8de2..b904ff92be3e 100644 --- a/x-pack/plugins/reporting/server/routes/generation.test.ts +++ b/x-pack/plugins/reporting/server/routes/generation.test.ts @@ -13,6 +13,7 @@ import { ReportingCore } from '..'; import { ExportTypesRegistry } from '../lib/export_types_registry'; import { createMockReportingCore, createMockLevelLogger } from '../test_helpers'; import { registerJobGenerationRoutes } from './generation'; +import type { ReportingRequestHandlerContext } from '../types'; type SetupServerReturn = UnwrapPromise>; @@ -46,7 +47,11 @@ describe('POST /api/reporting/generate', () => { beforeEach(async () => { ({ server, httpSetup } = await setupServer(reportingSymbol)); - httpSetup.registerRouteHandlerContext(reportingSymbol, 'reporting', () => ({})); + httpSetup.registerRouteHandlerContext( + reportingSymbol, + 'reporting', + () => ({}) + ); callClusterStub = sinon.stub().resolves({}); diff --git a/x-pack/plugins/reporting/server/routes/jobs.test.ts b/x-pack/plugins/reporting/server/routes/jobs.test.ts index fc1cfd00493c..ccbec158cb5a 100644 --- a/x-pack/plugins/reporting/server/routes/jobs.test.ts +++ b/x-pack/plugins/reporting/server/routes/jobs.test.ts @@ -12,7 +12,7 @@ import { ReportingCore } from '..'; import { ReportingInternalSetup } from '../core'; import { ExportTypesRegistry } from '../lib/export_types_registry'; import { createMockConfig, createMockConfigSchema, createMockReportingCore } from '../test_helpers'; -import { ExportTypeDefinition } from '../types'; +import { ExportTypeDefinition, ReportingRequestHandlerContext } from '../types'; import { registerJobInfoRoutes } from './jobs'; type SetupServerReturn = UnwrapPromise>; @@ -35,7 +35,11 @@ describe('GET /api/reporting/jobs/download', () => { beforeEach(async () => { ({ server, httpSetup } = await setupServer(reportingSymbol)); - httpSetup.registerRouteHandlerContext(reportingSymbol, 'reporting', () => ({})); + httpSetup.registerRouteHandlerContext( + reportingSymbol, + 'reporting', + () => ({}) + ); core = await createMockReportingCore(config, ({ elasticsearch: { legacy: { client: { callAsInternalUser: jest.fn() } }, diff --git a/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.ts b/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.ts index cce002a0e693..18191a9f1b71 100644 --- a/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.ts +++ b/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, KibanaResponseFactory, RequestHandlerContext } from 'kibana/server'; +import { KibanaRequest, KibanaResponseFactory } from 'kibana/server'; import { coreMock, httpServerMock } from 'src/core/server/mocks'; import { ReportingCore } from '../../'; import { ReportingInternalSetup } from '../../core'; @@ -14,6 +14,7 @@ import { createMockReportingCore, } from '../../test_helpers'; import { authorizedUserPreRoutingFactory } from './authorized_user_pre_routing'; +import type { ReportingRequestHandlerContext } from '../../types'; let mockCore: ReportingCore; const mockConfig: any = { 'server.basePath': '/sbp', 'roles.allow': ['reporting_user'] }; @@ -23,7 +24,7 @@ const mockReportingConfig = createMockConfig(mockReportingConfigSchema); const getMockContext = () => (({ core: coreMock.createRequestHandlerContext(), - } as unknown) as RequestHandlerContext); + } as unknown) as ReportingRequestHandlerContext); const getMockRequest = () => ({ diff --git a/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts b/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts index 4d57bc154444..e8be2b1c9288 100644 --- a/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts +++ b/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts @@ -8,11 +8,17 @@ import { RequestHandler, RouteMethod } from 'src/core/server'; import { AuthenticatedUser } from '../../../../security/server'; import { ReportingCore } from '../../core'; import { getUserFactory } from './get_user'; +import type { ReportingRequestHandlerContext } from '../../types'; const superuserRole = 'superuser'; type ReportingRequestUser = AuthenticatedUser | false; -export type RequestHandlerUser = RequestHandler extends (...a: infer U) => infer R +export type RequestHandlerUser = RequestHandler< + P, + Q, + B, + ReportingRequestHandlerContext +> extends (...a: infer U) => infer R ? (user: ReportingRequestUser, ...a: U) => R : never; @@ -21,7 +27,9 @@ export const authorizedUserPreRoutingFactory = function authorizedUserPreRouting ) { const setupDeps = reporting.getPluginSetupDeps(); const getUser = getUserFactory(setupDeps.security); - return (handler: RequestHandlerUser): RequestHandler => { + return ( + handler: RequestHandlerUser + ): RequestHandler => { return (context, req, res) => { let user: ReportingRequestUser = false; if (setupDeps.security && setupDeps.security.license.isEnabled()) { diff --git a/x-pack/plugins/reporting/server/routes/types.d.ts b/x-pack/plugins/reporting/server/routes/types.d.ts index b3f9225c3dce..de451773437f 100644 --- a/x-pack/plugins/reporting/server/routes/types.d.ts +++ b/x-pack/plugins/reporting/server/routes/types.d.ts @@ -4,14 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, KibanaResponseFactory, RequestHandlerContext } from 'src/core/server'; -import { BaseParams, BasePayload, ReportingUser } from '../types'; +import { KibanaRequest, KibanaResponseFactory } from 'src/core/server'; +import type { + BaseParams, + BasePayload, + ReportingUser, + ReportingRequestHandlerContext, +} from '../types'; export type HandlerFunction = ( user: ReportingUser, exportType: string, jobParams: BaseParams, - context: RequestHandlerContext, + context: ReportingRequestHandlerContext, req: KibanaRequest, res: KibanaResponseFactory ) => any; diff --git a/x-pack/plugins/reporting/server/types.ts b/x-pack/plugins/reporting/server/types.ts index 8cd26df032f6..8ad458ed65d4 100644 --- a/x-pack/plugins/reporting/server/types.ts +++ b/x-pack/plugins/reporting/server/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; +import type { IRouter, KibanaRequest, RequestHandlerContext } from 'src/core/server'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { DataPluginStart } from 'src/plugins/data/server/plugin'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; @@ -58,7 +58,7 @@ export interface BasePayload extends BaseParams { // default fn type for CreateJobFnFactory export type CreateJobFn = ( jobParams: JobParamsType, - context: RequestHandlerContext, + context: ReportingRequestHandlerContext, request: KibanaRequest ) => Promise; @@ -89,3 +89,15 @@ export interface ExportTypeDefinition; validLicenses: string[]; } + +/** + * @internal + */ +export interface ReportingRequestHandlerContext extends RequestHandlerContext { + reporting: ReportingStart | null; +} + +/** + * @internal + */ +export type ReportingPluginRouter = IRouter; diff --git a/x-pack/plugins/rollup/server/plugin.ts b/x-pack/plugins/rollup/server/plugin.ts index 3c670f56c7d8..dc505d1aa585 100644 --- a/x-pack/plugins/rollup/server/plugin.ts +++ b/x-pack/plugins/rollup/server/plugin.ts @@ -4,12 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -declare module 'src/core/server' { - interface RequestHandlerContext { - rollup?: RollupContext; - } -} - import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; import { @@ -18,14 +12,13 @@ import { Plugin, Logger, PluginInitializerContext, - ILegacyScopedClusterClient, SharedGlobalConfig, } from 'src/core/server'; import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { PLUGIN, CONFIG_ROLLUPS } from '../common'; -import { Dependencies } from './types'; +import { Dependencies, RollupHandlerContext } from './types'; import { registerApiRoutes } from './routes'; import { License } from './services'; import { registerRollupUsageCollector } from './collectors'; @@ -36,9 +29,6 @@ import { isEsError } from './shared_imports'; import { formatEsError } from './lib/format_es_error'; import { getCapabilitiesForRollupIndices } from '../../../../src/plugins/data/server'; -interface RollupContext { - client: ILegacyScopedClusterClient; -} async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { const [core] = await getStartServices(); // Extend the elasticsearchJs client with additional endpoints. @@ -91,12 +81,15 @@ export class RollupPlugin implements Plugin { ], }); - http.registerRouteHandlerContext('rollup', async (context, request) => { - this.rollupEsClient = this.rollupEsClient ?? (await getCustomEsClient(getStartServices)); - return { - client: this.rollupEsClient.asScoped(request), - }; - }); + http.registerRouteHandlerContext( + 'rollup', + async (context, request) => { + this.rollupEsClient = this.rollupEsClient ?? (await getCustomEsClient(getStartServices)); + return { + client: this.rollupEsClient.asScoped(request), + }; + } + ); registerApiRoutes({ router: http.createRouter(), diff --git a/x-pack/plugins/rollup/server/routes/api/search/register_search_route.ts b/x-pack/plugins/rollup/server/routes/api/search/register_search_route.ts index c5c56336def1..cdc4c255a5ab 100644 --- a/x-pack/plugins/rollup/server/routes/api/search/register_search_route.ts +++ b/x-pack/plugins/rollup/server/routes/api/search/register_search_route.ts @@ -28,7 +28,7 @@ export const registerSearchRoute = ({ license.guardApiRoute(async (context, request, response) => { try { const requests = request.body.map(({ index, query }: { index: string; query?: any }) => - context.rollup!.client.callAsCurrentUser('rollup.search', { + context.rollup.client.callAsCurrentUser('rollup.search', { index, rest_total_hits_as_int: true, body: query, diff --git a/x-pack/plugins/rollup/server/services/license.ts b/x-pack/plugins/rollup/server/services/license.ts index 5424092a01ee..f80f91b8d03e 100644 --- a/x-pack/plugins/rollup/server/services/license.ts +++ b/x-pack/plugins/rollup/server/services/license.ts @@ -4,15 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import { Logger } from 'src/core/server'; -import { - KibanaRequest, - KibanaResponseFactory, - RequestHandler, - RequestHandlerContext, -} from 'src/core/server'; +import { KibanaRequest, KibanaResponseFactory, RequestHandler } from 'src/core/server'; import { LicensingPluginSetup } from '../../../licensing/server'; import { LicenseType } from '../../../licensing/common/types'; +import type { RollupHandlerContext } from '../types'; export interface LicenseStatus { isValid: boolean; @@ -59,11 +55,11 @@ export class License { }); } - guardApiRoute(handler: RequestHandler) { + guardApiRoute(handler: RequestHandler) { const license = this; return function licenseCheck( - ctx: RequestHandlerContext, + ctx: RollupHandlerContext, request: KibanaRequest, response: KibanaResponseFactory ) { diff --git a/x-pack/plugins/rollup/server/types.ts b/x-pack/plugins/rollup/server/types.ts index 89e13e69c4da..e6fe0eae15ed 100644 --- a/x-pack/plugins/rollup/server/types.ts +++ b/x-pack/plugins/rollup/server/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; +import { IRouter, ILegacyScopedClusterClient, RequestHandlerContext } from 'src/core/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { VisTypeTimeseriesSetup } from 'src/plugins/vis_type_timeseries/server'; @@ -26,7 +26,7 @@ export interface Dependencies { } export interface RouteDependencies { - router: IRouter; + router: RollupPluginRouter; license: License; lib: { isEsError: typeof isEsError; @@ -37,3 +37,22 @@ export interface RouteDependencies { IndexPatternsFetcher: typeof IndexPatternsFetcher; }; } + +/** + * @internal + */ +interface RollupApiRequestHandlerContext { + client: ILegacyScopedClusterClient; +} + +/** + * @internal + */ +export interface RollupHandlerContext extends RequestHandlerContext { + rollup: RollupApiRequestHandlerContext; +} + +/** + * @internal + */ +export type RollupPluginRouter = IRouter; diff --git a/x-pack/plugins/saved_objects_tagging/server/plugin.ts b/x-pack/plugins/saved_objects_tagging/server/plugin.ts index ce687866711e..4f291b7c033a 100644 --- a/x-pack/plugins/saved_objects_tagging/server/plugin.ts +++ b/x-pack/plugins/saved_objects_tagging/server/plugin.ts @@ -17,7 +17,7 @@ import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/s import { SecurityPluginSetup } from '../../security/server'; import { savedObjectsTaggingFeature } from './features'; import { tagType } from './saved_objects'; -import { ITagsRequestHandlerContext } from './types'; +import type { TagsHandlerContext } from './types'; import { TagsRequestHandlerContext } from './request_handler_context'; import { registerRoutes } from './routes'; import { createTagUsageCollector } from './usage'; @@ -41,12 +41,12 @@ export class SavedObjectTaggingPlugin implements Plugin<{}, {}, SetupDeps, {}> { ) { savedObjects.registerType(tagType); - const router = http.createRouter(); + const router = http.createRouter(); registerRoutes({ router }); - http.registerRouteHandlerContext( + http.registerRouteHandlerContext( 'tags', - async (context, req, res): Promise => { + async (context, req, res) => { return new TagsRequestHandlerContext(req, context.core, security); } ); diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/find_assignable_objects.ts b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/find_assignable_objects.ts index dcfb2f801eba..86fa6d8a7c40 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/find_assignable_objects.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/find_assignable_objects.ts @@ -5,10 +5,10 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; import { FindAssignableObjectResponse } from '../../../common/http_api_types'; +import type { TagsPluginRouter } from '../../types'; -export const registerFindAssignableObjectsRoute = (router: IRouter) => { +export const registerFindAssignableObjectsRoute = (router: TagsPluginRouter) => { router.get( { path: '/internal/saved_objects_tagging/assignments/_find_assignable_objects', diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/get_assignable_types.ts b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/get_assignable_types.ts index 182aa6d5ce43..627244badbce 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/get_assignable_types.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/get_assignable_types.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; +import type { TagsPluginRouter } from '../../types'; import { GetAssignableTypesResponse } from '../../../common/http_api_types'; -export const registerGetAssignableTypesRoute = (router: IRouter) => { +export const registerGetAssignableTypesRoute = (router: TagsPluginRouter) => { router.get( { path: '/internal/saved_objects_tagging/assignments/_assignable_types', diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/update_tags_assignments.ts b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/update_tags_assignments.ts index 2144b7ffd99a..b2179cd9b092 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/assignments/update_tags_assignments.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/assignments/update_tags_assignments.ts @@ -5,10 +5,10 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { TagsPluginRouter } from '../../types'; import { AssignmentError } from '../../services'; -export const registerUpdateTagsAssignmentsRoute = (router: IRouter) => { +export const registerUpdateTagsAssignmentsRoute = (router: TagsPluginRouter) => { const objectReferenceSchema = schema.object({ type: schema.string(), id: schema.string(), diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/index.ts b/x-pack/plugins/saved_objects_tagging/server/routes/index.ts index bba2673b1ce0..d1204ca96518 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/index.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; import { registerUpdateTagRoute, registerGetAllTagsRoute, @@ -18,8 +17,9 @@ import { registerGetAssignableTypesRoute, } from './assignments'; import { registerInternalFindTagsRoute, registerInternalBulkDeleteRoute } from './internal'; +import { TagsPluginRouter } from '../types'; -export const registerRoutes = ({ router }: { router: IRouter }) => { +export const registerRoutes = ({ router }: { router: TagsPluginRouter }) => { // tags API registerCreateTagRoute(router); registerUpdateTagRoute(router); diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/internal/bulk_delete.ts b/x-pack/plugins/saved_objects_tagging/server/routes/internal/bulk_delete.ts index bade81678543..68848f9716a2 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/internal/bulk_delete.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/internal/bulk_delete.ts @@ -5,9 +5,9 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { TagsPluginRouter } from '../../types'; -export const registerInternalBulkDeleteRoute = (router: IRouter) => { +export const registerInternalBulkDeleteRoute = (router: TagsPluginRouter) => { router.post( { path: '/internal/saved_objects_tagging/tags/_bulk_delete', diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/internal/find_tags.ts b/x-pack/plugins/saved_objects_tagging/server/routes/internal/find_tags.ts index 6e095eb6e4a6..e99d9c1da7b9 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/internal/find_tags.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/internal/find_tags.ts @@ -5,13 +5,13 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { TagsPluginRouter } from '../../types'; import { tagSavedObjectTypeName } from '../../../common/constants'; import { TagAttributes } from '../../../common/types'; import { savedObjectToTag } from '../../services/tags'; import { addConnectionCount } from '../lib'; -export const registerInternalFindTagsRoute = (router: IRouter) => { +export const registerInternalFindTagsRoute = (router: TagsPluginRouter) => { router.get( { path: '/internal/saved_objects_tagging/tags/_find', diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/tags/create_tag.ts b/x-pack/plugins/saved_objects_tagging/server/routes/tags/create_tag.ts index 499f73c3e047..56b05a83a333 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/tags/create_tag.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/tags/create_tag.ts @@ -5,10 +5,10 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { TagsPluginRouter } from '../../types'; import { TagValidationError } from '../../services/tags'; -export const registerCreateTagRoute = (router: IRouter) => { +export const registerCreateTagRoute = (router: TagsPluginRouter) => { router.post( { path: '/api/saved_objects_tagging/tags/create', diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/tags/delete_tag.ts b/x-pack/plugins/saved_objects_tagging/server/routes/tags/delete_tag.ts index 84f798063555..656a97aece88 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/tags/delete_tag.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/tags/delete_tag.ts @@ -5,9 +5,9 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { TagsPluginRouter } from '../../types'; -export const registerDeleteTagRoute = (router: IRouter) => { +export const registerDeleteTagRoute = (router: TagsPluginRouter) => { router.delete( { path: '/api/saved_objects_tagging/tags/{id}', diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_all_tags.ts b/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_all_tags.ts index cbc43d66f0ec..3603265beae5 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_all_tags.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_all_tags.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; +import type { TagsPluginRouter } from '../../types'; -export const registerGetAllTagsRoute = (router: IRouter) => { +export const registerGetAllTagsRoute = (router: TagsPluginRouter) => { router.get( { path: '/api/saved_objects_tagging/tags', diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_tag.ts b/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_tag.ts index 559c5ed2d8dd..9f7401d6c1cd 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_tag.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/tags/get_tag.ts @@ -5,9 +5,9 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; +import type { TagsPluginRouter } from '../../types'; -export const registerGetTagRoute = (router: IRouter) => { +export const registerGetTagRoute = (router: TagsPluginRouter) => { router.get( { path: '/api/saved_objects_tagging/tags/{id}', diff --git a/x-pack/plugins/saved_objects_tagging/server/routes/tags/update_tag.ts b/x-pack/plugins/saved_objects_tagging/server/routes/tags/update_tag.ts index fe8a48ae6e85..b81bc54c1b5d 100644 --- a/x-pack/plugins/saved_objects_tagging/server/routes/tags/update_tag.ts +++ b/x-pack/plugins/saved_objects_tagging/server/routes/tags/update_tag.ts @@ -5,10 +5,10 @@ */ import { schema } from '@kbn/config-schema'; -import { IRouter } from 'src/core/server'; import { TagValidationError } from '../../services/tags'; +import type { TagsPluginRouter } from '../../types'; -export const registerUpdateTagRoute = (router: IRouter) => { +export const registerUpdateTagRoute = (router: TagsPluginRouter) => { router.post( { path: '/api/saved_objects_tagging/tags/{id}', diff --git a/x-pack/plugins/saved_objects_tagging/server/types.ts b/x-pack/plugins/saved_objects_tagging/server/types.ts index de5997b84a75..6e3e151ad8ad 100644 --- a/x-pack/plugins/saved_objects_tagging/server/types.ts +++ b/x-pack/plugins/saved_objects_tagging/server/types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import type { IRouter, RequestHandlerContext } from 'src/core/server'; import { ITagsClient } from '../common/types'; import { IAssignmentService } from './services'; @@ -12,8 +13,14 @@ export interface ITagsRequestHandlerContext { assignmentService: IAssignmentService; } -declare module 'src/core/server' { - interface RequestHandlerContext { - tags?: ITagsRequestHandlerContext; - } +/** + * @internal + */ +export interface TagsHandlerContext extends RequestHandlerContext { + tags: ITagsRequestHandlerContext; } + +/** + * @internal + */ +export type TagsPluginRouter = IRouter; diff --git a/x-pack/plugins/security/common/licensing/index.mock.ts b/x-pack/plugins/security/common/licensing/index.mock.ts index 88cb3206a253..ed89ad5cc505 100644 --- a/x-pack/plugins/security/common/licensing/index.mock.ts +++ b/x-pack/plugins/security/common/licensing/index.mock.ts @@ -9,7 +9,7 @@ import { SecurityLicense, SecurityLicenseFeatures } from '.'; export const licenseMock = { create: (features?: Partial): jest.Mocked => ({ - isLicenseAvailable: jest.fn(), + isLicenseAvailable: jest.fn().mockReturnValue(true), isEnabled: jest.fn().mockReturnValue(true), getType: jest.fn().mockReturnValue('basic'), getFeatures: jest.fn(), diff --git a/x-pack/plugins/security/server/audit/audit_events.test.ts b/x-pack/plugins/security/server/audit/audit_events.test.ts index f7e41bce674e..ef77170de69e 100644 --- a/x-pack/plugins/security/server/audit/audit_events.test.ts +++ b/x-pack/plugins/security/server/audit/audit_events.test.ts @@ -115,6 +115,12 @@ describe('#savedObjectEvent', () => { savedObject: { type: 'dashboard', id: 'SAVED_OBJECT_ID' }, }) ).not.toBeUndefined(); + expect( + savedObjectEvent({ + action: SavedObjectAction.RESOLVE, + savedObject: { type: 'dashboard', id: 'SAVED_OBJECT_ID' }, + }) + ).not.toBeUndefined(); expect( savedObjectEvent({ action: SavedObjectAction.FIND, @@ -136,6 +142,18 @@ describe('#savedObjectEvent', () => { savedObject: { type: 'telemetry', id: 'SAVED_OBJECT_ID' }, }) ).toBeUndefined(); + expect( + savedObjectEvent({ + action: SavedObjectAction.RESOLVE, + savedObject: { type: 'config', id: 'SAVED_OBJECT_ID' }, + }) + ).toBeUndefined(); + expect( + savedObjectEvent({ + action: SavedObjectAction.RESOLVE, + savedObject: { type: 'telemetry', id: 'SAVED_OBJECT_ID' }, + }) + ).toBeUndefined(); expect( savedObjectEvent({ action: SavedObjectAction.FIND, diff --git a/x-pack/plugins/security/server/audit/audit_events.ts b/x-pack/plugins/security/server/audit/audit_events.ts index b6538af31bd6..f7d99877bca2 100644 --- a/x-pack/plugins/security/server/audit/audit_events.ts +++ b/x-pack/plugins/security/server/audit/audit_events.ts @@ -182,6 +182,7 @@ export function userLoginEvent({ export enum SavedObjectAction { CREATE = 'saved_object_create', GET = 'saved_object_get', + RESOLVE = 'saved_object_resolve', UPDATE = 'saved_object_update', DELETE = 'saved_object_delete', FIND = 'saved_object_find', @@ -195,6 +196,7 @@ type VerbsTuple = [string, string, string]; const savedObjectAuditVerbs: Record = { saved_object_create: ['create', 'creating', 'created'], saved_object_get: ['access', 'accessing', 'accessed'], + saved_object_resolve: ['resolve', 'resolving', 'resolved'], saved_object_update: ['update', 'updating', 'updated'], saved_object_delete: ['delete', 'deleting', 'deleted'], saved_object_find: ['access', 'accessing', 'accessed'], @@ -210,6 +212,7 @@ const savedObjectAuditVerbs: Record = { const savedObjectAuditTypes: Record = { saved_object_create: EventType.CREATION, saved_object_get: EventType.ACCESS, + saved_object_resolve: EventType.ACCESS, saved_object_update: EventType.CHANGE, saved_object_delete: EventType.DELETION, saved_object_find: EventType.ACCESS, diff --git a/x-pack/plugins/security/server/authentication/authentication_service.mock.ts b/x-pack/plugins/security/server/authentication/authentication_service.mock.ts index 06884611f387..9c67cf611eb4 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.mock.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.mock.ts @@ -5,17 +5,11 @@ */ import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; -import type { - AuthenticationServiceSetup, - AuthenticationServiceStart, -} from './authentication_service'; +import type { AuthenticationServiceStart } from './authentication_service'; import { apiKeysMock } from './api_keys/api_keys.mock'; export const authenticationServiceMock = { - createSetup: (): jest.Mocked => ({ - getCurrentUser: jest.fn(), - }), createStart: (): DeeplyMockedKeys => ({ apiKeys: apiKeysMock.create(), login: jest.fn(), diff --git a/x-pack/plugins/security/server/authentication/authentication_service.test.ts b/x-pack/plugins/security/server/authentication/authentication_service.test.ts index 244cf1d0a8f5..942ddc202360 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.test.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.test.ts @@ -25,11 +25,9 @@ import { sessionMock } from '../session_management/session.mock'; import type { AuthenticationHandler, AuthToolkit, - ILegacyClusterClient, KibanaRequest, Logger, LoggerFactory, - LegacyScopedClusterClient, HttpServiceSetup, HttpServiceStart, } from '../../../../../src/core/server'; @@ -46,47 +44,17 @@ describe('AuthenticationService', () => { let service: AuthenticationService; let logger: jest.Mocked; let mockSetupAuthenticationParams: { - legacyAuditLogger: jest.Mocked; - audit: jest.Mocked; - config: ConfigType; - loggers: LoggerFactory; http: jest.Mocked; - clusterClient: jest.Mocked; license: jest.Mocked; - getFeatureUsageService: () => jest.Mocked; - session: jest.Mocked>; }; - let mockScopedClusterClient: jest.Mocked>; beforeEach(() => { logger = loggingSystemMock.createLogger(); mockSetupAuthenticationParams = { - legacyAuditLogger: securityAuditLoggerMock.create(), - audit: auditServiceMock.create(), http: coreMock.createSetup().http, - config: createConfig( - ConfigSchema.validate({ - encryptionKey: 'ab'.repeat(16), - secureCookies: true, - cookieName: 'my-sid-cookie', - }), - loggingSystemMock.create().get(), - { isTLSEnabled: false } - ), - clusterClient: elasticsearchServiceMock.createLegacyClusterClient(), license: licenseMock.create(), - loggers: loggingSystemMock.create(), - getFeatureUsageService: jest - .fn() - .mockReturnValue(securityFeatureUsageServiceMock.createStartContract()), - session: sessionMock.create(), }; - mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockSetupAuthenticationParams.clusterClient.asScoped.mockReturnValue( - (mockScopedClusterClient as unknown) as jest.Mocked - ); - service = new AuthenticationService(logger); }); @@ -101,6 +69,42 @@ describe('AuthenticationService', () => { expect.any(Function) ); }); + }); + + describe('#start()', () => { + let mockStartAuthenticationParams: { + legacyAuditLogger: jest.Mocked; + audit: jest.Mocked; + config: ConfigType; + loggers: LoggerFactory; + http: jest.Mocked; + clusterClient: ReturnType; + featureUsageService: jest.Mocked; + session: jest.Mocked>; + }; + beforeEach(() => { + const coreStart = coreMock.createStart(); + mockStartAuthenticationParams = { + legacyAuditLogger: securityAuditLoggerMock.create(), + audit: auditServiceMock.create(), + config: createConfig( + ConfigSchema.validate({ + encryptionKey: 'ab'.repeat(16), + secureCookies: true, + cookieName: 'my-sid-cookie', + }), + loggingSystemMock.create().get(), + { isTLSEnabled: false } + ), + http: coreStart.http, + clusterClient: elasticsearchServiceMock.createClusterClient(), + loggers: loggingSystemMock.create(), + featureUsageService: securityFeatureUsageServiceMock.createStartContract(), + session: sessionMock.create(), + }; + + service.setup(mockSetupAuthenticationParams); + }); describe('authentication handler', () => { let authHandler: AuthenticationHandler; @@ -109,18 +113,30 @@ describe('AuthenticationService', () => { beforeEach(() => { mockAuthToolkit = httpServiceMock.createAuthToolkit(); - service.setup(mockSetupAuthenticationParams); - - expect(mockSetupAuthenticationParams.http.registerAuth).toHaveBeenCalledTimes(1); - expect(mockSetupAuthenticationParams.http.registerAuth).toHaveBeenCalledWith( - expect.any(Function) - ); + service.start(mockStartAuthenticationParams); authHandler = mockSetupAuthenticationParams.http.registerAuth.mock.calls[0][0]; authenticate = jest.requireMock('./authenticator').Authenticator.mock.instances[0] .authenticate; }); + it('returns error if license is not available.', async () => { + const mockResponse = httpServerMock.createLifecycleResponseFactory(); + + mockSetupAuthenticationParams.license.isLicenseAvailable.mockReturnValue(false); + + await authHandler(httpServerMock.createKibanaRequest(), mockResponse, mockAuthToolkit); + + expect(mockResponse.customError).toHaveBeenCalledTimes(1); + expect(mockResponse.customError).toHaveBeenCalledWith({ + body: 'License is not available.', + statusCode: 503, + headers: { 'Retry-After': '30' }, + }); + expect(mockAuthToolkit.authenticated).not.toHaveBeenCalled(); + expect(mockAuthToolkit.redirected).not.toHaveBeenCalled(); + }); + it('replies with no credentials when security is disabled in elasticsearch', async () => { const mockRequest = httpServerMock.createKibanaRequest(); const mockResponse = httpServerMock.createLifecycleResponseFactory(); @@ -281,63 +297,7 @@ describe('AuthenticationService', () => { describe('getCurrentUser()', () => { let getCurrentUser: (r: KibanaRequest) => AuthenticatedUser | null; beforeEach(async () => { - getCurrentUser = service.setup(mockSetupAuthenticationParams).getCurrentUser; - }); - - it('returns `null` if Security is disabled', () => { - mockSetupAuthenticationParams.license.isEnabled.mockReturnValue(false); - - expect(getCurrentUser(httpServerMock.createKibanaRequest())).toBe(null); - }); - - it('returns user from the auth state.', () => { - const mockUser = mockAuthenticatedUser(); - - const mockAuthGet = mockSetupAuthenticationParams.http.auth.get as jest.Mock; - mockAuthGet.mockReturnValue({ state: mockUser }); - - const mockRequest = httpServerMock.createKibanaRequest(); - expect(getCurrentUser(mockRequest)).toBe(mockUser); - expect(mockAuthGet).toHaveBeenCalledTimes(1); - expect(mockAuthGet).toHaveBeenCalledWith(mockRequest); - }); - - it('returns null if auth state is not available.', () => { - const mockAuthGet = mockSetupAuthenticationParams.http.auth.get as jest.Mock; - mockAuthGet.mockReturnValue({}); - - const mockRequest = httpServerMock.createKibanaRequest(); - expect(getCurrentUser(mockRequest)).toBeNull(); - expect(mockAuthGet).toHaveBeenCalledTimes(1); - expect(mockAuthGet).toHaveBeenCalledWith(mockRequest); - }); - }); - }); - - describe('#start()', () => { - let mockStartAuthenticationParams: { - http: jest.Mocked; - clusterClient: ReturnType; - }; - beforeEach(() => { - const coreStart = coreMock.createStart(); - mockStartAuthenticationParams = { - http: coreStart.http, - clusterClient: elasticsearchServiceMock.createClusterClient(), - }; - service.setup(mockSetupAuthenticationParams); - }); - - describe('getCurrentUser()', () => { - let getCurrentUser: (r: KibanaRequest) => AuthenticatedUser | null; - beforeEach(async () => { - getCurrentUser = (await service.start(mockStartAuthenticationParams)).getCurrentUser; - }); - - it('returns `null` if Security is disabled', () => { - mockSetupAuthenticationParams.license.isEnabled.mockReturnValue(false); - - expect(getCurrentUser(httpServerMock.createKibanaRequest())).toBe(null); + getCurrentUser = service.start(mockStartAuthenticationParams).getCurrentUser; }); it('returns user from the auth state.', () => { diff --git a/x-pack/plugins/security/server/authentication/authentication_service.ts b/x-pack/plugins/security/server/authentication/authentication_service.ts index 6cd20592f21a..3ab92d0bd211 100644 --- a/x-pack/plugins/security/server/authentication/authentication_service.ts +++ b/x-pack/plugins/security/server/authentication/authentication_service.ts @@ -11,7 +11,6 @@ import type { Logger, HttpServiceSetup, IClusterClient, - ILegacyClusterClient, HttpServiceStart, } from '../../../../../src/core/server'; import type { SecurityLicense } from '../../common/licensing'; @@ -27,27 +26,19 @@ import { APIKeys } from './api_keys'; import { Authenticator, ProviderLoginAttempt } from './authenticator'; interface AuthenticationServiceSetupParams { - legacyAuditLogger: SecurityAuditLogger; - audit: AuditServiceSetup; - getFeatureUsageService: () => SecurityFeatureUsageServiceStart; - http: HttpServiceSetup; - clusterClient: ILegacyClusterClient; - config: ConfigType; + http: Pick; license: SecurityLicense; - loggers: LoggerFactory; - session: PublicMethodsOf; } interface AuthenticationServiceStartParams { - http: HttpServiceStart; + http: Pick; + config: ConfigType; clusterClient: IClusterClient; -} - -export interface AuthenticationServiceSetup { - /** - * @deprecated use `getCurrentUser` from the start contract instead - */ - getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null; + legacyAuditLogger: SecurityAuditLogger; + audit: AuditServiceSetup; + featureUsageService: SecurityFeatureUsageServiceStart; + session: PublicMethodsOf; + loggers: LoggerFactory; } export interface AuthenticationServiceStart { @@ -67,50 +58,40 @@ export interface AuthenticationServiceStart { export class AuthenticationService { private license!: SecurityLicense; - private authenticator!: Authenticator; + private authenticator?: Authenticator; constructor(private readonly logger: Logger) {} - setup({ - legacyAuditLogger: auditLogger, - audit, - getFeatureUsageService, - http, - clusterClient, - config, - license, - loggers, - session, - }: AuthenticationServiceSetupParams): AuthenticationServiceSetup { + setup({ http, license }: AuthenticationServiceSetupParams) { this.license = license; - const getCurrentUser = (request: KibanaRequest) => { - if (!license.isEnabled()) { - return null; + http.registerAuth(async (request, response, t) => { + if (!license.isLicenseAvailable()) { + this.logger.error('License is not available, authentication is not possible.'); + return response.customError({ + body: 'License is not available.', + statusCode: 503, + headers: { 'Retry-After': '30' }, + }); } - return http.auth.get(request).state ?? null; - }; - - this.authenticator = new Authenticator({ - legacyAuditLogger: auditLogger, - audit, - loggers, - clusterClient, - basePath: http.basePath, - config: { authc: config.authc }, - getCurrentUser, - getFeatureUsageService, - license, - session, - }); - - http.registerAuth(async (request, response, t) => { - // If security is disabled continue with no user credentials and delete the client cookie as well. + // If security is disabled, then continue with no user credentials. if (!license.isEnabled()) { + this.logger.debug( + 'Current license does not support any security features, authentication is not needed.' + ); return t.authenticated(); } + if (!this.authenticator) { + this.logger.error('Authentication sub-system is not fully initialized yet.'); + return response.customError({ + body: 'Authentication sub-system is not fully initialized yet.', + statusCode: 503, + headers: { 'Retry-After': '30' }, + }); + } + let authenticationResult; try { authenticationResult = await this.authenticator.authenticate(request); @@ -162,19 +143,40 @@ export class AuthenticationService { }); this.logger.debug('Successfully registered core authentication handler.'); - - return { - getCurrentUser, - }; } - start({ clusterClient, http }: AuthenticationServiceStartParams): AuthenticationServiceStart { + start({ + audit, + config, + clusterClient, + featureUsageService, + http, + legacyAuditLogger, + loggers, + session, + }: AuthenticationServiceStartParams): AuthenticationServiceStart { const apiKeys = new APIKeys({ clusterClient, logger: this.logger.get('api-key'), license: this.license, }); + const getCurrentUser = (request: KibanaRequest) => + http.auth.get(request).state ?? null; + + this.authenticator = new Authenticator({ + legacyAuditLogger, + audit, + loggers, + clusterClient, + basePath: http.basePath, + config: { authc: config.authc }, + getCurrentUser, + featureUsageService, + license: this.license, + session, + }); + return { apiKeys: { areAPIKeysEnabled: apiKeys.areAPIKeysEnabled.bind(apiKeys), @@ -194,12 +196,7 @@ export class AuthenticationService { * Retrieves currently authenticated user associated with the specified request. * @param request */ - getCurrentUser: (request: KibanaRequest) => { - if (!this.license.isEnabled()) { - return null; - } - return http.auth.get(request).state ?? null; - }, + getCurrentUser, }; } } diff --git a/x-pack/plugins/security/server/authentication/authenticator.test.ts b/x-pack/plugins/security/server/authentication/authenticator.test.ts index 3d3946fde9f3..08d671d64179 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.test.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.test.ts @@ -43,7 +43,7 @@ function getMockOptions({ legacyAuditLogger: securityAuditLoggerMock.create(), audit: auditServiceMock.create(), getCurrentUser: jest.fn(), - clusterClient: elasticsearchServiceMock.createLegacyClusterClient(), + clusterClient: elasticsearchServiceMock.createClusterClient(), basePath: httpServiceMock.createSetupContract().basePath, license: licenseMock.create(), loggers: loggingSystemMock.create(), @@ -53,9 +53,7 @@ function getMockOptions({ { isTLSEnabled: false } ), session: sessionMock.create(), - getFeatureUsageService: jest - .fn() - .mockReturnValue(securityFeatureUsageServiceMock.createStartContract()), + featureUsageService: securityFeatureUsageServiceMock.createStartContract(), }; } @@ -1880,9 +1878,7 @@ describe('Authenticator', () => { ); expect(mockOptions.session.update).not.toHaveBeenCalled(); - expect( - mockOptions.getFeatureUsageService().recordPreAccessAgreementUsage - ).not.toHaveBeenCalled(); + expect(mockOptions.featureUsageService.recordPreAccessAgreementUsage).not.toHaveBeenCalled(); }); it('fails if cannot retrieve user session', async () => { @@ -1895,12 +1891,10 @@ describe('Authenticator', () => { ); expect(mockOptions.session.update).not.toHaveBeenCalled(); - expect( - mockOptions.getFeatureUsageService().recordPreAccessAgreementUsage - ).not.toHaveBeenCalled(); + expect(mockOptions.featureUsageService.recordPreAccessAgreementUsage).not.toHaveBeenCalled(); }); - it('fails if license doesn allow access agreement acknowledgement', async () => { + it('fails if license does not allow access agreement acknowledgement', async () => { mockOptions.license.getFeatures.mockReturnValue({ allowAccessAgreement: false, } as SecurityLicenseFeatures); @@ -1912,9 +1906,7 @@ describe('Authenticator', () => { ); expect(mockOptions.session.update).not.toHaveBeenCalled(); - expect( - mockOptions.getFeatureUsageService().recordPreAccessAgreementUsage - ).not.toHaveBeenCalled(); + expect(mockOptions.featureUsageService.recordPreAccessAgreementUsage).not.toHaveBeenCalled(); }); it('properly acknowledges access agreement for the authenticated user', async () => { @@ -1936,9 +1928,9 @@ describe('Authenticator', () => { } ); - expect( - mockOptions.getFeatureUsageService().recordPreAccessAgreementUsage - ).toHaveBeenCalledTimes(1); + expect(mockOptions.featureUsageService.recordPreAccessAgreementUsage).toHaveBeenCalledTimes( + 1 + ); }); }); }); diff --git a/x-pack/plugins/security/server/authentication/authenticator.ts b/x-pack/plugins/security/server/authentication/authenticator.ts index 85215ebf46fb..af492bf24772 100644 --- a/x-pack/plugins/security/server/authentication/authenticator.ts +++ b/x-pack/plugins/security/server/authentication/authenticator.ts @@ -7,8 +7,8 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import { KibanaRequest, LoggerFactory, - ILegacyClusterClient, IBasePath, + IClusterClient, } from '../../../../../src/core/server'; import { AUTH_PROVIDER_HINT_QUERY_STRING_PARAMETER, @@ -68,13 +68,13 @@ export interface ProviderLoginAttempt { export interface AuthenticatorOptions { legacyAuditLogger: SecurityAuditLogger; audit: AuditServiceSetup; - getFeatureUsageService: () => SecurityFeatureUsageServiceStart; + featureUsageService: SecurityFeatureUsageServiceStart; getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null; config: Pick; basePath: IBasePath; license: SecurityLicense; loggers: LoggerFactory; - clusterClient: ILegacyClusterClient; + clusterClient: IClusterClient; session: PublicMethodsOf; } @@ -201,7 +201,7 @@ export class Authenticator { client: this.options.clusterClient, basePath: this.options.basePath, tokens: new Tokens({ - client: this.options.clusterClient, + client: this.options.clusterClient.asInternalUser, logger: this.options.loggers.get('tokens'), }), }; @@ -448,7 +448,7 @@ export class Authenticator { existingSessionValue.provider ); - this.options.getFeatureUsageService().recordPreAccessAgreementUsage(); + this.options.featureUsageService.recordPreAccessAgreementUsage(); } /** diff --git a/x-pack/plugins/security/server/authentication/index.ts b/x-pack/plugins/security/server/authentication/index.ts index c87a02c9545c..e745e1c5717b 100644 --- a/x-pack/plugins/security/server/authentication/index.ts +++ b/x-pack/plugins/security/server/authentication/index.ts @@ -5,11 +5,7 @@ */ export { canRedirectRequest } from './can_redirect_request'; -export { - AuthenticationService, - AuthenticationServiceSetup, - AuthenticationServiceStart, -} from './authentication_service'; +export { AuthenticationService, AuthenticationServiceStart } from './authentication_service'; export { AuthenticationResult } from './authentication_result'; export { DeauthenticationResult } from './deauthentication_result'; export { diff --git a/x-pack/plugins/security/server/authentication/providers/anonymous.test.ts b/x-pack/plugins/security/server/authentication/providers/anonymous.test.ts index 9674181e1875..a2db41331954 100644 --- a/x-pack/plugins/security/server/authentication/providers/anonymous.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/anonymous.test.ts @@ -4,11 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import { errors } from '@elastic/elasticsearch'; + import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; +import { securityMock } from '../../mocks'; import { mockAuthenticationProviderOptions } from './base.mock'; -import { ILegacyClusterClient, ScopeableRequest } from '../../../../../../src/core/server'; +import { ScopeableRequest } from '../../../../../../src/core/server'; import { AuthenticationResult } from '../authentication_result'; import { DeauthenticationResult } from '../deauthentication_result'; import { @@ -18,15 +21,14 @@ import { import { AnonymousAuthenticationProvider } from './anonymous'; function expectAuthenticateCall( - mockClusterClient: jest.Mocked, + mockClusterClient: ReturnType, scopeableRequest: ScopeableRequest ) { expect(mockClusterClient.asScoped).toHaveBeenCalledTimes(1); expect(mockClusterClient.asScoped).toHaveBeenCalledWith(scopeableRequest); const mockScopedClusterClient = mockClusterClient.asScoped.mock.results[0].value; - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledTimes(1); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.authenticate'); + expect(mockScopedClusterClient.asCurrentUser.security.authenticate).toHaveBeenCalledTimes(1); } enum CredentialsType { @@ -75,8 +77,10 @@ describe('AnonymousAuthenticationProvider', () => { describe('`login` method', () => { it('succeeds if credentials are valid, and creates session and authHeaders', async () => { - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: user }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect( @@ -92,10 +96,13 @@ describe('AnonymousAuthenticationProvider', () => { it('fails if user cannot be retrieved during login attempt', async () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); - - const authenticationError = new Error('Some error'); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(authenticationError); + const authenticationError = new errors.ResponseError( + securityMock.createApiResponse({ body: {} }) + ); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + authenticationError + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.login(request)).resolves.toEqual( @@ -155,8 +162,10 @@ describe('AnonymousAuthenticationProvider', () => { it('succeeds for non-AJAX requests if state is available.', async () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: user }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request, {})).resolves.toEqual( @@ -169,8 +178,10 @@ describe('AnonymousAuthenticationProvider', () => { it('succeeds for AJAX requests if state is available.', async () => { const request = httpServerMock.createKibanaRequest({ headers: { 'kbn-xsrf': 'xsrf' } }); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: user }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request, {})).resolves.toEqual( @@ -185,8 +196,10 @@ describe('AnonymousAuthenticationProvider', () => { it('non-AJAX requests can start a new session.', async () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: user }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request)).resolves.toEqual( @@ -199,9 +212,13 @@ describe('AnonymousAuthenticationProvider', () => { it('fails if credentials are not valid.', async () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); - const authenticationError = new Error('Forbidden'); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(authenticationError); + const authenticationError = new errors.ResponseError( + securityMock.createApiResponse({ body: {} }) + ); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + authenticationError + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request)).resolves.toEqual( @@ -225,8 +242,10 @@ describe('AnonymousAuthenticationProvider', () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: user }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request, {})).resolves.toEqual( diff --git a/x-pack/plugins/security/server/authentication/providers/anonymous.ts b/x-pack/plugins/security/server/authentication/providers/anonymous.ts index 1585b0592b35..249b4adea7bb 100644 --- a/x-pack/plugins/security/server/authentication/providers/anonymous.ts +++ b/x-pack/plugins/security/server/authentication/providers/anonymous.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaRequest, LegacyElasticsearchErrorHelpers } from '../../../../../../src/core/server'; +import { KibanaRequest } from '../../../../../../src/core/server'; +import { getErrorStatusCode } from '../../errors'; import { AuthenticationResult } from '../authentication_result'; import { canRedirectRequest } from '../can_redirect_request'; import { DeauthenticationResult } from '../deauthentication_result'; @@ -213,7 +214,7 @@ export class AnonymousAuthenticationProvider extends BaseAuthenticationProvider // Create session only if it doesn't exist yet, otherwise keep it unchanged. return AuthenticationResult.succeeded(user, { authHeaders, state: state ? undefined : {} }); } catch (err) { - if (LegacyElasticsearchErrorHelpers.isNotAuthorizedError(err)) { + if (getErrorStatusCode(err) === 401) { if (!this.httpAuthorizationHeader) { this.logger.error( `Failed to authenticate anonymous request using Elasticsearch reserved anonymous user. Anonymous access may not be properly configured in Elasticsearch: ${err.message}` diff --git a/x-pack/plugins/security/server/authentication/providers/base.mock.ts b/x-pack/plugins/security/server/authentication/providers/base.mock.ts index 47d961bc8faf..3eea6f9aadad 100644 --- a/x-pack/plugins/security/server/authentication/providers/base.mock.ts +++ b/x-pack/plugins/security/server/authentication/providers/base.mock.ts @@ -16,7 +16,7 @@ export type MockAuthenticationProviderOptions = ReturnType< export function mockAuthenticationProviderOptions(options?: { name: string }) { return { - client: elasticsearchServiceMock.createLegacyClusterClient(), + client: elasticsearchServiceMock.createClusterClient(), logger: loggingSystemMock.create().get(), basePath: httpServiceMock.createBasePath(), tokens: { refresh: jest.fn(), invalidate: jest.fn() }, diff --git a/x-pack/plugins/security/server/authentication/providers/base.ts b/x-pack/plugins/security/server/authentication/providers/base.ts index f1845617c87a..73449cf1077f 100644 --- a/x-pack/plugins/security/server/authentication/providers/base.ts +++ b/x-pack/plugins/security/server/authentication/providers/base.ts @@ -10,8 +10,8 @@ import { KibanaRequest, Logger, HttpServiceSetup, - ILegacyClusterClient, Headers, + IClusterClient, } from '../../../../../../src/core/server'; import type { AuthenticatedUser } from '../../../common/model'; import type { AuthenticationInfo } from '../../elasticsearch'; @@ -25,7 +25,7 @@ import { Tokens } from '../tokens'; export interface AuthenticationProviderOptions { name: string; basePath: HttpServiceSetup['basePath']; - client: ILegacyClusterClient; + client: IClusterClient; logger: Logger; tokens: PublicMethodsOf; urls: { @@ -111,9 +111,11 @@ export abstract class BaseAuthenticationProvider { */ protected async getUser(request: KibanaRequest, authHeaders: Headers = {}) { return this.authenticationInfoToAuthenticatedUser( - await this.options.client - .asScoped({ headers: { ...request.headers, ...authHeaders } }) - .callAsCurrentUser('shield.authenticate') + ( + await this.options.client + .asScoped({ headers: { ...request.headers, ...authHeaders } }) + .asCurrentUser.security.authenticate() + ).body ); } diff --git a/x-pack/plugins/security/server/authentication/providers/basic.test.ts b/x-pack/plugins/security/server/authentication/providers/basic.test.ts index 4f93e2327da0..e7cf3d95b082 100644 --- a/x-pack/plugins/security/server/authentication/providers/basic.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/basic.test.ts @@ -4,11 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ +import { errors } from '@elastic/elasticsearch'; + import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; +import { securityMock } from '../../mocks'; import { mockAuthenticationProviderOptions } from './base.mock'; -import { ILegacyClusterClient, ScopeableRequest } from '../../../../../../src/core/server'; +import { ScopeableRequest } from '../../../../../../src/core/server'; import { AuthenticationResult } from '../authentication_result'; import { DeauthenticationResult } from '../deauthentication_result'; import { BasicAuthenticationProvider } from './basic'; @@ -18,15 +21,14 @@ function generateAuthorizationHeader(username: string, password: string) { } function expectAuthenticateCall( - mockClusterClient: jest.Mocked, + mockClusterClient: ReturnType, scopeableRequest: ScopeableRequest ) { expect(mockClusterClient.asScoped).toHaveBeenCalledTimes(1); expect(mockClusterClient.asScoped).toHaveBeenCalledWith(scopeableRequest); const mockScopedClusterClient = mockClusterClient.asScoped.mock.results[0].value; - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledTimes(1); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.authenticate'); + expect(mockScopedClusterClient.asCurrentUser.security.authenticate).toHaveBeenCalledTimes(1); } describe('BasicAuthenticationProvider', () => { @@ -45,8 +47,10 @@ describe('BasicAuthenticationProvider', () => { const credentials = { username: 'user', password: 'password' }; const authorization = generateAuthorizationHeader(credentials.username, credentials.password); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: user }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect( @@ -66,9 +70,13 @@ describe('BasicAuthenticationProvider', () => { const credentials = { username: 'user', password: 'password' }; const authorization = generateAuthorizationHeader(credentials.username, credentials.password); - const authenticationError = new Error('Some error'); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(authenticationError); + const authenticationError = new errors.ResponseError( + securityMock.createApiResponse({ body: {} }) + ); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + authenticationError + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.login(request, credentials)).resolves.toEqual( @@ -149,8 +157,10 @@ describe('BasicAuthenticationProvider', () => { const user = mockAuthenticatedUser(); const authorization = generateAuthorizationHeader('user', 'password'); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: user }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request, { authorization })).resolves.toEqual( @@ -164,9 +174,13 @@ describe('BasicAuthenticationProvider', () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); const authorization = generateAuthorizationHeader('user', 'password'); - const authenticationError = new Error('Forbidden'); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(authenticationError); + const authenticationError = new errors.ResponseError( + securityMock.createApiResponse({ body: {} }) + ); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + authenticationError + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request, { authorization })).resolves.toEqual( diff --git a/x-pack/plugins/security/server/authentication/providers/http.test.ts b/x-pack/plugins/security/server/authentication/providers/http.test.ts index 512a8ead2c32..b8a2a110d45b 100644 --- a/x-pack/plugins/security/server/authentication/providers/http.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/http.test.ts @@ -4,29 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ +import { errors } from '@elastic/elasticsearch'; + import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; +import { securityMock } from '../../mocks'; import { MockAuthenticationProviderOptions, mockAuthenticationProviderOptions } from './base.mock'; -import { - LegacyElasticsearchErrorHelpers, - ILegacyClusterClient, - ScopeableRequest, -} from '../../../../../../src/core/server'; +import { ScopeableRequest } from '../../../../../../src/core/server'; import { AuthenticationResult } from '../authentication_result'; import { DeauthenticationResult } from '../deauthentication_result'; import { HTTPAuthenticationProvider } from './http'; function expectAuthenticateCall( - mockClusterClient: jest.Mocked, + mockClusterClient: ReturnType, scopeableRequest: ScopeableRequest ) { expect(mockClusterClient.asScoped).toHaveBeenCalledTimes(1); expect(mockClusterClient.asScoped).toHaveBeenCalledWith(scopeableRequest); const mockScopedClusterClient = mockClusterClient.asScoped.mock.results[0].value; - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledTimes(1); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.authenticate'); + expect(mockScopedClusterClient.asCurrentUser.security.authenticate).toHaveBeenCalledTimes(1); } describe('HTTPAuthenticationProvider', () => { @@ -58,7 +56,6 @@ describe('HTTPAuthenticationProvider', () => { await expect(provider.login()).resolves.toEqual(AuthenticationResult.notHandled()); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); }); }); @@ -73,7 +70,6 @@ describe('HTTPAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); }); it('does not handle authentication for requests with empty scheme in `authorization` header.', async () => { @@ -88,7 +84,6 @@ describe('HTTPAuthenticationProvider', () => { ).resolves.toEqual(AuthenticationResult.notHandled()); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); }); it('does not handle authentication via `authorization` header if scheme is not supported.', async () => { @@ -112,7 +107,6 @@ describe('HTTPAuthenticationProvider', () => { } expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); }); it('succeeds if authentication via `authorization` header with supported scheme succeeds.', async () => { @@ -126,8 +120,10 @@ describe('HTTPAuthenticationProvider', () => { ]) { const request = httpServerMock.createKibanaRequest({ headers: { authorization: header } }); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: user }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); mockOptions.client.asScoped.mockClear(); @@ -149,7 +145,7 @@ describe('HTTPAuthenticationProvider', () => { }); it('fails if authentication via `authorization` header with supported scheme fails.', async () => { - const failureReason = LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()); + const failureReason = new errors.ResponseError(securityMock.createApiResponse({ body: {} })); for (const { schemes, header } of [ { schemes: ['basic'], header: 'Basic xxx' }, { schemes: ['bearer'], header: 'Bearer xxx' }, @@ -159,8 +155,10 @@ describe('HTTPAuthenticationProvider', () => { ]) { const request = httpServerMock.createKibanaRequest({ headers: { authorization: header } }); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(failureReason); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + failureReason + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); mockOptions.client.asScoped.mockClear(); @@ -188,7 +186,6 @@ describe('HTTPAuthenticationProvider', () => { await expect(provider.logout()).resolves.toEqual(DeauthenticationResult.notHandled()); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); }); }); 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 d368bf90cf36..f8b7b42d1845 100644 --- a/x-pack/plugins/security/server/authentication/providers/kerberos.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/kerberos.test.ts @@ -5,32 +5,27 @@ */ import Boom from '@hapi/boom'; -import { errors } from 'elasticsearch'; +import { errors } from '@elastic/elasticsearch'; import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; +import { securityMock } from '../../mocks'; import { MockAuthenticationProviderOptions, mockAuthenticationProviderOptions } from './base.mock'; -import { - LegacyElasticsearchErrorHelpers, - ILegacyClusterClient, - KibanaRequest, - ScopeableRequest, -} from '../../../../../../src/core/server'; +import { KibanaRequest, ScopeableRequest } from '../../../../../../src/core/server'; import { AuthenticationResult } from '../authentication_result'; import { DeauthenticationResult } from '../deauthentication_result'; import { KerberosAuthenticationProvider } from './kerberos'; function expectAuthenticateCall( - mockClusterClient: jest.Mocked, + mockClusterClient: ReturnType, scopeableRequest: ScopeableRequest ) { expect(mockClusterClient.asScoped).toHaveBeenCalledTimes(1); expect(mockClusterClient.asScoped).toHaveBeenCalledWith(scopeableRequest); const mockScopedClusterClient = mockClusterClient.asScoped.mock.results[0].value; - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledTimes(1); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.authenticate'); + expect(mockScopedClusterClient.asCurrentUser.security.authenticate).toHaveBeenCalledTimes(1); } describe('KerberosAuthenticationProvider', () => { @@ -47,8 +42,10 @@ describe('KerberosAuthenticationProvider', () => { it('does not handle requests that can be authenticated without `Negotiate` header.', async () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue({}); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: {} }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(operation(request)).resolves.toEqual(AuthenticationResult.notHandled()); @@ -61,9 +58,9 @@ describe('KerberosAuthenticationProvider', () => { it('does not handle requests if backend does not support Kerberos.', async () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); @@ -77,17 +74,18 @@ describe('KerberosAuthenticationProvider', () => { it('fails with `Negotiate` challenge if backend supports Kerberos.', async () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); - const failureReason = LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError( - new (errors.AuthenticationException as any)('Unauthorized', { + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ + statusCode: 401, body: { error: { header: { 'WWW-Authenticate': 'Negotiate' } } }, }) ); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(failureReason); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue(failureReason); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(operation(request)).resolves.toEqual( - AuthenticationResult.failed(failureReason, { + AuthenticationResult.failed(Boom.unauthorized(), { authResponseHeaders: { 'WWW-Authenticate': 'Negotiate' }, }) ); @@ -100,9 +98,12 @@ describe('KerberosAuthenticationProvider', () => { it('fails if request authentication is failed with non-401 error.', async () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); - const failureReason = new errors.ServiceUnavailable(); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(failureReason); + const failureReason = new errors.NoLivingConnectionsError( + 'Unavailable', + securityMock.createApiResponse({ statusCode: 500, body: {} }) + ); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue(failureReason); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(operation(request)).resolves.toEqual(AuthenticationResult.failed(failureReason)); @@ -118,11 +119,15 @@ describe('KerberosAuthenticationProvider', () => { headers: { authorization: 'negotiate spnego' }, }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - access_token: 'some-token', - refresh_token: 'some-refresh-token', - authentication: user, - }); + mockOptions.client.asInternalUser.security.getToken.mockResolvedValue( + securityMock.createApiResponse({ + body: { + access_token: 'some-token', + refresh_token: 'some-refresh-token', + authentication: user, + }, + }) + ); await expect(operation(request)).resolves.toEqual( AuthenticationResult.succeeded( @@ -135,7 +140,7 @@ describe('KerberosAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.getAccessToken', { + expect(mockOptions.client.asInternalUser.security.getToken).toHaveBeenCalledWith({ body: { grant_type: '_kerberos', kerberos_ticket: 'spnego' }, }); @@ -148,12 +153,16 @@ describe('KerberosAuthenticationProvider', () => { headers: { authorization: 'negotiate spnego' }, }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - access_token: 'some-token', - refresh_token: 'some-refresh-token', - kerberos_authentication_response_token: 'response-token', - authentication: user, - }); + mockOptions.client.asInternalUser.security.getToken.mockResolvedValue( + securityMock.createApiResponse({ + body: { + access_token: 'some-token', + refresh_token: 'some-refresh-token', + kerberos_authentication_response_token: 'response-token', + authentication: user, + }, + }) + ); await expect(operation(request)).resolves.toEqual( AuthenticationResult.succeeded( @@ -167,7 +176,7 @@ describe('KerberosAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.getAccessToken', { + expect(mockOptions.client.asInternalUser.security.getToken).toHaveBeenCalledWith({ body: { grant_type: '_kerberos', kerberos_ticket: 'spnego' }, }); @@ -179,12 +188,13 @@ describe('KerberosAuthenticationProvider', () => { headers: { authorization: 'negotiate spnego' }, }); - const failureReason = LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError( - new (errors.AuthenticationException as any)('Unauthorized', { + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ + statusCode: 401, body: { error: { header: { 'WWW-Authenticate': 'Negotiate response-token' } } }, }) ); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + mockOptions.client.asInternalUser.security.getToken.mockRejectedValue(failureReason); await expect(operation(request)).resolves.toEqual( AuthenticationResult.failed(Boom.unauthorized(), { @@ -192,7 +202,7 @@ describe('KerberosAuthenticationProvider', () => { }) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.getAccessToken', { + expect(mockOptions.client.asInternalUser.security.getToken).toHaveBeenCalledWith({ body: { grant_type: '_kerberos', kerberos_ticket: 'spnego' }, }); @@ -204,12 +214,13 @@ describe('KerberosAuthenticationProvider', () => { headers: { authorization: 'negotiate spnego' }, }); - const failureReason = LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError( - new (errors.AuthenticationException as any)('Unauthorized', { + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ + statusCode: 401, body: { error: { header: { 'WWW-Authenticate': 'Negotiate' } } }, }) ); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + mockOptions.client.asInternalUser.security.getToken.mockRejectedValue(failureReason); await expect(operation(request)).resolves.toEqual( AuthenticationResult.failed(Boom.unauthorized(), { @@ -217,7 +228,7 @@ describe('KerberosAuthenticationProvider', () => { }) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.getAccessToken', { + expect(mockOptions.client.asInternalUser.security.getToken).toHaveBeenCalledWith({ body: { grant_type: '_kerberos', kerberos_ticket: 'spnego' }, }); @@ -229,12 +240,14 @@ describe('KerberosAuthenticationProvider', () => { headers: { authorization: 'negotiate spnego' }, }); - const failureReason = LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 401, body: {} }) + ); + mockOptions.client.asInternalUser.security.getToken.mockRejectedValue(failureReason); await expect(operation(request)).resolves.toEqual(AuthenticationResult.failed(failureReason)); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.getAccessToken', { + expect(mockOptions.client.asInternalUser.security.getToken).toHaveBeenCalledWith({ body: { grant_type: '_kerberos', kerberos_ticket: 'spnego' }, }); @@ -259,7 +272,7 @@ describe('KerberosAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.security.getToken).not.toHaveBeenCalled(); expect(request.headers.authorization).toBe('Bearer some-token'); }); @@ -277,7 +290,7 @@ describe('KerberosAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.security.getToken).not.toHaveBeenCalled(); expect(request.headers.authorization).toBe('Bearer some-token'); }); @@ -285,14 +298,15 @@ describe('KerberosAuthenticationProvider', () => { const request = httpServerMock.createKibanaRequest(); const tokenPair = { accessToken: 'token', refreshToken: 'refresh-token' }; - const failureReason = LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(failureReason); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); mockOptions.tokens.refresh.mockResolvedValue(null); await expect(provider.authenticate(request, tokenPair)).resolves.toEqual( - AuthenticationResult.failed(failureReason) + AuthenticationResult.failed(Boom.unauthorized()) ); expect(mockOptions.tokens.refresh).toHaveBeenCalledTimes(1); @@ -306,7 +320,7 @@ describe('KerberosAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.security.getToken).not.toHaveBeenCalled(); }); it('does not start SPNEGO for Ajax requests.', async () => { @@ -316,7 +330,7 @@ describe('KerberosAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.security.getToken).not.toHaveBeenCalled(); }); it('succeeds if state contains a valid token.', async () => { @@ -328,8 +342,10 @@ describe('KerberosAuthenticationProvider', () => { }; const authorization = `Bearer ${tokenPair.accessToken}`; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: user }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request, tokenPair)).resolves.toEqual( @@ -349,9 +365,9 @@ describe('KerberosAuthenticationProvider', () => { const request = httpServerMock.createKibanaRequest(); const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); @@ -384,9 +400,11 @@ describe('KerberosAuthenticationProvider', () => { refreshToken: 'some-valid-refresh-token', }; - const failureReason = new errors.InternalServerError('Token is not valid!'); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 503, body: {} }) + ); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue(failureReason); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request, tokenPair)).resolves.toEqual( @@ -396,23 +414,22 @@ describe('KerberosAuthenticationProvider', () => { expectAuthenticateCall(mockOptions.client, { headers: { authorization: `Bearer ${tokenPair.accessToken}` }, }); - - expect(mockScopedClusterClient.callAsInternalUser).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.security.getToken).not.toHaveBeenCalled(); expect(request.headers).not.toHaveProperty('authorization'); }); it('fails with `Negotiate` challenge if both access and refresh tokens from the state are expired and backend supports Kerberos.', async () => { - const failureReason = LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError( - new (errors.AuthenticationException as any)('Unauthorized', { - body: { error: { header: { 'WWW-Authenticate': 'Negotiate' } } }, - }) + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError( + securityMock.createApiResponse({ + statusCode: 401, + body: { error: { header: { 'WWW-Authenticate': 'Negotiate' } } }, + }) + ) ); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(failureReason); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); - mockOptions.tokens.refresh.mockResolvedValue(null); const nonAjaxRequest = httpServerMock.createKibanaRequest(); @@ -421,7 +438,7 @@ describe('KerberosAuthenticationProvider', () => { refreshToken: 'some-valid-refresh-token', }; await expect(provider.authenticate(nonAjaxRequest, nonAjaxTokenPair)).resolves.toEqual( - AuthenticationResult.failed(failureReason, { + AuthenticationResult.failed(Boom.unauthorized(), { authResponseHeaders: { 'WWW-Authenticate': 'Negotiate' }, }) ); @@ -432,7 +449,7 @@ describe('KerberosAuthenticationProvider', () => { refreshToken: 'ajax-some-valid-refresh-token', }; await expect(provider.authenticate(ajaxRequest, ajaxTokenPair)).resolves.toEqual( - AuthenticationResult.failed(failureReason, { + AuthenticationResult.failed(Boom.unauthorized(), { authResponseHeaders: { 'WWW-Authenticate': 'Negotiate' }, }) ); @@ -445,7 +462,7 @@ describe('KerberosAuthenticationProvider', () => { await expect( provider.authenticate(optionalAuthRequest, optionalAuthTokenPair) ).resolves.toEqual( - AuthenticationResult.failed(failureReason, { + AuthenticationResult.failed(Boom.unauthorized(), { authResponseHeaders: { 'WWW-Authenticate': 'Negotiate' }, }) ); diff --git a/x-pack/plugins/security/server/authentication/providers/kerberos.ts b/x-pack/plugins/security/server/authentication/providers/kerberos.ts index b7abed979164..a02f6a8dfb94 100644 --- a/x-pack/plugins/security/server/authentication/providers/kerberos.ts +++ b/x-pack/plugins/security/server/authentication/providers/kerberos.ts @@ -5,12 +5,10 @@ */ import Boom from '@hapi/boom'; -import { - LegacyElasticsearchError, - LegacyElasticsearchErrorHelpers, - KibanaRequest, -} from '../../../../../../src/core/server'; +import { errors } from '@elastic/elasticsearch'; +import type { KibanaRequest } from '../../../../../../src/core/server'; import type { AuthenticationInfo } from '../../elasticsearch'; +import { getDetailedErrorMessage, getErrorStatusCode } from '../../errors'; import { AuthenticationResult } from '../authentication_result'; import { DeauthenticationResult } from '../deauthentication_result'; import { HTTPAuthorizationHeader } from '../http_authentication'; @@ -153,16 +151,21 @@ export class KerberosAuthenticationProvider extends BaseAuthenticationProvider { authentication: AuthenticationInfo; }; try { - tokens = await this.options.client.callAsInternalUser('shield.getAccessToken', { - body: { grant_type: '_kerberos', kerberos_ticket: kerberosTicket }, - }); + tokens = ( + await this.options.client.asInternalUser.security.getToken({ + body: { grant_type: '_kerberos', kerberos_ticket: kerberosTicket }, + }) + ).body; } catch (err) { - this.logger.debug(`Failed to exchange SPNEGO token for an access token: ${err.message}`); + this.logger.debug( + `Failed to exchange SPNEGO token for an access token: ${getDetailedErrorMessage(err)}` + ); // Check if SPNEGO context wasn't established and we have a response token to return to the client. - const challenge = LegacyElasticsearchErrorHelpers.isNotAuthorizedError(err) - ? this.getNegotiateChallenge(err) - : undefined; + const challenge = + getErrorStatusCode(err) === 401 && err instanceof errors.ResponseError + ? this.getNegotiateChallenge(err) + : undefined; if (!challenge) { return AuthenticationResult.failed(err); } @@ -292,7 +295,7 @@ export class KerberosAuthenticationProvider extends BaseAuthenticationProvider { this.logger.debug('Trying to authenticate request via SPNEGO.'); // Try to authenticate current request with Elasticsearch to see whether it supports SPNEGO. - let elasticsearchError: LegacyElasticsearchError; + let elasticsearchError: errors.ResponseError; try { await this.getUser(request, { // We should send a fake SPNEGO token to Elasticsearch to make sure Kerberos realm is included @@ -306,7 +309,7 @@ export class KerberosAuthenticationProvider extends BaseAuthenticationProvider { } catch (err) { // Fail immediately if we get unexpected error (e.g. ES isn't available). We should not touch // session cookie in this case. - if (!LegacyElasticsearchErrorHelpers.isNotAuthorizedError(err)) { + if (getErrorStatusCode(err) !== 401 || !(err instanceof errors.ResponseError)) { return AuthenticationResult.failed(err); } @@ -332,11 +335,14 @@ export class KerberosAuthenticationProvider extends BaseAuthenticationProvider { * Extracts `Negotiate` challenge from the list of challenges returned with Elasticsearch error if any. * @param error Error to extract challenges from. */ - private getNegotiateChallenge(error: LegacyElasticsearchError) { + private getNegotiateChallenge(error: errors.ResponseError) { + // We extract headers from the original Elasticsearch error and not from the top-level `headers` + // property of the Elasticsearch client error since client merges multiple `WWW-Authenticate` + // headers into one using comma as a separator. That makes it hard to correctly parse the header + // since `WWW-Authenticate` values can also include commas. const challenges = ([] as string[]).concat( - (error.output.headers as { [key: string]: string })[WWWAuthenticateHeaderName] + error.body?.error?.header?.[WWWAuthenticateHeaderName] || [] ); - const negotiateChallenge = challenges.find((challenge) => challenge.toLowerCase().startsWith('negotiate') ); 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 9988ddd99c39..8037b067852d 100644 --- a/x-pack/plugins/security/server/authentication/providers/oidc.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/oidc.test.ts @@ -5,16 +5,14 @@ */ import Boom from '@hapi/boom'; +import { errors } from '@elastic/elasticsearch'; import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; +import { securityMock } from '../../mocks'; import { MockAuthenticationProviderOptions, mockAuthenticationProviderOptions } from './base.mock'; -import { - LegacyElasticsearchErrorHelpers, - KibanaRequest, - ILegacyScopedClusterClient, -} from '../../../../../../src/core/server'; +import { KibanaRequest } from '../../../../../../src/core/server'; import { AuthenticationResult } from '../authentication_result'; import { DeauthenticationResult } from '../deauthentication_result'; import { OIDCAuthenticationProvider, OIDCLogin, ProviderLoginAttempt } from './oidc'; @@ -24,19 +22,18 @@ describe('OIDCAuthenticationProvider', () => { let provider: OIDCAuthenticationProvider; let mockOptions: MockAuthenticationProviderOptions; let mockUser: AuthenticatedUser; - let mockScopedClusterClient: jest.Mocked; + let mockScopedClusterClient: ReturnType< + typeof elasticsearchServiceMock.createScopedClusterClient + >; beforeEach(() => { mockOptions = mockAuthenticationProviderOptions({ name: 'oidc' }); - mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); + mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); mockUser = mockAuthenticatedUser({ authentication_provider: { type: 'oidc', name: 'oidc' } }); - mockScopedClusterClient.callAsCurrentUser.mockImplementation(async (method) => { - if (method === 'shield.authenticate') { - return mockUser; - } - - throw new Error(`Unexpected call to ${method}!`); - }); + mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: mockUser }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); provider = new OIDCAuthenticationProvider(mockOptions, { realm: 'oidc1' }); @@ -60,17 +57,21 @@ describe('OIDCAuthenticationProvider', () => { it('redirects third party initiated login attempts to the OpenId Connect Provider.', async () => { const request = httpServerMock.createKibanaRequest({ path: '/api/security/oidc/callback' }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - state: 'statevalue', - nonce: 'noncevalue', - redirect: - 'https://op-host/path/login?response_type=code' + - '&scope=openid%20profile%20email' + - '&client_id=s6BhdRkqt3' + - '&state=statevalue' + - '&redirect_uri=https%3A%2F%2Ftest-hostname:1234%2Ftest-base-path%2Fapi%2Fsecurity%2Fv1%2F/oidc' + - '&login_hint=loginhint', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + state: 'statevalue', + nonce: 'noncevalue', + redirect: + 'https://op-host/path/login?response_type=code' + + '&scope=openid%20profile%20email' + + '&client_id=s6BhdRkqt3' + + '&state=statevalue' + + '&redirect_uri=https%3A%2F%2Ftest-hostname:1234%2Ftest-base-path%2Fapi%2Fsecurity%2Fv1%2F/oidc' + + '&login_hint=loginhint', + }, + }) + ); await expect( provider.login(request, { @@ -97,7 +98,10 @@ describe('OIDCAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.oidcPrepare', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/oidc/prepare', body: { iss: 'theissuer', login_hint: 'loginhint' }, }); }); @@ -105,17 +109,21 @@ describe('OIDCAuthenticationProvider', () => { it('redirects user initiated login attempts to the OpenId Connect Provider.', async () => { const request = httpServerMock.createKibanaRequest(); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - state: 'statevalue', - nonce: 'noncevalue', - redirect: - 'https://op-host/path/login?response_type=code' + - '&scope=openid%20profile%20email' + - '&client_id=s6BhdRkqt3' + - '&state=statevalue' + - '&redirect_uri=https%3A%2F%2Ftest-hostname:1234%2Ftest-base-path%2Fapi%2Fsecurity%2Fv1%2F/oidc' + - '&login_hint=loginhint', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + state: 'statevalue', + nonce: 'noncevalue', + redirect: + 'https://op-host/path/login?response_type=code' + + '&scope=openid%20profile%20email' + + '&client_id=s6BhdRkqt3' + + '&state=statevalue' + + '&redirect_uri=https%3A%2F%2Ftest-hostname:1234%2Ftest-base-path%2Fapi%2Fsecurity%2Fv1%2F/oidc' + + '&login_hint=loginhint', + }, + }) + ); await expect( provider.login(request, { @@ -141,7 +149,10 @@ describe('OIDCAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.oidcPrepare', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/oidc/prepare', body: { realm: 'oidc1' }, }); }); @@ -149,8 +160,10 @@ describe('OIDCAuthenticationProvider', () => { it('fails if OpenID Connect authentication request preparation fails.', async () => { const request = httpServerMock.createKibanaRequest(); - const failureReason = new Error('Realm is misconfigured!'); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 503, body: {} }) + ); + mockOptions.client.asInternalUser.transport.request.mockRejectedValue(failureReason); await expect( provider.login(request, { @@ -159,8 +172,11 @@ describe('OIDCAuthenticationProvider', () => { }) ).resolves.toEqual(AuthenticationResult.failed(failureReason)); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.oidcPrepare', { - body: { realm: `oidc1` }, + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/oidc/prepare', + body: { realm: 'oidc1' }, }); }); @@ -174,11 +190,15 @@ describe('OIDCAuthenticationProvider', () => { it('gets token and redirects user to requested URL if OIDC authentication response is valid.', async () => { const { request, attempt, expectedRedirectURI } = getMocks(); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - authentication: mockUser, - access_token: 'some-token', - refresh_token: 'some-refresh-token', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + authentication: mockUser, + access_token: 'some-token', + refresh_token: 'some-refresh-token', + }, + }) + ); await expect( provider.login(request, attempt, { @@ -198,17 +218,17 @@ describe('OIDCAuthenticationProvider', () => { }) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.oidcAuthenticate', - { - body: { - state: 'statevalue', - nonce: 'noncevalue', - redirect_uri: expectedRedirectURI, - realm: 'oidc1', - }, - } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/oidc/authenticate', + body: { + state: 'statevalue', + nonce: 'noncevalue', + redirect_uri: expectedRedirectURI, + realm: 'oidc1', + }, + }); }); it('fails if authentication response is presented but session state does not contain the state parameter.', async () => { @@ -224,7 +244,7 @@ describe('OIDCAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('fails if authentication response is presented but session state does not contain redirect URL.', async () => { @@ -244,7 +264,7 @@ describe('OIDCAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('fails if session state is not presented.', async () => { @@ -258,16 +278,19 @@ describe('OIDCAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('fails if authentication response is not valid.', async () => { const { request, attempt, expectedRedirectURI } = getMocks(); - const failureReason = new Error( - 'Failed to exchange code for Id Token using the Token Endpoint.' + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ + statusCode: 400, + body: { message: 'Failed to exchange code for Id Token using the Token Endpoint.' }, + }) ); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + mockOptions.client.asInternalUser.transport.request.mockRejectedValue(failureReason); await expect( provider.login(request, attempt, { @@ -278,17 +301,17 @@ describe('OIDCAuthenticationProvider', () => { }) ).resolves.toEqual(AuthenticationResult.failed(failureReason)); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.oidcAuthenticate', - { - body: { - state: 'statevalue', - nonce: 'noncevalue', - redirect_uri: expectedRedirectURI, - realm: 'oidc1', - }, - } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/oidc/authenticate', + body: { + state: 'statevalue', + nonce: 'noncevalue', + redirect_uri: expectedRedirectURI, + realm: 'oidc1', + }, + }); }); it('fails if realm from state is different from the realm provider is configured with.', async () => { @@ -302,7 +325,7 @@ describe('OIDCAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); } @@ -353,11 +376,6 @@ describe('OIDCAuthenticationProvider', () => { it('redirects non-AJAX request that can not be authenticated to the "capture URL" page.', async () => { const request = httpServerMock.createKibanaRequest({ path: '/s/foo/some-path' }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - id: 'some-request-id', - redirect: 'https://idp-host/path/login?SAMLRequest=some%20request%20', - }); - await expect(provider.authenticate(request, null)).resolves.toEqual( AuthenticationResult.redirectTo( '/mock-server-basepath/internal/security/capture-url?next=%2Fmock-server-basepath%2Fs%2Ffoo%2Fsome-path&providerType=oidc&providerName=oidc', @@ -365,7 +383,7 @@ describe('OIDCAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('succeeds if state contains a valid token.', async () => { @@ -425,8 +443,10 @@ describe('OIDCAuthenticationProvider', () => { }; const authorization = `Bearer ${tokenPair.accessToken}`; - const failureReason = new Error('Token is not valid!'); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 400, body: {} }) + ); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue(failureReason); await expect( provider.authenticate(request, { ...tokenPair, realm: 'oidc1' }) @@ -441,8 +461,8 @@ describe('OIDCAuthenticationProvider', () => { const request = httpServerMock.createKibanaRequest(); const tokenPair = { accessToken: 'expired-token', refreshToken: 'valid-refresh-token' }; - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.tokens.refresh.mockResolvedValue({ @@ -475,8 +495,8 @@ describe('OIDCAuthenticationProvider', () => { const tokenPair = { accessToken: 'expired-token', refreshToken: 'invalid-refresh-token' }; const authorization = `Bearer ${tokenPair.accessToken}`; - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); const refreshFailureReason = { @@ -502,8 +522,8 @@ describe('OIDCAuthenticationProvider', () => { const tokenPair = { accessToken: 'expired-token', refreshToken: 'expired-refresh-token' }; const authorization = `Bearer ${tokenPair.accessToken}`; - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.tokens.refresh.mockResolvedValue(null); @@ -524,10 +544,9 @@ describe('OIDCAuthenticationProvider', () => { expect(mockOptions.client.asScoped).toHaveBeenCalledWith({ headers: { authorization }, }); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledTimes(1); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.authenticate'); + expect(mockScopedClusterClient.asCurrentUser.security.authenticate).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('fails for AJAX requests with user friendly message if refresh token is expired.', async () => { @@ -535,8 +554,8 @@ describe('OIDCAuthenticationProvider', () => { const tokenPair = { accessToken: 'expired-token', refreshToken: 'expired-refresh-token' }; const authorization = `Bearer ${tokenPair.accessToken}`; - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.tokens.refresh.mockResolvedValue(null); @@ -562,8 +581,8 @@ describe('OIDCAuthenticationProvider', () => { const tokenPair = { accessToken: 'expired-token', refreshToken: 'expired-refresh-token' }; const authorization = `Bearer ${tokenPair.accessToken}`; - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.tokens.refresh.mockResolvedValue(null); @@ -604,7 +623,7 @@ describe('OIDCAuthenticationProvider', () => { await expect(provider.logout(request)).resolves.toEqual(DeauthenticationResult.notHandled()); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('redirects to logged out view if state is `null` or does not include access token.', async () => { @@ -617,7 +636,7 @@ describe('OIDCAuthenticationProvider', () => { DeauthenticationResult.redirectTo(mockOptions.urls.loggedOut(request)) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('fails if OpenID Connect logout call fails.', async () => { @@ -625,15 +644,22 @@ describe('OIDCAuthenticationProvider', () => { const accessToken = 'x-oidc-token'; const refreshToken = 'x-oidc-refresh-token'; - const failureReason = new Error('Realm is misconfigured!'); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ + statusCode: 400, + body: { message: 'Realm is misconfigured!' }, + }) + ); + mockOptions.client.asInternalUser.transport.request.mockRejectedValue(failureReason); await expect( provider.logout(request, { accessToken, refreshToken, realm: 'oidc1' }) ).resolves.toEqual(DeauthenticationResult.failed(failureReason)); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.oidcLogout', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/oidc/logout', body: { token: accessToken, refresh_token: refreshToken }, }); }); @@ -643,14 +669,18 @@ describe('OIDCAuthenticationProvider', () => { const accessToken = 'x-oidc-token'; const refreshToken = 'x-oidc-refresh-token'; - mockOptions.client.callAsInternalUser.mockResolvedValue({ redirect: null }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ body: { redirect: null } }) + ); await expect( provider.logout(request, { accessToken, refreshToken, realm: 'oidc1' }) ).resolves.toEqual(DeauthenticationResult.redirectTo(mockOptions.urls.loggedOut(request))); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.oidcLogout', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/oidc/logout', body: { token: accessToken, refresh_token: refreshToken }, }); }); @@ -660,9 +690,11 @@ describe('OIDCAuthenticationProvider', () => { const accessToken = 'x-oidc-token'; const refreshToken = 'x-oidc-refresh-token'; - mockOptions.client.callAsInternalUser.mockResolvedValue({ - redirect: 'http://fake-idp/logout&id_token_hint=thehint', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { redirect: 'http://fake-idp/logout&id_token_hint=thehint' }, + }) + ); await expect( provider.logout(request, { accessToken, refreshToken, realm: 'oidc1' }) @@ -670,8 +702,10 @@ describe('OIDCAuthenticationProvider', () => { DeauthenticationResult.redirectTo('http://fake-idp/logout&id_token_hint=thehint') ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.oidcLogout', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/oidc/logout', body: { token: accessToken, refresh_token: refreshToken }, }); }); diff --git a/x-pack/plugins/security/server/authentication/providers/oidc.ts b/x-pack/plugins/security/server/authentication/providers/oidc.ts index c46ea37f144e..b89267f44eee 100644 --- a/x-pack/plugins/security/server/authentication/providers/oidc.ts +++ b/x-pack/plugins/security/server/authentication/providers/oidc.ts @@ -248,14 +248,20 @@ export class OIDCAuthenticationProvider extends BaseAuthenticationProvider { try { // This operation should be performed on behalf of the user with a privilege that normal // user usually doesn't have `cluster:admin/xpack/security/oidc/authenticate`. - result = await this.options.client.callAsInternalUser('shield.oidcAuthenticate', { - body: { - state: stateOIDCState, - nonce: stateNonce, - redirect_uri: authenticationResponseURI, - realm: this.realm, - }, - }); + // We can replace generic `transport.request` with a dedicated API method call once + // https://github.com/elastic/elasticsearch/issues/67189 is resolved. + result = ( + await this.options.client.asInternalUser.transport.request({ + method: 'POST', + path: '/_security/oidc/authenticate', + body: { + state: stateOIDCState, + nonce: stateNonce, + redirect_uri: authenticationResponseURI, + realm: this.realm, + }, + }) + ).body as any; } catch (err) { this.logger.debug(`Failed to authenticate request via OpenID Connect: ${err.message}`); return AuthenticationResult.failed(err); @@ -289,11 +295,15 @@ export class OIDCAuthenticationProvider extends BaseAuthenticationProvider { try { // This operation should be performed on behalf of the user with a privilege that normal // user usually doesn't have `cluster:admin/xpack/security/oidc/prepare`. - const { - state, - nonce, - redirect, - } = await this.options.client.callAsInternalUser('shield.oidcPrepare', { body: params }); + // We can replace generic `transport.request` with a dedicated API method call once + // https://github.com/elastic/elasticsearch/issues/67189 is resolved. + const { state, nonce, redirect } = ( + await this.options.client.asInternalUser.transport.request({ + method: 'POST', + path: '/_security/oidc/prepare', + body: params, + }) + ).body as any; this.logger.debug('Redirecting to OpenID Connect Provider with authentication request.'); return AuthenticationResult.redirectTo( @@ -407,18 +417,17 @@ export class OIDCAuthenticationProvider extends BaseAuthenticationProvider { if (state?.accessToken) { try { - const logoutBody = { - body: { - token: state.accessToken, - refresh_token: state.refreshToken, - }, - }; // This operation should be performed on behalf of the user with a privilege that normal // user usually doesn't have `cluster:admin/xpack/security/oidc/logout`. - const { redirect } = await this.options.client.callAsInternalUser( - 'shield.oidcLogout', - logoutBody - ); + // We can replace generic `transport.request` with a dedicated API method call once + // https://github.com/elastic/elasticsearch/issues/67189 is resolved. + const { redirect } = ( + await this.options.client.asInternalUser.transport.request({ + method: 'POST', + path: '/_security/oidc/logout', + body: { token: state.accessToken, refresh_token: state.refreshToken }, + }) + ).body as any; this.logger.debug('User session has been successfully invalidated.'); 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 88753f8dc2ab..d98d6ca4fa07 100644 --- a/x-pack/plugins/security/server/authentication/providers/pki.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/pki.test.ts @@ -10,18 +10,14 @@ jest.mock('tls'); import { Socket } from 'net'; import { PeerCertificate, TLSSocket } from 'tls'; import Boom from '@hapi/boom'; -import { errors } from 'elasticsearch'; +import { errors } from '@elastic/elasticsearch'; import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; +import { securityMock } from '../../mocks'; import { MockAuthenticationProviderOptions, mockAuthenticationProviderOptions } from './base.mock'; -import { - LegacyElasticsearchErrorHelpers, - ILegacyClusterClient, - KibanaRequest, - ScopeableRequest, -} from '../../../../../../src/core/server'; +import { KibanaRequest, ScopeableRequest } from '../../../../../../src/core/server'; import { AuthenticationResult } from '../authentication_result'; import { DeauthenticationResult } from '../deauthentication_result'; import { PKIAuthenticationProvider } from './pki'; @@ -87,15 +83,14 @@ function getMockSocket({ } function expectAuthenticateCall( - mockClusterClient: jest.Mocked, + mockClusterClient: ReturnType, scopeableRequest: ScopeableRequest ) { expect(mockClusterClient.asScoped).toHaveBeenCalledTimes(1); expect(mockClusterClient.asScoped).toHaveBeenCalledWith(scopeableRequest); const mockScopedClusterClient = mockClusterClient.asScoped.mock.results[0].value; - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledTimes(1); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.authenticate'); + expect(mockScopedClusterClient.asCurrentUser.security.authenticate).toHaveBeenCalledTimes(1); } describe('PKIAuthenticationProvider', () => { @@ -125,7 +120,7 @@ describe('PKIAuthenticationProvider', () => { await expect(operation(request)).resolves.toEqual(AuthenticationResult.notHandled()); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); expectDebugLogs( 'Peer certificate chain: [{"subject":"mock subject(2A:7A:C2:DD)","issuer":"mock issuer","issuerCertType":"object","subjectaltname":"mock subjectaltname","validFrom":"mock valid_from","validTo":"mock valid_to"}]', 'Authentication is not possible since peer certificate was not authorized: Error: mock authorization error.' @@ -139,7 +134,7 @@ describe('PKIAuthenticationProvider', () => { await expect(operation(request)).resolves.toEqual(AuthenticationResult.notHandled()); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); expectDebugLogs( 'Peer certificate chain: []', 'Authentication is not possible due to missing peer certificate chain.' @@ -159,7 +154,7 @@ describe('PKIAuthenticationProvider', () => { await expect(operation(request)).resolves.toEqual(AuthenticationResult.notHandled()); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); expectDebugLogs( `Detected incomplete certificate chain with protocol 'TLSv1.3', cannot renegotiate connection.`, 'Peer certificate chain: [{"subject":"mock subject(2A:7A:C2:DD)","issuer":"mock issuer","issuerCertType":"undefined","subjectaltname":"mock subjectaltname","validFrom":"mock valid_from","validTo":"mock valid_to"}]', @@ -181,7 +176,7 @@ describe('PKIAuthenticationProvider', () => { await expect(operation(request)).resolves.toEqual(AuthenticationResult.notHandled()); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); expectDebugLogs( `Detected incomplete certificate chain with protocol 'TLSv1.2', attempting to renegotiate connection.`, `Failed to renegotiate connection: Error: Oh no!.`, @@ -203,7 +198,7 @@ describe('PKIAuthenticationProvider', () => { await expect(operation(request)).resolves.toEqual(AuthenticationResult.notHandled()); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); expectDebugLogs( `Detected incomplete certificate chain with protocol 'TLSv1.2', attempting to renegotiate connection.`, 'Peer certificate chain: [{"subject":"mock subject(2A:7A:C2:DD)","issuer":"mock issuer","issuerCertType":"undefined","subjectaltname":"mock subjectaltname","validFrom":"mock valid_from","validTo":"mock valid_to"}]', @@ -231,7 +226,7 @@ describe('PKIAuthenticationProvider', () => { await expect(operation(request)).resolves.toEqual(AuthenticationResult.notHandled()); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); expectDebugLogs( `Detected incomplete certificate chain with protocol 'TLSv1.2', attempting to renegotiate connection.`, 'Self-signed certificate is detected in certificate chain', @@ -253,10 +248,11 @@ describe('PKIAuthenticationProvider', () => { mockGetPeerCertificate.mockReturnValueOnce(peerCertificate1); const request = httpServerMock.createKibanaRequest({ socket }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - authentication: user, - access_token: 'access-token', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { authentication: user, access_token: 'access-token' }, + }) + ); await expect(operation(request)).resolves.toEqual( AuthenticationResult.succeeded( @@ -268,8 +264,10 @@ describe('PKIAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.delegatePKI', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/delegate_pki', body: { x509_certificate_chain: [ 'fingerprint:2A:7A:C2:DD:base64', @@ -295,10 +293,11 @@ describe('PKIAuthenticationProvider', () => { const { socket } = getMockSocket({ authorized: true, peerCertificate }); const request = httpServerMock.createKibanaRequest({ socket, headers: {} }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - authentication: user, - access_token: 'access-token', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { authentication: user, access_token: 'access-token' }, + }) + ); await expect(operation(request)).resolves.toEqual( AuthenticationResult.succeeded( @@ -310,8 +309,10 @@ describe('PKIAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.delegatePKI', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/delegate_pki', body: { x509_certificate_chain: [ 'fingerprint:2A:7A:C2:DD:base64', @@ -330,10 +331,11 @@ describe('PKIAuthenticationProvider', () => { const { socket } = getMockSocket({ authorized: true, peerCertificate }); const request = httpServerMock.createKibanaRequest({ socket, headers: {} }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - authentication: user, - access_token: 'access-token', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { authentication: user, access_token: 'access-token' }, + }) + ); await expect(operation(request)).resolves.toEqual( AuthenticationResult.succeeded( @@ -345,8 +347,10 @@ describe('PKIAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.delegatePKI', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/delegate_pki', body: { x509_certificate_chain: ['fingerprint:2A:7A:C2:DD:base64'] }, }); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); @@ -359,13 +363,17 @@ describe('PKIAuthenticationProvider', () => { const { socket } = getMockSocket({ authorized: true, peerCertificate }); const request = httpServerMock.createKibanaRequest({ socket, headers: {} }); - const failureReason = LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 401, body: {} }) + ); + mockOptions.client.asInternalUser.transport.request.mockRejectedValue(failureReason); await expect(operation(request)).resolves.toEqual(AuthenticationResult.failed(failureReason)); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.delegatePKI', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/delegate_pki', body: { x509_certificate_chain: ['fingerprint:2A:7A:C2:DD:base64'] }, }); @@ -390,7 +398,7 @@ describe('PKIAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); expect(request.headers.authorization).toBe('Bearer some-token'); }); @@ -408,7 +416,7 @@ describe('PKIAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); expect(request.headers.authorization).toBe('Bearer some-token'); }); @@ -421,7 +429,7 @@ describe('PKIAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('does not exchange peer certificate to access token for Ajax requests.', async () => { @@ -436,7 +444,7 @@ describe('PKIAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('fails with non-401 error if state is available, peer is authorized, but certificate is not available.', async () => { @@ -490,10 +498,11 @@ describe('PKIAuthenticationProvider', () => { const request = httpServerMock.createKibanaRequest({ socket }); const state = { accessToken: 'existing-token', peerCertificateFingerprint256: '3A:9A:C5:DD' }; - mockOptions.client.callAsInternalUser.mockResolvedValue({ - authentication: user, - access_token: 'access-token', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { authentication: user, access_token: 'access-token' }, + }) + ); await expect(provider.authenticate(request, state)).resolves.toEqual( AuthenticationResult.succeeded( @@ -510,8 +519,10 @@ describe('PKIAuthenticationProvider', () => { accessToken: state.accessToken, }); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.delegatePKI', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/delegate_pki', body: { x509_certificate_chain: [ 'fingerprint:2A:7A:C2:DD:base64', @@ -526,15 +537,16 @@ describe('PKIAuthenticationProvider', () => { it('gets a new access token even if existing token is expired.', async () => { const user = mockAuthenticatedUser({ authentication_provider: { type: 'pki', name: 'pki' } }); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - authentication: user, - access_token: 'access-token', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { authentication: user, access_token: 'access-token' }, + }) + ); const nonAjaxRequest = httpServerMock.createKibanaRequest({ socket: getMockSocket({ @@ -589,8 +601,10 @@ describe('PKIAuthenticationProvider', () => { }) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(3); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.delegatePKI', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(3); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/delegate_pki', body: { x509_certificate_chain: [ 'fingerprint:2A:7A:C2:DD:base64', @@ -598,7 +612,9 @@ describe('PKIAuthenticationProvider', () => { ], }, }); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.delegatePKI', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/delegate_pki', body: { x509_certificate_chain: [ 'fingerprint:3A:7A:C2:DD:base64', @@ -606,7 +622,9 @@ describe('PKIAuthenticationProvider', () => { ], }, }); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.delegatePKI', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/delegate_pki', body: { x509_certificate_chain: [ 'fingerprint:4A:7A:C2:DD:base64', @@ -625,9 +643,9 @@ describe('PKIAuthenticationProvider', () => { const request = httpServerMock.createKibanaRequest({ socket }); const state = { accessToken: 'existing-token', peerCertificateFingerprint256: '2A:7A:C2:DD' }; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); @@ -635,7 +653,7 @@ describe('PKIAuthenticationProvider', () => { AuthenticationResult.failed(Boom.unauthorized()) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); expect(request.headers).not.toHaveProperty('authorization'); }); @@ -647,8 +665,10 @@ describe('PKIAuthenticationProvider', () => { const { socket } = getMockSocket({ authorized: true, peerCertificate }); const request = httpServerMock.createKibanaRequest({ socket, headers: {} }); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: user }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request, state)).resolves.toEqual( @@ -660,7 +680,7 @@ describe('PKIAuthenticationProvider', () => { expectAuthenticateCall(mockOptions.client, { headers: { authorization: 'Bearer token' } }); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); expect(request.headers).not.toHaveProperty('authorization'); }); @@ -671,9 +691,12 @@ describe('PKIAuthenticationProvider', () => { const { socket } = getMockSocket({ authorized: true, peerCertificate }); const request = httpServerMock.createKibanaRequest({ socket, headers: {} }); - const failureReason = new errors.ServiceUnavailable(); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(failureReason); + const failureReason = new errors.ConnectionError( + 'unavailable', + securityMock.createApiResponse({ statusCode: 503, body: {} }) + ); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue(failureReason); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request, state)).resolves.toEqual( diff --git a/x-pack/plugins/security/server/authentication/providers/pki.ts b/x-pack/plugins/security/server/authentication/providers/pki.ts index 3171c5ff24ab..a5dc870bf9c8 100644 --- a/x-pack/plugins/security/server/authentication/providers/pki.ts +++ b/x-pack/plugins/security/server/authentication/providers/pki.ts @@ -272,9 +272,15 @@ export class PKIAuthenticationProvider extends BaseAuthenticationProvider { let result: { access_token: string; authentication: AuthenticationInfo }; try { - result = await this.options.client.callAsInternalUser('shield.delegatePKI', { - body: { x509_certificate_chain: certificateChain }, - }); + // We can replace generic `transport.request` with a dedicated API method call once + // https://github.com/elastic/elasticsearch/issues/67189 is resolved. + result = ( + await this.options.client.asInternalUser.transport.request({ + method: 'POST', + path: '/_security/delegate_pki', + body: { x509_certificate_chain: certificateChain }, + }) + ).body as any; } catch (err) { this.logger.debug( `Failed to exchange peer certificate chain to an access token: ${err.message}` @@ -298,7 +304,8 @@ export class PKIAuthenticationProvider extends BaseAuthenticationProvider { } /** - * Obtains the peer certificate chain. Starts from the leaf peer certificate and iterates up to the top-most available certificate + * Obtains the peer certificate chain as an ordered array of base64-encoded (Section 4 of RFC4648 - not base64url-encoded) + * DER PKIX certificate values. Starts from the leaf peer certificate and iterates up to the top-most available certificate * authority using `issuerCertificate` certificate property. THe iteration is stopped only when we detect circular reference * (root/self-signed certificate) or when `issuerCertificate` isn't available (null or empty object). Automatically attempts to * renegotiate the TLS connection once if the peer certificate chain is incomplete. 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 5cba017e4916..48334358bb00 100644 --- a/x-pack/plugins/security/server/authentication/providers/saml.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/saml.test.ts @@ -5,15 +5,13 @@ */ import Boom from '@hapi/boom'; +import { errors } from '@elastic/elasticsearch'; import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; +import { securityMock } from '../../mocks'; import { MockAuthenticationProviderOptions, mockAuthenticationProviderOptions } from './base.mock'; -import { - LegacyElasticsearchErrorHelpers, - ILegacyScopedClusterClient, -} from '../../../../../../src/core/server'; import { AuthenticationResult } from '../authentication_result'; import { DeauthenticationResult } from '../deauthentication_result'; import { SAMLAuthenticationProvider, SAMLLogin } from './saml'; @@ -23,19 +21,17 @@ describe('SAMLAuthenticationProvider', () => { let provider: SAMLAuthenticationProvider; let mockOptions: MockAuthenticationProviderOptions; let mockUser: AuthenticatedUser; - let mockScopedClusterClient: jest.Mocked; + let mockScopedClusterClient: ReturnType< + typeof elasticsearchServiceMock.createScopedClusterClient + >; beforeEach(() => { mockOptions = mockAuthenticationProviderOptions({ name: 'saml' }); - mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); mockUser = mockAuthenticatedUser({ authentication_provider: { type: 'saml', name: 'saml' } }); - mockScopedClusterClient.callAsCurrentUser.mockImplementation(async (method) => { - if (method === 'shield.authenticate') { - return mockUser; - } - - throw new Error(`Unexpected call to ${method}!`); - }); + mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: mockUser }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); provider = new SAMLAuthenticationProvider(mockOptions, { @@ -61,11 +57,15 @@ describe('SAMLAuthenticationProvider', () => { it('gets token and redirects user to requested URL if SAML Response is valid.', async () => { const request = httpServerMock.createKibanaRequest(); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - access_token: 'some-token', - refresh_token: 'some-refresh-token', - authentication: mockUser, - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + access_token: 'some-token', + refresh_token: 'some-refresh-token', + authentication: mockUser, + }, + }) + ); await expect( provider.login( @@ -88,20 +88,25 @@ describe('SAMLAuthenticationProvider', () => { }) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { body: { ids: ['some-request-id'], content: 'saml-response-xml', realm: 'test-realm' } } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/authenticate', + body: { ids: ['some-request-id'], content: 'saml-response-xml', realm: 'test-realm' }, + }); }); it('gets token and redirects user to the requested URL if SAML Response is valid ignoring Relay State.', async () => { const request = httpServerMock.createKibanaRequest(); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - access_token: 'some-token', - refresh_token: 'some-refresh-token', - authentication: mockUser, - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + access_token: 'some-token', + refresh_token: 'some-refresh-token', + authentication: mockUser, + }, + }) + ); provider = new SAMLAuthenticationProvider(mockOptions, { realm: 'test-realm', @@ -132,10 +137,11 @@ describe('SAMLAuthenticationProvider', () => { }) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { body: { ids: ['some-request-id'], content: 'saml-response-xml', realm: 'test-realm' } } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/authenticate', + body: { ids: ['some-request-id'], content: 'saml-response-xml', realm: 'test-realm' }, + }); }); it('fails if SAML Response payload is presented but state does not contain SAML Request token.', async () => { @@ -153,7 +159,7 @@ describe('SAMLAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('fails if realm from state is different from the realm provider is configured with.', async () => { @@ -173,17 +179,21 @@ describe('SAMLAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('redirects to the default location if state contains empty redirect URL.', async () => { const request = httpServerMock.createKibanaRequest(); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - access_token: 'user-initiated-login-token', - refresh_token: 'user-initiated-login-refresh-token', - authentication: mockUser, - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + access_token: 'user-initiated-login-token', + refresh_token: 'user-initiated-login-refresh-token', + authentication: mockUser, + }, + }) + ); await expect( provider.login( @@ -202,20 +212,25 @@ describe('SAMLAuthenticationProvider', () => { }) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { body: { ids: ['some-request-id'], content: 'saml-response-xml', realm: 'test-realm' } } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/authenticate', + body: { ids: ['some-request-id'], content: 'saml-response-xml', realm: 'test-realm' }, + }); }); it('redirects to the default location if state contains empty redirect URL ignoring Relay State.', async () => { const request = httpServerMock.createKibanaRequest(); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - access_token: 'user-initiated-login-token', - refresh_token: 'user-initiated-login-refresh-token', - authentication: mockUser, - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + access_token: 'user-initiated-login-token', + refresh_token: 'user-initiated-login-refresh-token', + authentication: mockUser, + }, + }) + ); provider = new SAMLAuthenticationProvider(mockOptions, { realm: 'test-realm', @@ -242,20 +257,25 @@ describe('SAMLAuthenticationProvider', () => { }) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { body: { ids: ['some-request-id'], content: 'saml-response-xml', realm: 'test-realm' } } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/authenticate', + body: { ids: ['some-request-id'], content: 'saml-response-xml', realm: 'test-realm' }, + }); }); it('redirects to the default location if state is not presented.', async () => { const request = httpServerMock.createKibanaRequest(); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - access_token: 'idp-initiated-login-token', - refresh_token: 'idp-initiated-login-refresh-token', - authentication: mockUser, - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + access_token: 'idp-initiated-login-token', + refresh_token: 'idp-initiated-login-refresh-token', + authentication: mockUser, + }, + }) + ); await expect( provider.login(request, { @@ -273,17 +293,20 @@ describe('SAMLAuthenticationProvider', () => { }) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' } } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/authenticate', + body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, + }); }); it('fails if SAML Response is rejected.', async () => { const request = httpServerMock.createKibanaRequest(); - const failureReason = new Error('SAML response is stale!'); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 503, body: {} }) + ); + mockOptions.client.asInternalUser.transport.request.mockRejectedValue(failureReason); await expect( provider.login( @@ -297,22 +320,27 @@ describe('SAMLAuthenticationProvider', () => { ) ).resolves.toEqual(AuthenticationResult.failed(failureReason)); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { body: { ids: ['some-request-id'], content: 'saml-response-xml', realm: 'test-realm' } } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/authenticate', + body: { ids: ['some-request-id'], content: 'saml-response-xml', realm: 'test-realm' }, + }); }); describe('IdP initiated login', () => { beforeEach(() => { mockOptions.basePath.get.mockReturnValue(mockOptions.basePath.serverBasePath); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - username: 'user', - access_token: 'valid-token', - refresh_token: 'valid-refresh-token', - authentication: mockUser, - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + username: 'user', + access_token: 'valid-token', + refresh_token: 'valid-refresh-token', + authentication: mockUser, + }, + }) + ); provider = new SAMLAuthenticationProvider(mockOptions, { realm: 'test-realm', @@ -428,8 +456,10 @@ describe('SAMLAuthenticationProvider', () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); const authorization = 'Bearer some-valid-token'; - const failureReason = new Error('SAML response is invalid!'); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 503, body: {} }) + ); + mockOptions.client.asInternalUser.transport.request.mockRejectedValue(failureReason); await expect( provider.login( @@ -444,12 +474,11 @@ describe('SAMLAuthenticationProvider', () => { ).resolves.toEqual(AuthenticationResult.notHandled()); expect(mockOptions.client.asScoped).toHaveBeenCalledWith({ headers: { authorization } }); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { - body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, - } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/authenticate', + body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, + }); }); it('fails if fails to invalidate existing access/refresh tokens.', async () => { @@ -461,12 +490,16 @@ describe('SAMLAuthenticationProvider', () => { }; const authorization = `Bearer ${state.accessToken}`; - mockOptions.client.callAsInternalUser.mockResolvedValue({ - username: 'user', - access_token: 'new-valid-token', - refresh_token: 'new-valid-refresh-token', - authentication: mockUser, - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + username: 'user', + access_token: 'new-valid-token', + refresh_token: 'new-valid-refresh-token', + authentication: mockUser, + }, + }) + ); const failureReason = new Error('Failed to invalidate token!'); mockOptions.tokens.invalidate.mockRejectedValue(failureReason); @@ -480,12 +513,11 @@ describe('SAMLAuthenticationProvider', () => { ).resolves.toEqual(AuthenticationResult.failed(failureReason)); expect(mockOptions.client.asScoped).toHaveBeenCalledWith({ headers: { authorization } }); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { - body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, - } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/authenticate', + body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, + }); expect(mockOptions.tokens.invalidate).toHaveBeenCalledTimes(1); expect(mockOptions.tokens.invalidate).toHaveBeenCalledWith({ @@ -498,14 +530,20 @@ describe('SAMLAuthenticationProvider', () => { [ 'current session is valid', Promise.resolve( - mockAuthenticatedUser({ authentication_provider: { type: 'saml', name: 'saml' } }) + securityMock.createApiResponse({ + body: mockAuthenticatedUser({ + authentication_provider: { type: 'saml', name: 'saml' }, + }), + }) ), ], [ 'current session is is expired', - Promise.reject(LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error())), + Promise.reject( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) + ), ], - ] as Array<[string, Promise]>) { + ] as Array<[string, any]>) { it(`redirects to the home page if ${description}.`, async () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); const state = { @@ -516,19 +554,19 @@ describe('SAMLAuthenticationProvider', () => { const authorization = `Bearer ${state.accessToken}`; // The first call is made using tokens from existing session. - mockScopedClusterClient.callAsCurrentUser.mockImplementationOnce(() => response); - // The second call is made using new tokens. - mockScopedClusterClient.callAsCurrentUser.mockImplementationOnce(() => - Promise.resolve(mockUser) + mockScopedClusterClient.asCurrentUser.security.authenticate.mockImplementationOnce( + () => response + ); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + username: 'user', + access_token: 'new-valid-token', + refresh_token: 'new-valid-refresh-token', + authentication: mockUser, + }, + }) ); - - mockOptions.client.callAsInternalUser.mockResolvedValue({ - username: 'user', - access_token: 'new-valid-token', - refresh_token: 'new-valid-refresh-token', - authentication: mockUser, - }); - mockOptions.tokens.invalidate.mockResolvedValue(undefined); await expect( @@ -549,12 +587,11 @@ describe('SAMLAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).toHaveBeenCalledWith({ headers: { authorization } }); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { - body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, - } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/authenticate', + body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, + }); expect(mockOptions.tokens.invalidate).toHaveBeenCalledTimes(1); expect(mockOptions.tokens.invalidate).toHaveBeenCalledWith({ @@ -573,13 +610,19 @@ describe('SAMLAuthenticationProvider', () => { const authorization = `Bearer ${state.accessToken}`; // The first call is made using tokens from existing session. - mockScopedClusterClient.callAsCurrentUser.mockImplementationOnce(() => response); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - username: 'user', - access_token: 'new-valid-token', - refresh_token: 'new-valid-refresh-token', - authentication: mockUser, - }); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockImplementationOnce( + () => response + ); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + username: 'user', + access_token: 'new-valid-token', + refresh_token: 'new-valid-refresh-token', + authentication: mockUser, + }, + }) + ); mockOptions.tokens.invalidate.mockResolvedValue(undefined); @@ -610,12 +653,11 @@ describe('SAMLAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).toHaveBeenCalledWith({ headers: { authorization } }); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith( - 'shield.samlAuthenticate', - { - body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, - } - ); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/authenticate', + body: { ids: [], content: 'saml-response-xml', realm: 'test-realm' }, + }); expect(mockOptions.tokens.invalidate).toHaveBeenCalledTimes(1); expect(mockOptions.tokens.invalidate).toHaveBeenCalledWith({ @@ -641,16 +683,20 @@ describe('SAMLAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('redirects requests to the IdP remembering redirect URL with existing state.', async () => { const request = httpServerMock.createKibanaRequest(); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - id: 'some-request-id', - redirect: 'https://idp-host/path/login?SAMLRequest=some%20request%20', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + id: 'some-request-id', + redirect: 'https://idp-host/path/login?SAMLRequest=some%20request%20', + }, + }) + ); await expect( provider.login( @@ -674,7 +720,9 @@ describe('SAMLAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.samlPrepare', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/prepare', body: { realm: 'test-realm' }, }); @@ -684,10 +732,14 @@ describe('SAMLAuthenticationProvider', () => { it('redirects requests to the IdP remembering redirect URL without state.', async () => { const request = httpServerMock.createKibanaRequest(); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - id: 'some-request-id', - redirect: 'https://idp-host/path/login?SAMLRequest=some%20request%20', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { + id: 'some-request-id', + redirect: 'https://idp-host/path/login?SAMLRequest=some%20request%20', + }, + }) + ); await expect( provider.login( @@ -711,7 +763,9 @@ describe('SAMLAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.samlPrepare', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/prepare', body: { realm: 'test-realm' }, }); @@ -721,8 +775,10 @@ describe('SAMLAuthenticationProvider', () => { it('fails if SAML request preparation fails.', async () => { const request = httpServerMock.createKibanaRequest(); - const failureReason = new Error('Realm is misconfigured!'); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 401, body: {} }) + ); + mockOptions.client.asInternalUser.transport.request.mockRejectedValue(failureReason); await expect( provider.login( @@ -735,7 +791,9 @@ describe('SAMLAuthenticationProvider', () => { ) ).resolves.toEqual(AuthenticationResult.failed(failureReason)); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.samlPrepare', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/prepare', body: { realm: 'test-realm' }, }); }); @@ -791,11 +849,6 @@ describe('SAMLAuthenticationProvider', () => { it('redirects non-AJAX request that can not be authenticated to the "capture URL" page.', async () => { const request = httpServerMock.createKibanaRequest({ path: '/s/foo/some-path' }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - id: 'some-request-id', - redirect: 'https://idp-host/path/login?SAMLRequest=some%20request%20', - }); - await expect(provider.authenticate(request)).resolves.toEqual( AuthenticationResult.redirectTo( '/mock-server-basepath/internal/security/capture-url?next=%2Fmock-server-basepath%2Fs%2Ffoo%2Fsome-path&providerType=saml&providerName=saml', @@ -803,7 +856,7 @@ describe('SAMLAuthenticationProvider', () => { ) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('succeeds if state contains a valid token.', async () => { @@ -833,11 +886,13 @@ describe('SAMLAuthenticationProvider', () => { }; const authorization = `Bearer ${state.accessToken}`; - const failureReason = { statusCode: 500, message: 'Token is not valid!' }; - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 500, body: {} }) + ); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue(failureReason); await expect(provider.authenticate(request, state)).resolves.toEqual( - AuthenticationResult.failed(failureReason as any) + AuthenticationResult.failed(failureReason) ); expect(mockOptions.client.asScoped).toHaveBeenCalledWith({ headers: { authorization } }); @@ -853,8 +908,8 @@ describe('SAMLAuthenticationProvider', () => { realm: 'test-realm', }; - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.tokens.refresh.mockResolvedValue({ @@ -889,8 +944,8 @@ describe('SAMLAuthenticationProvider', () => { }; const authorization = `Bearer ${state.accessToken}`; - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); const refreshFailureReason = { @@ -920,8 +975,8 @@ describe('SAMLAuthenticationProvider', () => { }; const authorization = `Bearer ${state.accessToken}`; - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.tokens.refresh.mockResolvedValue(null); @@ -949,8 +1004,8 @@ describe('SAMLAuthenticationProvider', () => { }; const authorization = `Bearer ${state.accessToken}`; - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.tokens.refresh.mockResolvedValue(null); @@ -976,8 +1031,8 @@ describe('SAMLAuthenticationProvider', () => { }; const authorization = `Bearer ${state.accessToken}`; - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.tokens.refresh.mockResolvedValue(null); @@ -994,7 +1049,7 @@ describe('SAMLAuthenticationProvider', () => { expect(mockOptions.client.asScoped).toHaveBeenCalledWith({ headers: { authorization } }); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('fails if realm from state is different from the realm provider is configured with.', async () => { @@ -1015,7 +1070,7 @@ describe('SAMLAuthenticationProvider', () => { await expect(provider.logout(request)).resolves.toEqual(DeauthenticationResult.notHandled()); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('redirects to logged out view if state is `null` or does not include access token.', async () => { @@ -1028,7 +1083,7 @@ describe('SAMLAuthenticationProvider', () => { DeauthenticationResult.redirectTo(mockOptions.urls.loggedOut(request)) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('fails if SAML logout call fails.', async () => { @@ -1036,8 +1091,10 @@ describe('SAMLAuthenticationProvider', () => { const accessToken = 'x-saml-token'; const refreshToken = 'x-saml-refresh-token'; - const failureReason = new Error('Realm is misconfigured!'); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 500, body: {} }) + ); + mockOptions.client.asInternalUser.transport.request.mockRejectedValue(failureReason); await expect( provider.logout(request, { @@ -1047,8 +1104,10 @@ describe('SAMLAuthenticationProvider', () => { }) ).resolves.toEqual(DeauthenticationResult.failed(failureReason)); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.samlLogout', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/logout', body: { token: accessToken, refresh_token: refreshToken }, }); }); @@ -1056,15 +1115,19 @@ describe('SAMLAuthenticationProvider', () => { it('fails if SAML invalidate call fails.', async () => { const request = httpServerMock.createKibanaRequest({ query: { SAMLRequest: 'xxx yyy' } }); - const failureReason = new Error('Realm is misconfigured!'); - mockOptions.client.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 500, body: {} }) + ); + mockOptions.client.asInternalUser.transport.request.mockRejectedValue(failureReason); await expect(provider.logout(request)).resolves.toEqual( DeauthenticationResult.failed(failureReason) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.samlInvalidate', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/invalidate', body: { queryString: 'SAMLRequest=xxx%20yyy', realm: 'test-realm' }, }); }); @@ -1074,7 +1137,9 @@ describe('SAMLAuthenticationProvider', () => { const accessToken = 'x-saml-token'; const refreshToken = 'x-saml-refresh-token'; - mockOptions.client.callAsInternalUser.mockResolvedValue({ redirect: null }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ body: { redirect: null } }) + ); await expect( provider.logout(request, { @@ -1084,8 +1149,10 @@ describe('SAMLAuthenticationProvider', () => { }) ).resolves.toEqual(DeauthenticationResult.redirectTo(mockOptions.urls.loggedOut(request))); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.samlLogout', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/logout', body: { token: accessToken, refresh_token: refreshToken }, }); }); @@ -1095,7 +1162,9 @@ describe('SAMLAuthenticationProvider', () => { const accessToken = 'x-saml-token'; const refreshToken = 'x-saml-refresh-token'; - mockOptions.client.callAsInternalUser.mockResolvedValue({ redirect: undefined }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ body: { redirect: undefined } }) + ); await expect( provider.logout(request, { @@ -1105,8 +1174,10 @@ describe('SAMLAuthenticationProvider', () => { }) ).resolves.toEqual(DeauthenticationResult.redirectTo(mockOptions.urls.loggedOut(request))); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.samlLogout', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/logout', body: { token: accessToken, refresh_token: refreshToken }, }); }); @@ -1118,7 +1189,9 @@ describe('SAMLAuthenticationProvider', () => { const accessToken = 'x-saml-token'; const refreshToken = 'x-saml-refresh-token'; - mockOptions.client.callAsInternalUser.mockResolvedValue({ redirect: null }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ body: { redirect: null } }) + ); await expect( provider.logout(request, { @@ -1128,8 +1201,10 @@ describe('SAMLAuthenticationProvider', () => { }) ).resolves.toEqual(DeauthenticationResult.redirectTo(mockOptions.urls.loggedOut(request))); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.samlLogout', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/logout', body: { token: accessToken, refresh_token: refreshToken }, }); }); @@ -1137,7 +1212,9 @@ describe('SAMLAuthenticationProvider', () => { it('relies on SAML invalidate call even if access token is presented.', async () => { const request = httpServerMock.createKibanaRequest({ query: { SAMLRequest: 'xxx yyy' } }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ redirect: null }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ body: { redirect: null } }) + ); await expect( provider.logout(request, { @@ -1147,8 +1224,10 @@ describe('SAMLAuthenticationProvider', () => { }) ).resolves.toEqual(DeauthenticationResult.redirectTo(mockOptions.urls.loggedOut(request))); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.samlInvalidate', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/invalidate', body: { queryString: 'SAMLRequest=xxx%20yyy', realm: 'test-realm' }, }); }); @@ -1156,14 +1235,18 @@ describe('SAMLAuthenticationProvider', () => { it('redirects to `loggedOut` URL if `redirect` field in SAML invalidate response is null.', async () => { const request = httpServerMock.createKibanaRequest({ query: { SAMLRequest: 'xxx yyy' } }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ redirect: null }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ body: { redirect: null } }) + ); await expect(provider.logout(request)).resolves.toEqual( DeauthenticationResult.redirectTo(mockOptions.urls.loggedOut(request)) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.samlInvalidate', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/invalidate', body: { queryString: 'SAMLRequest=xxx%20yyy', realm: 'test-realm' }, }); }); @@ -1171,14 +1254,18 @@ describe('SAMLAuthenticationProvider', () => { it('redirects to `loggedOut` URL if `redirect` field in SAML invalidate response is not defined.', async () => { const request = httpServerMock.createKibanaRequest({ query: { SAMLRequest: 'xxx yyy' } }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ redirect: undefined }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ body: { redirect: undefined } }) + ); await expect(provider.logout(request)).resolves.toEqual( DeauthenticationResult.redirectTo(mockOptions.urls.loggedOut(request)) ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.samlInvalidate', { + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledWith({ + method: 'POST', + path: '/_security/saml/invalidate', body: { queryString: 'SAMLRequest=xxx%20yyy', realm: 'test-realm' }, }); }); @@ -1190,7 +1277,7 @@ describe('SAMLAuthenticationProvider', () => { DeauthenticationResult.redirectTo(mockOptions.urls.loggedOut(request)) ); - expect(mockOptions.client.callAsInternalUser).not.toHaveBeenCalled(); + expect(mockOptions.client.asInternalUser.transport.request).not.toHaveBeenCalled(); }); it('redirects user to the IdP if SLO is supported by IdP in case of SP initiated logout.', async () => { @@ -1198,9 +1285,11 @@ describe('SAMLAuthenticationProvider', () => { const accessToken = 'x-saml-token'; const refreshToken = 'x-saml-refresh-token'; - mockOptions.client.callAsInternalUser.mockResolvedValue({ - redirect: 'http://fake-idp/SLO?SAMLRequest=7zlH37H', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { redirect: 'http://fake-idp/SLO?SAMLRequest=7zlH37H' }, + }) + ); await expect( provider.logout(request, { @@ -1212,15 +1301,17 @@ describe('SAMLAuthenticationProvider', () => { DeauthenticationResult.redirectTo('http://fake-idp/SLO?SAMLRequest=7zlH37H') ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); }); it('redirects user to the IdP if SLO is supported by IdP in case of IdP initiated logout.', async () => { const request = httpServerMock.createKibanaRequest({ query: { SAMLRequest: 'xxx yyy' } }); - mockOptions.client.callAsInternalUser.mockResolvedValue({ - redirect: 'http://fake-idp/SLO?SAMLRequest=7zlH37H', - }); + mockOptions.client.asInternalUser.transport.request.mockResolvedValue( + securityMock.createApiResponse({ + body: { redirect: 'http://fake-idp/SLO?SAMLRequest=7zlH37H' }, + }) + ); await expect( provider.logout(request, { @@ -1232,7 +1323,7 @@ describe('SAMLAuthenticationProvider', () => { DeauthenticationResult.redirectTo('http://fake-idp/SLO?SAMLRequest=7zlH37H') ); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.transport.request).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/security/server/authentication/providers/saml.ts b/x-pack/plugins/security/server/authentication/providers/saml.ts index 34639a849d35..58792727de73 100644 --- a/x-pack/plugins/security/server/authentication/providers/saml.ts +++ b/x-pack/plugins/security/server/authentication/providers/saml.ts @@ -343,13 +343,19 @@ export class SAMLAuthenticationProvider extends BaseAuthenticationProvider { try { // This operation should be performed on behalf of the user with a privilege that normal // user usually doesn't have `cluster:admin/xpack/security/saml/authenticate`. - result = await this.options.client.callAsInternalUser('shield.samlAuthenticate', { - body: { - ids: !isIdPInitiatedLogin ? [stateRequestId] : [], - content: samlResponse, - realm: this.realm, - }, - }); + // We can replace generic `transport.request` with a dedicated API method call once + // https://github.com/elastic/elasticsearch/issues/67189 is resolved. + result = ( + await this.options.client.asInternalUser.transport.request({ + method: 'POST', + path: '/_security/saml/authenticate', + body: { + ids: !isIdPInitiatedLogin ? [stateRequestId] : [], + content: samlResponse, + realm: this.realm, + }, + }) + ).body as any; } catch (err) { this.logger.debug(`Failed to log in with SAML response: ${err.message}`); @@ -541,12 +547,15 @@ export class SAMLAuthenticationProvider extends BaseAuthenticationProvider { try { // This operation should be performed on behalf of the user with a privilege that normal // user usually doesn't have `cluster:admin/xpack/security/saml/prepare`. - const { id: requestId, redirect } = await this.options.client.callAsInternalUser( - 'shield.samlPrepare', - { + // We can replace generic `transport.request` with a dedicated API method call once + // https://github.com/elastic/elasticsearch/issues/67189 is resolved. + const { id: requestId, redirect } = ( + await this.options.client.asInternalUser.transport.request({ + method: 'POST', + path: '/_security/saml/prepare', body: { realm: this.realm }, - } - ); + }) + ).body as any; this.logger.debug('Redirecting to Identity Provider with SAML request.'); @@ -570,9 +579,15 @@ export class SAMLAuthenticationProvider extends BaseAuthenticationProvider { // This operation should be performed on behalf of the user with a privilege that normal // user usually doesn't have `cluster:admin/xpack/security/saml/logout`. - const { redirect } = await this.options.client.callAsInternalUser('shield.samlLogout', { - body: { token: accessToken, refresh_token: refreshToken }, - }); + // We can replace generic `transport.request` with a dedicated API method call once + // https://github.com/elastic/elasticsearch/issues/67189 is resolved. + const { redirect } = ( + await this.options.client.asInternalUser.transport.request({ + method: 'POST', + path: '/_security/saml/logout', + body: { token: accessToken, refresh_token: refreshToken }, + }) + ).body as any; this.logger.debug('User session has been successfully invalidated.'); @@ -589,13 +604,19 @@ export class SAMLAuthenticationProvider extends BaseAuthenticationProvider { // This operation should be performed on behalf of the user with a privilege that normal // user usually doesn't have `cluster:admin/xpack/security/saml/invalidate`. - const { redirect } = await this.options.client.callAsInternalUser('shield.samlInvalidate', { - // Elasticsearch expects `queryString` without leading `?`, so we should strip it with `slice`. - body: { - queryString: request.url.search ? request.url.search.slice(1) : '', - realm: this.realm, - }, - }); + // We can replace generic `transport.request` with a dedicated API method call once + // https://github.com/elastic/elasticsearch/issues/67189 is resolved. + const { redirect } = ( + await this.options.client.asInternalUser.transport.request({ + method: 'POST', + path: '/_security/saml/invalidate', + // Elasticsearch expects `queryString` without leading `?`, so we should strip it with `slice`. + body: { + queryString: request.url.search ? request.url.search.slice(1) : '', + realm: this.realm, + }, + }) + ).body as any; this.logger.debug('User session has been successfully invalidated.'); diff --git a/x-pack/plugins/security/server/authentication/providers/token.test.ts b/x-pack/plugins/security/server/authentication/providers/token.test.ts index 5a600461ef46..ad100ac5be89 100644 --- a/x-pack/plugins/security/server/authentication/providers/token.test.ts +++ b/x-pack/plugins/security/server/authentication/providers/token.test.ts @@ -5,31 +5,27 @@ */ import Boom from '@hapi/boom'; -import { errors } from 'elasticsearch'; +import { errors } from '@elastic/elasticsearch'; import { elasticsearchServiceMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; +import { securityMock } from '../../mocks'; import { MockAuthenticationProviderOptions, mockAuthenticationProviderOptions } from './base.mock'; -import { - LegacyElasticsearchErrorHelpers, - ILegacyClusterClient, - ScopeableRequest, -} from '../../../../../../src/core/server'; +import { ScopeableRequest } from '../../../../../../src/core/server'; import { AuthenticationResult } from '../authentication_result'; import { DeauthenticationResult } from '../deauthentication_result'; import { TokenAuthenticationProvider } from './token'; function expectAuthenticateCall( - mockClusterClient: jest.Mocked, + mockClusterClient: ReturnType, scopeableRequest: ScopeableRequest ) { expect(mockClusterClient.asScoped).toHaveBeenCalledTimes(1); expect(mockClusterClient.asScoped).toHaveBeenCalledWith(scopeableRequest); const mockScopedClusterClient = mockClusterClient.asScoped.mock.results[0].value; - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledTimes(1); - expect(mockScopedClusterClient.callAsCurrentUser).toHaveBeenCalledWith('shield.authenticate'); + expect(mockScopedClusterClient.asCurrentUser.security.authenticate).toHaveBeenCalledTimes(1); } describe('TokenAuthenticationProvider', () => { @@ -51,11 +47,15 @@ describe('TokenAuthenticationProvider', () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; const authorization = `Bearer ${tokenPair.accessToken}`; - mockOptions.client.callAsInternalUser.mockResolvedValue({ - access_token: tokenPair.accessToken, - refresh_token: tokenPair.refreshToken, - authentication: user, - }); + mockOptions.client.asInternalUser.security.getToken.mockResolvedValue( + securityMock.createApiResponse({ + body: { + access_token: tokenPair.accessToken, + refresh_token: tokenPair.refreshToken, + authentication: user, + }, + }) + ); await expect(provider.login(request, credentials)).resolves.toEqual( AuthenticationResult.succeeded( @@ -65,8 +65,8 @@ describe('TokenAuthenticationProvider', () => { ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.getAccessToken', { + expect(mockOptions.client.asInternalUser.security.getToken).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.security.getToken).toHaveBeenCalledWith({ body: { grant_type: 'password', ...credentials }, }); }); @@ -75,17 +75,18 @@ describe('TokenAuthenticationProvider', () => { const request = httpServerMock.createKibanaRequest(); const credentials = { username: 'user', password: 'password' }; - const authenticationError = new Error('Invalid credentials'); - mockOptions.client.callAsInternalUser.mockRejectedValue(authenticationError); + const authenticationError = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 500, body: {} }) + ); + mockOptions.client.asInternalUser.security.getToken.mockRejectedValue(authenticationError); await expect(provider.login(request, credentials)).resolves.toEqual( AuthenticationResult.failed(authenticationError) ); expect(mockOptions.client.asScoped).not.toHaveBeenCalled(); - - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockOptions.client.callAsInternalUser).toHaveBeenCalledWith('shield.getAccessToken', { + expect(mockOptions.client.asInternalUser.security.getToken).toHaveBeenCalledTimes(1); + expect(mockOptions.client.asInternalUser.security.getToken).toHaveBeenCalledWith({ body: { grant_type: 'password', ...credentials }, }); @@ -158,8 +159,10 @@ describe('TokenAuthenticationProvider', () => { const user = mockAuthenticatedUser(); const authorization = `Bearer ${tokenPair.accessToken}`; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockResolvedValue(user); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockResolvedValue( + securityMock.createApiResponse({ body: user }) + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request, tokenPair)).resolves.toEqual( @@ -179,9 +182,9 @@ describe('TokenAuthenticationProvider', () => { const request = httpServerMock.createKibanaRequest(); const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); @@ -212,9 +215,13 @@ describe('TokenAuthenticationProvider', () => { const request = httpServerMock.createKibanaRequest({ headers: {} }); const authorization = `Bearer ${tokenPair.accessToken}`; - const authenticationError = new errors.InternalServerError('something went wrong'); - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue(authenticationError); + const authenticationError = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 500, body: {} }) + ); + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + authenticationError + ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); await expect(provider.authenticate(request, tokenPair)).resolves.toEqual( @@ -231,13 +238,15 @@ describe('TokenAuthenticationProvider', () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; const authorization = `Bearer ${tokenPair.accessToken}`; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); - const refreshError = new errors.InternalServerError('failed to refresh token'); + const refreshError = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 500, body: {} }) + ); mockOptions.tokens.refresh.mockRejectedValue(refreshError); await expect(provider.authenticate(request, tokenPair)).resolves.toEqual( @@ -257,9 +266,9 @@ describe('TokenAuthenticationProvider', () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; const authorization = `Bearer ${tokenPair.accessToken}`; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); @@ -288,9 +297,9 @@ describe('TokenAuthenticationProvider', () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; const authorization = `Bearer ${tokenPair.accessToken}`; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); @@ -319,9 +328,9 @@ describe('TokenAuthenticationProvider', () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; const authorization = `Bearer ${tokenPair.accessToken}`; - const mockScopedClusterClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - mockScopedClusterClient.callAsCurrentUser.mockRejectedValue( - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()) + const mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient(); + mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 401, body: {} })) ); mockOptions.client.asScoped.mockReturnValue(mockScopedClusterClient); diff --git a/x-pack/plugins/security/server/authentication/providers/token.ts b/x-pack/plugins/security/server/authentication/providers/token.ts index 67c2d244e75a..3afbc25122a2 100644 --- a/x-pack/plugins/security/server/authentication/providers/token.ts +++ b/x-pack/plugins/security/server/authentication/providers/token.ts @@ -7,6 +7,8 @@ import Boom from '@hapi/boom'; import { KibanaRequest } from '../../../../../../src/core/server'; import { NEXT_URL_QUERY_STRING_PARAMETER } from '../../../common/constants'; +import { AuthenticationInfo } from '../../elasticsearch'; +import { getDetailedErrorMessage } from '../../errors'; import { AuthenticationResult } from '../authentication_result'; import { DeauthenticationResult } from '../deauthentication_result'; import { canRedirectRequest } from '../can_redirect_request'; @@ -65,9 +67,13 @@ export class TokenAuthenticationProvider extends BaseAuthenticationProvider { access_token: accessToken, refresh_token: refreshToken, authentication: authenticationInfo, - } = await this.options.client.callAsInternalUser('shield.getAccessToken', { - body: { grant_type: 'password', username, password }, - }); + } = ( + await this.options.client.asInternalUser.security.getToken<{ + access_token: string; + refresh_token: string; + authentication: AuthenticationInfo; + }>({ body: { grant_type: 'password', username, password } }) + ).body; this.logger.debug('Get token API request to Elasticsearch successful'); return AuthenticationResult.succeeded( @@ -80,7 +86,7 @@ export class TokenAuthenticationProvider extends BaseAuthenticationProvider { } ); } catch (err) { - this.logger.debug(`Failed to perform a login: ${err.message}`); + this.logger.debug(`Failed to perform a login: ${getDetailedErrorMessage(err)}`); return AuthenticationResult.failed(err); } } diff --git a/x-pack/plugins/security/server/authentication/tokens.test.ts b/x-pack/plugins/security/server/authentication/tokens.test.ts index 18fdcf8608d2..eb439d74a46d 100644 --- a/x-pack/plugins/security/server/authentication/tokens.test.ts +++ b/x-pack/plugins/security/server/authentication/tokens.test.ts @@ -4,25 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { errors } from 'elasticsearch'; +import { errors } from '@elastic/elasticsearch'; +import { DeeplyMockedKeys } from '@kbn/utility-types/jest'; import { elasticsearchServiceMock, loggingSystemMock } from '../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock'; +import { securityMock } from '../mocks'; -import { - ILegacyClusterClient, - LegacyElasticsearchErrorHelpers, -} from '../../../../../src/core/server'; +import { ElasticsearchClient } from '../../../../../src/core/server'; import { Tokens } from './tokens'; describe('Tokens', () => { let tokens: Tokens; - let mockClusterClient: jest.Mocked; + let mockElasticsearchClient: DeeplyMockedKeys; beforeEach(() => { - mockClusterClient = elasticsearchServiceMock.createLegacyClusterClient(); + mockElasticsearchClient = elasticsearchServiceMock.createElasticsearchClient(); const tokensOptions = { - client: mockClusterClient, + client: mockElasticsearchClient, logger: loggingSystemMock.create().get(), }; @@ -33,9 +32,24 @@ describe('Tokens', () => { const nonExpirationErrors = [ {}, new Error(), - new errors.InternalServerError(), - new errors.Forbidden(), + new errors.NoLivingConnectionsError( + 'Server is not available', + securityMock.createApiResponse({ body: {} }) + ), + new errors.ResponseError( + securityMock.createApiResponse({ + statusCode: 403, + body: { error: { reason: 'forbidden' } }, + }) + ), { statusCode: 500, body: { error: { reason: 'some unknown reason' } } }, + new errors.NoLivingConnectionsError( + 'Server is not available', + securityMock.createApiResponse({ + statusCode: 500, + body: { error: { reason: 'some unknown reason' } }, + }) + ), ]; for (const error of nonExpirationErrors) { expect(Tokens.isAccessTokenExpiredError(error)).toBe(false); @@ -43,8 +57,10 @@ describe('Tokens', () => { const expirationErrors = [ { statusCode: 401 }, - LegacyElasticsearchErrorHelpers.decorateNotAuthorizedError(new Error()), - new errors.AuthenticationException(), + securityMock.createApiResponse({ + statusCode: 401, + body: { error: { reason: 'unauthenticated' } }, + }), ]; for (const error of expirationErrors) { expect(Tokens.isAccessTokenExpiredError(error)).toBe(true); @@ -55,25 +71,30 @@ describe('Tokens', () => { const refreshToken = 'some-refresh-token'; it('throws if API call fails with unknown reason', async () => { - const refreshFailureReason = new errors.ServiceUnavailable('Server is not available'); - mockClusterClient.callAsInternalUser.mockRejectedValue(refreshFailureReason); + const refreshFailureReason = new errors.NoLivingConnectionsError( + 'Server is not available', + securityMock.createApiResponse({ body: {} }) + ); + mockElasticsearchClient.security.getToken.mockRejectedValue(refreshFailureReason); await expect(tokens.refresh(refreshToken)).rejects.toBe(refreshFailureReason); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('shield.getAccessToken', { + expect(mockElasticsearchClient.security.getToken).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.security.getToken).toHaveBeenCalledWith({ body: { grant_type: 'refresh_token', refresh_token: refreshToken }, }); }); it('returns `null` if refresh token is not valid', async () => { - const refreshFailureReason = new errors.BadRequest(); - mockClusterClient.callAsInternalUser.mockRejectedValue(refreshFailureReason); + const refreshFailureReason = new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 400, body: {} }) + ); + mockElasticsearchClient.security.getToken.mockRejectedValue(refreshFailureReason); await expect(tokens.refresh(refreshToken)).resolves.toBe(null); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('shield.getAccessToken', { + expect(mockElasticsearchClient.security.getToken).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.security.getToken).toHaveBeenCalledWith({ body: { grant_type: 'refresh_token', refresh_token: refreshToken }, }); }); @@ -81,19 +102,23 @@ describe('Tokens', () => { it('returns token pair if refresh API call succeeds', async () => { const authenticationInfo = mockAuthenticatedUser(); const tokenPair = { accessToken: 'access-token', refreshToken: 'refresh-token' }; - mockClusterClient.callAsInternalUser.mockResolvedValue({ - access_token: tokenPair.accessToken, - refresh_token: tokenPair.refreshToken, - authentication: authenticationInfo, - }); + mockElasticsearchClient.security.getToken.mockResolvedValue( + securityMock.createApiResponse({ + body: { + access_token: tokenPair.accessToken, + refresh_token: tokenPair.refreshToken, + authentication: authenticationInfo, + }, + }) + ); await expect(tokens.refresh(refreshToken)).resolves.toEqual({ authenticationInfo, ...tokenPair, }); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('shield.getAccessToken', { + expect(mockElasticsearchClient.security.getToken).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.security.getToken).toHaveBeenCalledWith({ body: { grant_type: 'refresh_token', refresh_token: refreshToken }, }); }); @@ -101,158 +126,165 @@ describe('Tokens', () => { describe('invalidate()', () => { for (const [description, failureReason] of [ - ['an unknown error', new Error('failed to delete token')], - ['a 404 error without body', { statusCode: 404 }], + [ + 'an unknown error', + new errors.ResponseError( + securityMock.createApiResponse( + securityMock.createApiResponse({ body: { message: 'failed to delete token' } }) + ) + ), + ], + [ + 'a 404 error without body', + new errors.ResponseError( + securityMock.createApiResponse( + securityMock.createApiResponse({ statusCode: 404, body: {} }) + ) + ), + ], ] as Array<[string, object]>) { it(`throws if call to delete access token responds with ${description}`, async () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; - mockClusterClient.callAsInternalUser.mockImplementation((methodName, args: any) => { + mockElasticsearchClient.security.invalidateToken.mockImplementation((args: any) => { if (args && args.body && args.body.token) { - return Promise.reject(failureReason); + return Promise.reject(failureReason) as any; } - return Promise.resolve({ invalidated_tokens: 1 }); + return Promise.resolve( + securityMock.createApiResponse({ body: { invalidated_tokens: 1 } }) + ) as any; }); await expect(tokens.invalidate(tokenPair)).rejects.toBe(failureReason); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { - body: { token: tokenPair.accessToken }, - } - ); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { - body: { refresh_token: tokenPair.refreshToken }, - } - ); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledTimes(2); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { token: tokenPair.accessToken }, + }); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { refresh_token: tokenPair.refreshToken }, + }); }); it(`throws if call to delete refresh token responds with ${description}`, async () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; - mockClusterClient.callAsInternalUser.mockImplementation((methodName, args: any) => { + mockElasticsearchClient.security.invalidateToken.mockImplementation((args: any) => { if (args && args.body && args.body.refresh_token) { - return Promise.reject(failureReason); + return Promise.reject(failureReason) as any; } - return Promise.resolve({ invalidated_tokens: 1 }); + return Promise.resolve( + securityMock.createApiResponse({ body: { invalidated_tokens: 1 } }) + ) as any; }); await expect(tokens.invalidate(tokenPair)).rejects.toBe(failureReason); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { - body: { token: tokenPair.accessToken }, - } - ); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { - body: { refresh_token: tokenPair.refreshToken }, - } - ); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledTimes(2); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { token: tokenPair.accessToken }, + }); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { refresh_token: tokenPair.refreshToken }, + }); }); } it('invalidates all provided tokens', async () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; - mockClusterClient.callAsInternalUser.mockResolvedValue({ invalidated_tokens: 1 }); + mockElasticsearchClient.security.invalidateToken.mockResolvedValue( + securityMock.createApiResponse({ body: { invalidated_tokens: 1 } }) + ); await expect(tokens.invalidate(tokenPair)).resolves.toBe(undefined); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { token: tokenPair.accessToken } } - ); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { refresh_token: tokenPair.refreshToken } } - ); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledTimes(2); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { token: tokenPair.accessToken }, + }); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { refresh_token: tokenPair.refreshToken }, + }); }); it('invalidates only access token if only access token is provided', async () => { const tokenPair = { accessToken: 'foo' }; - mockClusterClient.callAsInternalUser.mockResolvedValue({ invalidated_tokens: 1 }); + mockElasticsearchClient.security.invalidateToken.mockResolvedValue( + securityMock.createApiResponse({ body: { invalidated_tokens: 1 } }) + ); await expect(tokens.invalidate(tokenPair)).resolves.toBe(undefined); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { token: tokenPair.accessToken } } - ); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { token: tokenPair.accessToken }, + }); }); it('invalidates only refresh token if only refresh token is provided', async () => { const tokenPair = { refreshToken: 'foo' }; - mockClusterClient.callAsInternalUser.mockResolvedValue({ invalidated_tokens: 1 }); + mockElasticsearchClient.security.invalidateToken.mockResolvedValue( + securityMock.createApiResponse({ body: { invalidated_tokens: 1 } }) + ); await expect(tokens.invalidate(tokenPair)).resolves.toBe(undefined); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { refresh_token: tokenPair.refreshToken } } - ); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { refresh_token: tokenPair.refreshToken }, + }); }); for (const [description, response] of [ - ['none of the tokens were invalidated', Promise.resolve({ invalidated_tokens: 0 })], + [ + 'none of the tokens were invalidated', + Promise.resolve(securityMock.createApiResponse({ body: { invalidated_tokens: 0 } })), + ], [ '404 error is returned', - Promise.reject({ statusCode: 404, body: { invalidated_tokens: 0 } }), + Promise.resolve( + securityMock.createApiResponse({ statusCode: 404, body: { invalidated_tokens: 0 } }) + ), ], - ] as Array<[string, Promise]>) { + ] as Array<[string, any]>) { it(`does not fail if ${description}`, async () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; - mockClusterClient.callAsInternalUser.mockImplementation(() => response); + mockElasticsearchClient.security.invalidateToken.mockImplementation(() => response); await expect(tokens.invalidate(tokenPair)).resolves.toBe(undefined); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { - body: { token: tokenPair.accessToken }, - } - ); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { - body: { refresh_token: tokenPair.refreshToken }, - } - ); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledTimes(2); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { token: tokenPair.accessToken }, + }); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { refresh_token: tokenPair.refreshToken }, + }); }); } it('does not fail if more than one token per access or refresh token were invalidated', async () => { const tokenPair = { accessToken: 'foo', refreshToken: 'bar' }; - mockClusterClient.callAsInternalUser.mockResolvedValue({ invalidated_tokens: 5 }); + mockElasticsearchClient.security.invalidateToken.mockResolvedValue( + securityMock.createApiResponse({ body: { invalidated_tokens: 5 } }) + ); await expect(tokens.invalidate(tokenPair)).resolves.toBe(undefined); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { token: tokenPair.accessToken } } - ); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith( - 'shield.deleteAccessToken', - { body: { refresh_token: tokenPair.refreshToken } } - ); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledTimes(2); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { token: tokenPair.accessToken }, + }); + expect(mockElasticsearchClient.security.invalidateToken).toHaveBeenCalledWith({ + body: { refresh_token: tokenPair.refreshToken }, + }); }); }); }); diff --git a/x-pack/plugins/security/server/authentication/tokens.ts b/x-pack/plugins/security/server/authentication/tokens.ts index a435452ae112..7bee3dfe1c5a 100644 --- a/x-pack/plugins/security/server/authentication/tokens.ts +++ b/x-pack/plugins/security/server/authentication/tokens.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ILegacyClusterClient, Logger } from '../../../../../src/core/server'; +import type { ElasticsearchClient, Logger } from '../../../../../src/core/server'; import type { AuthenticationInfo } from '../elasticsearch'; import { getErrorStatusCode } from '../errors'; @@ -42,9 +42,7 @@ export class Tokens { */ private readonly logger: Logger; - constructor( - private readonly options: Readonly<{ client: ILegacyClusterClient; logger: Logger }> - ) { + constructor(private readonly options: Readonly<{ client: ElasticsearchClient; logger: Logger }>) { this.logger = options.logger; } @@ -59,9 +57,13 @@ export class Tokens { access_token: accessToken, refresh_token: refreshToken, authentication: authenticationInfo, - } = await this.options.client.callAsInternalUser('shield.getAccessToken', { - body: { grant_type: 'refresh_token', refresh_token: existingRefreshToken }, - }); + } = ( + await this.options.client.security.getToken<{ + access_token: string; + refresh_token: string; + authentication: AuthenticationInfo; + }>({ body: { grant_type: 'refresh_token', refresh_token: existingRefreshToken } }) + ).body; this.logger.debug('Access token has been successfully refreshed.'); @@ -108,10 +110,10 @@ export class Tokens { let invalidatedTokensCount; try { invalidatedTokensCount = ( - await this.options.client.callAsInternalUser('shield.deleteAccessToken', { + await this.options.client.security.invalidateToken<{ invalidated_tokens: number }>({ body: { refresh_token: refreshToken }, }) - ).invalidated_tokens; + ).body.invalidated_tokens; } catch (err) { this.logger.debug(`Failed to invalidate refresh token: ${err.message}`); @@ -140,10 +142,10 @@ export class Tokens { let invalidatedTokensCount; try { invalidatedTokensCount = ( - await this.options.client.callAsInternalUser('shield.deleteAccessToken', { + await this.options.client.security.invalidateToken<{ invalidated_tokens: number }>({ body: { token: accessToken }, }) - ).invalidated_tokens; + ).body.invalidated_tokens; } catch (err) { this.logger.debug(`Failed to invalidate access token: ${err.message}`); diff --git a/x-pack/plugins/security/server/elasticsearch/elasticsearch_client_plugin.ts b/x-pack/plugins/security/server/elasticsearch/elasticsearch_client_plugin.ts deleted file mode 100644 index 0aaad251ae64..000000000000 --- a/x-pack/plugins/security/server/elasticsearch/elasticsearch_client_plugin.ts +++ /dev/null @@ -1,214 +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. - */ - -export function elasticsearchClientPlugin(Client: any, config: unknown, components: any) { - const ca = components.clientAction.factory; - - Client.prototype.shield = components.clientAction.namespaceFactory(); - const shield = Client.prototype.shield.prototype; - - /** - * Perform a [shield.authenticate](Retrieve details about the currently authenticated user) request - * - * @param {Object} params - An object with parameters used to carry out this action - */ - shield.authenticate = ca({ - params: {}, - url: { - fmt: '/_security/_authenticate', - }, - }); - - /** - * Asks Elasticsearch to prepare SAML authentication request to be sent to - * the 3rd-party SAML identity provider. - * - * @param {string} [acs] Optional assertion consumer service URL to use for SAML request or URL - * in the Kibana to which identity provider will post SAML response. Based on the ACS Elasticsearch - * will choose the right SAML realm. - * - * @param {string} [realm] Optional name of the Elasticsearch SAML realm to use to handle request. - * - * @returns {{realm: string, id: string, redirect: string}} Object that includes identifier - * of the SAML realm used to prepare authentication request, encrypted request token to be - * sent to Elasticsearch with SAML response and redirect URL to the identity provider that - * will be used to authenticate user. - */ - shield.samlPrepare = ca({ - method: 'POST', - needBody: true, - url: { - fmt: '/_security/saml/prepare', - }, - }); - - /** - * Sends SAML response returned by identity provider to Elasticsearch for validation. - * - * @param {Array.} ids A list of encrypted request tokens returned within SAML - * preparation response. - * @param {string} content SAML response returned by identity provider. - * @param {string} [realm] Optional string used to identify the name of the OpenID Connect realm - * that should be used to authenticate request. - * - * @returns {{username: string, access_token: string, expires_in: number}} Object that - * includes name of the user, access token to use for any consequent requests that - * need to be authenticated and a number of seconds after which access token will expire. - */ - shield.samlAuthenticate = ca({ - method: 'POST', - needBody: true, - url: { - fmt: '/_security/saml/authenticate', - }, - }); - - /** - * Invalidates SAML access token. - * - * @param {string} token SAML access token that needs to be invalidated. - * - * @returns {{redirect?: string}} - */ - shield.samlLogout = ca({ - method: 'POST', - needBody: true, - url: { - fmt: '/_security/saml/logout', - }, - }); - - /** - * Invalidates SAML session based on Logout Request received from the Identity Provider. - * - * @param {string} queryString URL encoded query string provided by Identity Provider. - * @param {string} [acs] Optional assertion consumer service URL to use for SAML request or URL in the - * Kibana to which identity provider will post SAML response. Based on the ACS Elasticsearch - * will choose the right SAML realm to invalidate session. - * @param {string} [realm] Optional name of the Elasticsearch SAML realm to use to handle request. - * - * @returns {{redirect?: string}} - */ - shield.samlInvalidate = ca({ - method: 'POST', - needBody: true, - url: { - fmt: '/_security/saml/invalidate', - }, - }); - - /** - * Asks Elasticsearch to prepare an OpenID Connect authentication request to be sent to - * the 3rd-party OpenID Connect provider. - * - * @param {string} realm The OpenID Connect realm name in Elasticsearch - * - * @returns {{state: string, nonce: string, redirect: string}} Object that includes two opaque parameters that need - * to be sent to Elasticsearch with the OpenID Connect response and redirect URL to the OpenID Connect provider that - * will be used to authenticate user. - */ - shield.oidcPrepare = ca({ - method: 'POST', - needBody: true, - url: { - fmt: '/_security/oidc/prepare', - }, - }); - - /** - * Sends the URL to which the OpenID Connect Provider redirected the UA to Elasticsearch for validation. - * - * @param {string} state The state parameter that was returned by Elasticsearch in the - * preparation response. - * @param {string} nonce The nonce parameter that was returned by Elasticsearch in the - * preparation response. - * @param {string} redirect_uri The URL to where the UA was redirected by the OpenID Connect provider. - * @param {string} [realm] Optional string used to identify the name of the OpenID Connect realm - * that should be used to authenticate request. - * - * @returns {{username: string, access_token: string, refresh_token; string, expires_in: number}} Object that - * includes name of the user, access token to use for any consequent requests that - * need to be authenticated and a number of seconds after which access token will expire. - */ - shield.oidcAuthenticate = ca({ - method: 'POST', - needBody: true, - url: { - fmt: '/_security/oidc/authenticate', - }, - }); - - /** - * Invalidates an access token and refresh token pair that was generated after an OpenID Connect authentication. - * - * @param {string} token An access token that was created by authenticating to an OpenID Connect realm and - * that needs to be invalidated. - * @param {string} refresh_token A refresh token that was created by authenticating to an OpenID Connect realm and - * that needs to be invalidated. - * - * @returns {{redirect?: string}} If the Elasticsearch OpenID Connect realm configuration and the - * OpenID Connect provider supports RP-initiated SLO, a URL to redirect the UA - */ - shield.oidcLogout = ca({ - method: 'POST', - needBody: true, - url: { - fmt: '/_security/oidc/logout', - }, - }); - - /** - * Refreshes an access token. - * - * @param {string} grant_type Currently only "refresh_token" grant type is supported. - * @param {string} refresh_token One-time refresh token that will be exchanged to the new access/refresh token pair. - * - * @returns {{access_token: string, type: string, expires_in: number, refresh_token: string}} - */ - shield.getAccessToken = ca({ - method: 'POST', - needBody: true, - url: { - fmt: '/_security/oauth2/token', - }, - }); - - /** - * Invalidates an access token. - * - * @param {string} token The access token to invalidate - * - * @returns {{created: boolean}} - */ - shield.deleteAccessToken = ca({ - method: 'DELETE', - needBody: true, - params: { - token: { - type: 'string', - }, - }, - url: { - fmt: '/_security/oauth2/token', - }, - }); - - /** - * Gets an access token in exchange to the certificate chain for the target subject distinguished name. - * - * @param {string[]} x509_certificate_chain An ordered array of base64-encoded (Section 4 of RFC4648 - not - * base64url-encoded) DER PKIX certificate values. - * - * @returns {{access_token: string, type: string, expires_in: number}} - */ - shield.delegatePKI = ca({ - method: 'POST', - needBody: true, - url: { - fmt: '/_security/delegate_pki', - }, - }); -} diff --git a/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.test.ts b/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.test.ts index 812e3e3d17f9..e58cc4b2caa5 100644 --- a/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.test.ts +++ b/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.test.ts @@ -5,20 +5,11 @@ */ import { BehaviorSubject } from 'rxjs'; -import { - ILegacyCustomClusterClient, - ServiceStatusLevels, - CoreStatus, -} from '../../../../../src/core/server'; +import { ServiceStatusLevels, CoreStatus } from '../../../../../src/core/server'; import { SecurityLicense, SecurityLicenseFeatures } from '../../common/licensing'; -import { elasticsearchClientPlugin } from './elasticsearch_client_plugin'; import { ElasticsearchService } from './elasticsearch_service'; -import { - coreMock, - elasticsearchServiceMock, - loggingSystemMock, -} from '../../../../../src/core/server/mocks'; +import { coreMock, loggingSystemMock } from '../../../../../src/core/server/mocks'; import { licenseMock } from '../../common/licensing/index.mock'; import { nextTick } from '@kbn/test/jest'; @@ -30,35 +21,20 @@ describe('ElasticsearchService', () => { describe('setup()', () => { it('exposes proper contract', () => { - const mockCoreSetup = coreMock.createSetup(); - const mockClusterClient = elasticsearchServiceMock.createLegacyCustomClusterClient(); - mockCoreSetup.elasticsearch.legacy.createClient.mockReturnValue(mockClusterClient); - expect( service.setup({ - elasticsearch: mockCoreSetup.elasticsearch, - status: mockCoreSetup.status, + status: coreMock.createSetup().status, license: licenseMock.create(), }) - ).toEqual({ clusterClient: mockClusterClient }); - - expect(mockCoreSetup.elasticsearch.legacy.createClient).toHaveBeenCalledTimes(1); - expect(mockCoreSetup.elasticsearch.legacy.createClient).toHaveBeenCalledWith('security', { - plugins: [elasticsearchClientPlugin], - }); + ).toBeUndefined(); }); }); describe('start()', () => { - let mockClusterClient: ILegacyCustomClusterClient; let mockLicense: jest.Mocked; let mockStatusSubject: BehaviorSubject; let mockLicenseSubject: BehaviorSubject; beforeEach(() => { - const mockCoreSetup = coreMock.createSetup(); - mockClusterClient = elasticsearchServiceMock.createLegacyCustomClusterClient(); - mockCoreSetup.elasticsearch.legacy.createClient.mockReturnValue(mockClusterClient); - mockLicenseSubject = new BehaviorSubject(({} as unknown) as SecurityLicenseFeatures); mockLicense = licenseMock.create(); mockLicense.isEnabled.mockReturnValue(false); @@ -71,20 +47,18 @@ describe('ElasticsearchService', () => { }, savedObjects: { level: ServiceStatusLevels.unavailable, summary: 'Service is NOT working' }, }); - mockCoreSetup.status.core$ = mockStatusSubject; + + const mockStatus = coreMock.createSetup().status; + mockStatus.core$ = mockStatusSubject; service.setup({ - elasticsearch: mockCoreSetup.elasticsearch, - status: mockCoreSetup.status, + status: mockStatus, license: mockLicense, }); }); it('exposes proper contract', () => { - expect(service.start()).toEqual({ - clusterClient: mockClusterClient, - watchOnlineStatus$: expect.any(Function), - }); + expect(service.start()).toEqual({ watchOnlineStatus$: expect.any(Function) }); }); it('`watchOnlineStatus$` allows tracking of Elasticsearch status', () => { @@ -199,24 +173,4 @@ describe('ElasticsearchService', () => { expect(mockHandler).toHaveBeenCalledTimes(2); }); }); - - describe('stop()', () => { - it('properly closes cluster client instance', () => { - const mockCoreSetup = coreMock.createSetup(); - const mockClusterClient = elasticsearchServiceMock.createLegacyCustomClusterClient(); - mockCoreSetup.elasticsearch.legacy.createClient.mockReturnValue(mockClusterClient); - - service.setup({ - elasticsearch: mockCoreSetup.elasticsearch, - status: mockCoreSetup.status, - license: licenseMock.create(), - }); - - expect(mockClusterClient.close).not.toHaveBeenCalled(); - - service.stop(); - - expect(mockClusterClient.close).toHaveBeenCalledTimes(1); - }); - }); }); diff --git a/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.ts b/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.ts index 42a83b2e5b52..ace1dc553890 100644 --- a/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.ts +++ b/x-pack/plugins/security/server/elasticsearch/elasticsearch_service.ts @@ -6,29 +6,15 @@ import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { distinctUntilChanged, filter, map, shareReplay, tap } from 'rxjs/operators'; -import { - ILegacyClusterClient, - ILegacyCustomClusterClient, - Logger, - ServiceStatusLevels, - StatusServiceSetup, - ElasticsearchServiceSetup as CoreElasticsearchServiceSetup, -} from '../../../../../src/core/server'; +import { Logger, ServiceStatusLevels, StatusServiceSetup } from '../../../../../src/core/server'; import { SecurityLicense } from '../../common/licensing'; -import { elasticsearchClientPlugin } from './elasticsearch_client_plugin'; export interface ElasticsearchServiceSetupParams { - readonly elasticsearch: CoreElasticsearchServiceSetup; readonly status: StatusServiceSetup; readonly license: SecurityLicense; } -export interface ElasticsearchServiceSetup { - readonly clusterClient: ILegacyClusterClient; -} - export interface ElasticsearchServiceStart { - readonly clusterClient: ILegacyClusterClient; readonly watchOnlineStatus$: () => Observable; } @@ -41,22 +27,13 @@ export interface OnlineStatusRetryScheduler { */ export class ElasticsearchService { readonly #logger: Logger; - #clusterClient?: ILegacyCustomClusterClient; #coreStatus$!: Observable; constructor(logger: Logger) { this.#logger = logger; } - setup({ - elasticsearch, - status, - license, - }: ElasticsearchServiceSetupParams): ElasticsearchServiceSetup { - this.#clusterClient = elasticsearch.legacy.createClient('security', { - plugins: [elasticsearchClientPlugin], - }); - + setup({ status, license }: ElasticsearchServiceSetupParams) { this.#coreStatus$ = combineLatest([status.core$, license.features$]).pipe( map( ([coreStatus]) => @@ -64,14 +41,10 @@ export class ElasticsearchService { ), shareReplay(1) ); - - return { clusterClient: this.#clusterClient }; } start(): ElasticsearchServiceStart { return { - clusterClient: this.#clusterClient!, - // We'll need to get rid of this as soon as Core's Elasticsearch service exposes this // functionality in the scope of https://github.com/elastic/kibana/issues/41983. watchOnlineStatus$: () => { @@ -120,11 +93,4 @@ export class ElasticsearchService { }, }; } - - stop() { - if (this.#clusterClient) { - this.#clusterClient.close(); - this.#clusterClient = undefined; - } - } } diff --git a/x-pack/plugins/security/server/elasticsearch/index.ts b/x-pack/plugins/security/server/elasticsearch/index.ts index 23e4876904c3..c770600db44c 100644 --- a/x-pack/plugins/security/server/elasticsearch/index.ts +++ b/x-pack/plugins/security/server/elasticsearch/index.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AuthenticatedUser } from '../../common/model'; +import type { AuthenticatedUser } from '../../common/model'; export type AuthenticationInfo = Omit; export { ElasticsearchService, - ElasticsearchServiceSetup, ElasticsearchServiceStart, OnlineStatusRetryScheduler, } from './elasticsearch_service'; diff --git a/x-pack/plugins/security/server/mocks.ts b/x-pack/plugins/security/server/mocks.ts index df30d1bf9d6f..7d8f3cf36a4a 100644 --- a/x-pack/plugins/security/server/mocks.ts +++ b/x-pack/plugins/security/server/mocks.ts @@ -14,7 +14,7 @@ function createSetupMock() { const mockAuthz = authorizationMock.create(); return { audit: auditServiceMock.create(), - authc: authenticationServiceMock.createSetup(), + authc: { getCurrentUser: jest.fn() }, authz: { actions: mockAuthz.actions, checkPrivilegesWithRequest: mockAuthz.checkPrivilegesWithRequest, diff --git a/x-pack/plugins/security/server/plugin.test.ts b/x-pack/plugins/security/server/plugin.test.ts index 54efdbdccbb7..256eca376fa0 100644 --- a/x-pack/plugins/security/server/plugin.test.ts +++ b/x-pack/plugins/security/server/plugin.test.ts @@ -6,11 +6,10 @@ import { of } from 'rxjs'; import { ByteSizeValue } from '@kbn/config-schema'; -import { ILegacyCustomClusterClient } from '../../../../src/core/server'; import { ConfigSchema } from './config'; import { Plugin, PluginSetupDependencies, PluginStartDependencies } from './plugin'; -import { coreMock, elasticsearchServiceMock } from '../../../../src/core/server/mocks'; +import { coreMock } from '../../../../src/core/server/mocks'; import { featuresPluginMock } from '../../features/server/mocks'; import { taskManagerMock } from '../../task_manager/server/mocks'; import { licensingMock } from '../../licensing/server/mocks'; @@ -19,7 +18,6 @@ describe('Security Plugin', () => { let plugin: Plugin; let mockCoreSetup: ReturnType; let mockCoreStart: ReturnType; - let mockClusterClient: jest.Mocked; let mockSetupDependencies: PluginSetupDependencies; let mockStartDependencies: PluginStartDependencies; beforeEach(() => { @@ -43,9 +41,6 @@ describe('Security Plugin', () => { protocol: 'https', }); - mockClusterClient = elasticsearchServiceMock.createLegacyCustomClusterClient(); - mockCoreSetup.elasticsearch.legacy.createClient.mockReturnValue(mockClusterClient); - mockSetupDependencies = ({ licensing: { license$: of({}), featureUsage: { register: jest.fn() } }, features: featuresPluginMock.createSetup(), diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index 1016221cb719..8d8e4c096f37 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -12,6 +12,7 @@ import { SecurityOssPluginSetup } from 'src/plugins/security_oss/server'; import { CoreSetup, CoreStart, + KibanaRequest, Logger, PluginInitializerContext, } from '../../../../src/core/server'; @@ -24,22 +25,19 @@ import { import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/server'; import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server'; -import { - AuthenticationService, - AuthenticationServiceSetup, - AuthenticationServiceStart, -} from './authentication'; +import { AuthenticationService, AuthenticationServiceStart } from './authentication'; import { AuthorizationService, AuthorizationServiceSetup } from './authorization'; import { AnonymousAccessService, AnonymousAccessServiceStart } from './anonymous_access'; import { ConfigSchema, ConfigType, createConfig } from './config'; import { defineRoutes } from './routes'; import { SecurityLicenseService, SecurityLicense } from '../common/licensing'; +import { AuthenticatedUser } from '../common/model'; import { setupSavedObjects } from './saved_objects'; import { AuditService, SecurityAuditLogger, AuditServiceSetup } from './audit'; import { SecurityFeatureUsageService, SecurityFeatureUsageServiceStart } from './feature_usage'; import { securityFeatures } from './features'; import { ElasticsearchService } from './elasticsearch'; -import { SessionManagementService } from './session_management'; +import { Session, SessionManagementService } from './session_management'; import { registerSecurityUsageCollector } from './usage_collector'; import { setupSpacesClient } from './spaces'; @@ -60,7 +58,7 @@ export interface SecurityPluginSetup { /** * @deprecated Use `authc` methods from the `SecurityServiceStart` contract instead. */ - authc: Pick; + authc: { getCurrentUser: (request: KibanaRequest) => AuthenticatedUser | null }; /** * @deprecated Use `authz` methods from the `SecurityServiceStart` contract instead. */ @@ -104,8 +102,8 @@ export interface PluginStartDependencies { */ export class Plugin { private readonly logger: Logger; - private authenticationStart?: AuthenticationServiceStart; private authorizationSetup?: AuthorizationServiceSetup; + private auditSetup?: AuditServiceSetup; private anonymousAccessStart?: AnonymousAccessServiceStart; private configSubscription?: Subscription; @@ -117,6 +115,14 @@ export class Plugin { return this.config; }; + private session?: Session; + private readonly getSession = () => { + if (!this.session) { + throw new Error('Session is not available.'); + } + return this.session; + }; + private kibanaIndexName?: string; private readonly getKibanaIndexName = () => { if (!this.kibanaIndexName) { @@ -125,6 +131,17 @@ export class Plugin { return this.kibanaIndexName; }; + private readonly authenticationService = new AuthenticationService( + this.initializerContext.logger.get('authentication') + ); + private authenticationStart?: AuthenticationServiceStart; + private readonly getAuthentication = () => { + if (!this.authenticationStart) { + throw new Error(`authenticationStart is not registered!`); + } + return this.authenticationStart; + }; + private readonly featureUsageService = new SecurityFeatureUsageService(); private featureUsageServiceStart?: SecurityFeatureUsageServiceStart; private readonly getFeatureUsageService = () => { @@ -143,9 +160,6 @@ export class Plugin { private readonly sessionManagementService = new SessionManagementService( this.initializerContext.logger.get('session') ); - private readonly authenticationService = new AuthenticationService( - this.initializerContext.logger.get('authentication') - ); private readonly anonymousAccessService = new AnonymousAccessService( this.initializerContext.logger.get('anonymous-access'), this.getConfig @@ -211,46 +225,22 @@ export class Plugin { features.registerElasticsearchFeature(securityFeature) ); - const { clusterClient } = this.elasticsearchService.setup({ - elasticsearch: core.elasticsearch, - license, - status: core.status, - }); - + this.elasticsearchService.setup({ license, status: core.status }); this.featureUsageService.setup({ featureUsage: licensing.featureUsage }); + this.sessionManagementService.setup({ config, http: core.http, taskManager }); + this.authenticationService.setup({ http: core.http, license }); registerSecurityUsageCollector({ usageCollection, config, license }); - const { session } = this.sessionManagementService.setup({ - config, - clusterClient, - http: core.http, - kibanaIndexName, - taskManager, - }); - - const audit = this.auditService.setup({ + this.auditSetup = this.auditService.setup({ license, config: config.audit, logging: core.logging, http: core.http, getSpaceId: (request) => spaces?.spacesService.getSpaceId(request), - getSID: (request) => session.getSID(request), - getCurrentUser: (request) => authenticationSetup.getCurrentUser(request), - recordAuditLoggingUsage: () => this.featureUsageServiceStart?.recordAuditLoggingUsage(), - }); - const legacyAuditLogger = new SecurityAuditLogger(audit.getLogger()); - - const authenticationSetup = this.authenticationService.setup({ - legacyAuditLogger, - audit, - getFeatureUsageService: this.getFeatureUsageService, - http: core.http, - clusterClient, - config, - license, - loggers: this.initializerContext.logger, - session, + getSID: (request) => this.getSession().getSID(request), + getCurrentUser: (request) => this.getAuthentication().getCurrentUser(request), + recordAuditLoggingUsage: () => this.getFeatureUsageService().recordAuditLoggingUsage(), }); this.anonymousAccessService.setup(); @@ -267,18 +257,18 @@ export class Plugin { buildNumber: this.initializerContext.env.packageInfo.buildNum, getSpacesService: () => spaces?.spacesService, features, - getCurrentUser: authenticationSetup.getCurrentUser, + getCurrentUser: (request) => this.getAuthentication().getCurrentUser(request), }); setupSpacesClient({ spaces, - audit, + audit: this.auditSetup, authz: this.authorizationSetup, }); setupSavedObjects({ - legacyAuditLogger, - audit, + legacyAuditLogger: new SecurityAuditLogger(this.auditSetup.getLogger()), + audit: this.auditSetup, authz: this.authorizationSetup, savedObjects: core.savedObjects, getSpacesService: () => spaces?.spacesService, @@ -292,26 +282,20 @@ export class Plugin { config, authz: this.authorizationSetup, license, - session, + getSession: this.getSession, getFeatures: () => startServicesPromise.then((services) => services.features.getKibanaFeatures()), getFeatureUsageService: this.getFeatureUsageService, - getAuthenticationService: () => { - if (!this.authenticationStart) { - throw new Error('Authentication service is not started!'); - } - - return this.authenticationStart; - }, + getAuthenticationService: this.getAuthentication, }); return Object.freeze({ audit: { - asScoped: audit.asScoped, - getLogger: audit.getLogger, + asScoped: this.auditSetup.asScoped, + getLogger: this.auditSetup.getLogger, }, - authc: { getCurrentUser: authenticationSetup.getCurrentUser }, + authc: { getCurrentUser: (request) => this.getAuthentication().getCurrentUser(request) }, authz: { actions: this.authorizationSetup.actions, @@ -337,11 +321,24 @@ export class Plugin { const clusterClient = core.elasticsearch.client; const { watchOnlineStatus$ } = this.elasticsearchService.start(); + const { session } = this.sessionManagementService.start({ + elasticsearchClient: clusterClient.asInternalUser, + kibanaIndexName: this.getKibanaIndexName(), + online$: watchOnlineStatus$(), + taskManager, + }); + this.session = session; - this.sessionManagementService.start({ online$: watchOnlineStatus$(), taskManager }); + const config = this.getConfig(); this.authenticationStart = this.authenticationService.start({ - http: core.http, + audit: this.auditSetup!, clusterClient, + config, + featureUsageService: this.featureUsageServiceStart, + http: core.http, + legacyAuditLogger: new SecurityAuditLogger(this.auditSetup!.getLogger()), + loggers: this.initializerContext.logger, + session, }); this.authorizationService.start({ features, clusterClient, online$: watchOnlineStatus$() }); @@ -391,7 +388,6 @@ export class Plugin { this.securityLicenseService.stop(); this.auditService.stop(); this.authorizationService.stop(); - this.elasticsearchService.stop(); this.sessionManagementService.stop(); } } diff --git a/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts b/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts index 53950cb43194..f329d7a3980c 100644 --- a/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts +++ b/x-pack/plugins/security/server/routes/api_keys/enabled.test.ts @@ -6,11 +6,7 @@ import Boom from '@hapi/boom'; import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; -import { - kibanaResponseFactory, - RequestHandler, - RequestHandlerContext, -} from '../../../../../../src/core/server'; +import { kibanaResponseFactory, RequestHandler } from '../../../../../../src/core/server'; import { httpServerMock } from '../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../index.mock'; @@ -18,6 +14,7 @@ import { routeDefinitionParamsMock } from '../index.mock'; import type { AuthenticationServiceStart } from '../../authentication'; import { defineEnabledApiKeysRoutes } from './enabled'; import { authenticationServiceMock } from '../../authentication/authentication_service.mock'; +import type { SecurityRequestHandlerContext } from '../../types'; describe('API keys enabled', () => { function getMockContext( @@ -25,7 +22,7 @@ describe('API keys enabled', () => { ) { return ({ licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown) as RequestHandlerContext; + } as unknown) as SecurityRequestHandlerContext; } let routeHandler: RequestHandler; diff --git a/x-pack/plugins/security/server/routes/authentication/common.test.ts b/x-pack/plugins/security/server/routes/authentication/common.test.ts index b032930e4400..4073ef9f21c6 100644 --- a/x-pack/plugins/security/server/routes/authentication/common.test.ts +++ b/x-pack/plugins/security/server/routes/authentication/common.test.ts @@ -7,10 +7,8 @@ import { Type } from '@kbn/config-schema'; import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; import { - IRouter, kibanaResponseFactory, RequestHandler, - RequestHandlerContext, RouteConfig, } from '../../../../../../src/core/server'; import type { SecurityLicense, SecurityLicenseFeatures } from '../../../common/licensing'; @@ -22,6 +20,7 @@ import { SAMLLogin, } from '../../authentication'; import { defineCommonRoutes } from './common'; +import type { SecurityRequestHandlerContext, SecurityRouter } from '../../types'; import { httpServerMock } from '../../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; @@ -29,10 +28,10 @@ import { routeDefinitionParamsMock } from '../index.mock'; import { authenticationServiceMock } from '../../authentication/authentication_service.mock'; describe('Common authentication routes', () => { - let router: jest.Mocked; + let router: jest.Mocked; let authc: DeeplyMockedKeys; let license: jest.Mocked; - let mockContext: RequestHandlerContext; + let mockContext: SecurityRequestHandlerContext; beforeEach(() => { const routeParamsMock = routeDefinitionParamsMock.create(); router = routeParamsMock.router; @@ -44,13 +43,13 @@ describe('Common authentication routes', () => { licensing: { license: { check: jest.fn().mockReturnValue({ check: 'valid' }) }, }, - } as unknown) as RequestHandlerContext; + } as unknown) as SecurityRequestHandlerContext; defineCommonRoutes(routeParamsMock); }); describe('logout', () => { - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; let routeConfig: RouteConfig; const mockRequest = httpServerMock.createKibanaRequest({ @@ -151,7 +150,7 @@ describe('Common authentication routes', () => { }); describe('me', () => { - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; let routeConfig: RouteConfig; const mockRequest = httpServerMock.createKibanaRequest({ @@ -185,7 +184,7 @@ describe('Common authentication routes', () => { }); describe('login', () => { - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; let routeConfig: RouteConfig; beforeEach(() => { const [acsRouteConfig, acsRouteHandler] = router.post.mock.calls.find( @@ -645,7 +644,7 @@ describe('Common authentication routes', () => { }); describe('acknowledge access agreement', () => { - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; let routeConfig: RouteConfig; beforeEach(() => { const [acsRouteConfig, acsRouteHandler] = router.post.mock.calls.find( diff --git a/x-pack/plugins/security/server/routes/authentication/saml.test.ts b/x-pack/plugins/security/server/routes/authentication/saml.test.ts index d1d5f601d7a4..ad7d141cad68 100644 --- a/x-pack/plugins/security/server/routes/authentication/saml.test.ts +++ b/x-pack/plugins/security/server/routes/authentication/saml.test.ts @@ -8,7 +8,8 @@ import { Type } from '@kbn/config-schema'; import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; import { AuthenticationResult, AuthenticationServiceStart, SAMLLogin } from '../../authentication'; import { defineSAMLRoutes } from './saml'; -import type { IRouter, RequestHandler, RouteConfig } from '../../../../../../src/core/server'; +import type { RequestHandler, RouteConfig } from '../../../../../../src/core/server'; +import type { SecurityRouter } from '../../types'; import { httpServerMock } from '../../../../../../src/core/server/mocks'; import { mockAuthenticatedUser } from '../../../common/model/authenticated_user.mock'; @@ -16,7 +17,7 @@ import { routeDefinitionParamsMock } from '../index.mock'; import { authenticationServiceMock } from '../../authentication/authentication_service.mock'; describe('SAML authentication routes', () => { - let router: jest.Mocked; + let router: jest.Mocked; let authc: DeeplyMockedKeys; beforeEach(() => { const routeParamsMock = routeDefinitionParamsMock.create(); diff --git a/x-pack/plugins/security/server/routes/authorization/privileges/get.test.ts b/x-pack/plugins/security/server/routes/authorization/privileges/get.test.ts index 7301a3cf5197..7e180f355ddf 100644 --- a/x-pack/plugins/security/server/routes/authorization/privileges/get.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/privileges/get.test.ts @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../../src/core/server'; +import { kibanaResponseFactory } from '../../../../../../../src/core/server'; import { LicenseCheck } from '../../../../../licensing/server'; import { RawKibanaPrivileges } from '../../../../common/model'; import { defineGetPrivilegesRoutes } from './get'; +import type { SecurityRequestHandlerContext } from '../../../types'; import { httpServerMock } from '../../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../../index.mock'; @@ -66,7 +67,7 @@ describe('GET privileges', () => { }); const mockContext = ({ licensing: { license: { check: jest.fn().mockReturnValue(licenseCheckResult) } }, - } as unknown) as RequestHandlerContext; + } as unknown) as SecurityRequestHandlerContext; const response = await handler(mockContext, mockRequest, kibanaResponseFactory); expect(response.status).toBe(asserts.statusCode); diff --git a/x-pack/plugins/security/server/routes/authorization/spaces/share_saved_object_permissions.test.ts b/x-pack/plugins/security/server/routes/authorization/spaces/share_saved_object_permissions.test.ts index ccdee8b10003..c3ceecef1fa6 100644 --- a/x-pack/plugins/security/server/routes/authorization/spaces/share_saved_object_permissions.test.ts +++ b/x-pack/plugins/security/server/routes/authorization/spaces/share_saved_object_permissions.test.ts @@ -5,10 +5,8 @@ */ import { - IRouter, kibanaResponseFactory, RequestHandler, - RequestHandlerContext, RouteConfig, } from '../../../../../../../src/core/server'; import { defineShareSavedObjectPermissionRoutes } from './share_saved_object_permissions'; @@ -18,26 +16,27 @@ import { routeDefinitionParamsMock } from '../../index.mock'; import { RouteDefinitionParams } from '../..'; import { DeeplyMockedKeys } from '@kbn/utility-types/target/jest'; import { CheckPrivileges } from '../../../authorization/types'; +import type { SecurityRequestHandlerContext, SecurityRouter } from '../../../types'; describe('Share Saved Object Permissions', () => { - let router: jest.Mocked; + let router: jest.Mocked; let routeParamsMock: DeeplyMockedKeys; const mockContext = ({ licensing: { license: { check: jest.fn().mockReturnValue({ state: 'valid' }) }, }, - } as unknown) as RequestHandlerContext; + } as unknown) as SecurityRequestHandlerContext; beforeEach(() => { routeParamsMock = routeDefinitionParamsMock.create(); - router = routeParamsMock.router as jest.Mocked; + router = routeParamsMock.router as jest.Mocked; defineShareSavedObjectPermissionRoutes(routeParamsMock); }); describe('GET /internal/security/_share_saved_object_permissions', () => { - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; let routeConfig: RouteConfig; beforeEach(() => { const [shareRouteConfig, shareRouteHandler] = router.get.mock.calls.find( diff --git a/x-pack/plugins/security/server/routes/index.mock.ts b/x-pack/plugins/security/server/routes/index.mock.ts index 4103594faba1..f7b51eeffe6e 100644 --- a/x-pack/plugins/security/server/routes/index.mock.ts +++ b/x-pack/plugins/security/server/routes/index.mock.ts @@ -32,7 +32,7 @@ export const routeDefinitionParamsMock = { httpResources: httpResourcesMock.createRegistrar(), getFeatures: jest.fn(), getFeatureUsageService: jest.fn(), - session: sessionMock.create(), + getSession: jest.fn().mockReturnValue(sessionMock.create()), getAuthenticationService: jest.fn().mockReturnValue(authenticationServiceMock.createStart()), } as unknown) as DeeplyMockedKeys), }; diff --git a/x-pack/plugins/security/server/routes/index.ts b/x-pack/plugins/security/server/routes/index.ts index 899215c49fa9..9a8fe2e0d6d1 100644 --- a/x-pack/plugins/security/server/routes/index.ts +++ b/x-pack/plugins/security/server/routes/index.ts @@ -5,13 +5,14 @@ */ import type { PublicMethodsOf } from '@kbn/utility-types'; import type { KibanaFeature } from '../../../features/server'; -import type { HttpResources, IBasePath, IRouter, Logger } from '../../../../../src/core/server'; +import type { HttpResources, IBasePath, Logger } from '../../../../../src/core/server'; import type { SecurityLicense } from '../../common/licensing'; import type { AuthenticationServiceStart } from '../authentication'; import type { AuthorizationServiceSetup } from '../authorization'; import type { ConfigType } from '../config'; import type { SecurityFeatureUsageServiceStart } from '../feature_usage'; import type { Session } from '../session_management'; +import type { SecurityRouter } from '../types'; import { defineAuthenticationRoutes } from './authentication'; import { defineAuthorizationRoutes } from './authorization'; @@ -26,13 +27,13 @@ import { defineViewRoutes } from './views'; * Describes parameters used to define HTTP routes. */ export interface RouteDefinitionParams { - router: IRouter; + router: SecurityRouter; basePath: IBasePath; httpResources: HttpResources; logger: Logger; config: ConfigType; authz: AuthorizationServiceSetup; - session: PublicMethodsOf; + getSession: () => PublicMethodsOf; license: SecurityLicense; getFeatures: () => Promise; getFeatureUsageService: () => SecurityFeatureUsageServiceStart; diff --git a/x-pack/plugins/security/server/routes/licensed_route_handler.ts b/x-pack/plugins/security/server/routes/licensed_route_handler.ts index d8c212aa2d21..c1722c5b1983 100644 --- a/x-pack/plugins/security/server/routes/licensed_route_handler.ts +++ b/x-pack/plugins/security/server/routes/licensed_route_handler.ts @@ -5,17 +5,19 @@ */ import { KibanaResponseFactory, RequestHandler, RouteMethod } from 'kibana/server'; +import type { SecurityRequestHandlerContext } from '../types'; export const createLicensedRouteHandler = < P, Q, B, + Context extends SecurityRequestHandlerContext, M extends RouteMethod, R extends KibanaResponseFactory >( - handler: RequestHandler + handler: RequestHandler ) => { - const licensedRouteHandler: RequestHandler = ( + const licensedRouteHandler: RequestHandler = ( context, request, responseToolkit diff --git a/x-pack/plugins/security/server/routes/session_management/extend.test.ts b/x-pack/plugins/security/server/routes/session_management/extend.test.ts index 235fce152510..8307905fb884 100644 --- a/x-pack/plugins/security/server/routes/session_management/extend.test.ts +++ b/x-pack/plugins/security/server/routes/session_management/extend.test.ts @@ -5,19 +5,18 @@ */ import { - IRouter, kibanaResponseFactory, RequestHandler, - RequestHandlerContext, RouteConfig, } from '../../../../../../src/core/server'; import { defineSessionExtendRoutes } from './extend'; import { httpServerMock } from '../../../../../../src/core/server/mocks'; +import type { SecurityRequestHandlerContext, SecurityRouter } from '../../types'; import { routeDefinitionParamsMock } from '../index.mock'; describe('Extend session routes', () => { - let router: jest.Mocked; + let router: jest.Mocked; beforeEach(() => { const routeParamsMock = routeDefinitionParamsMock.create(); router = routeParamsMock.router; @@ -26,7 +25,7 @@ describe('Extend session routes', () => { }); describe('extend session', () => { - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; let routeConfig: RouteConfig; beforeEach(() => { const [extendRouteConfig, extendRouteHandler] = router.post.mock.calls.find( @@ -45,7 +44,7 @@ describe('Extend session routes', () => { it('always returns 302.', async () => { await expect( routeHandler( - ({} as unknown) as RequestHandlerContext, + ({} as unknown) as SecurityRequestHandlerContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory ) diff --git a/x-pack/plugins/security/server/routes/session_management/info.test.ts b/x-pack/plugins/security/server/routes/session_management/info.test.ts index a104336f2e6b..b068e80cfa85 100644 --- a/x-pack/plugins/security/server/routes/session_management/info.test.ts +++ b/x-pack/plugins/security/server/routes/session_management/info.test.ts @@ -5,33 +5,34 @@ */ import { - IRouter, kibanaResponseFactory, RequestHandler, - RequestHandlerContext, RouteConfig, } from '../../../../../../src/core/server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { Session } from '../../session_management'; import { defineSessionInfoRoutes } from './info'; +import type { SecurityRequestHandlerContext, SecurityRouter } from '../../types'; import { httpServerMock } from '../../../../../../src/core/server/mocks'; import { sessionMock } from '../../session_management/session.mock'; import { routeDefinitionParamsMock } from '../index.mock'; describe('Info session routes', () => { - let router: jest.Mocked; + let router: jest.Mocked; let session: jest.Mocked>; beforeEach(() => { const routeParamsMock = routeDefinitionParamsMock.create(); router = routeParamsMock.router; - session = routeParamsMock.session; + + session = sessionMock.create(); + routeParamsMock.getSession.mockReturnValue(session); defineSessionInfoRoutes(routeParamsMock); }); describe('extend session', () => { - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; let routeConfig: RouteConfig; beforeEach(() => { const [extendRouteConfig, extendRouteHandler] = router.get.mock.calls.find( @@ -53,7 +54,11 @@ describe('Info session routes', () => { const request = httpServerMock.createKibanaRequest(); await expect( - routeHandler(({} as unknown) as RequestHandlerContext, request, kibanaResponseFactory) + routeHandler( + ({} as unknown) as SecurityRequestHandlerContext, + request, + kibanaResponseFactory + ) ).resolves.toEqual({ status: 500, options: {}, @@ -79,7 +84,7 @@ describe('Info session routes', () => { }; await expect( routeHandler( - ({} as unknown) as RequestHandlerContext, + ({} as unknown) as SecurityRequestHandlerContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory ) @@ -95,7 +100,7 @@ describe('Info session routes', () => { await expect( routeHandler( - ({} as unknown) as RequestHandlerContext, + ({} as unknown) as SecurityRequestHandlerContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory ) diff --git a/x-pack/plugins/security/server/routes/session_management/info.ts b/x-pack/plugins/security/server/routes/session_management/info.ts index 381127284f78..1f73edf51097 100644 --- a/x-pack/plugins/security/server/routes/session_management/info.ts +++ b/x-pack/plugins/security/server/routes/session_management/info.ts @@ -10,12 +10,12 @@ import { RouteDefinitionParams } from '..'; /** * Defines routes required for the session info. */ -export function defineSessionInfoRoutes({ router, logger, session }: RouteDefinitionParams) { +export function defineSessionInfoRoutes({ router, logger, getSession }: RouteDefinitionParams) { router.get( { path: '/internal/security/session', validate: false }, async (_context, request, response) => { try { - const sessionValue = await session.get(request); + const sessionValue = await getSession().get(request); if (sessionValue) { return response.ok({ body: { diff --git a/x-pack/plugins/security/server/routes/users/change_password.test.ts b/x-pack/plugins/security/server/routes/users/change_password.test.ts index 1ef4e407e45e..2c7c3f6bafc4 100644 --- a/x-pack/plugins/security/server/routes/users/change_password.test.ts +++ b/x-pack/plugins/security/server/routes/users/change_password.test.ts @@ -8,12 +8,11 @@ import { errors } from 'elasticsearch'; import { ObjectType } from '@kbn/config-schema'; import type { PublicMethodsOf } from '@kbn/utility-types'; import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; +import type { SecurityRequestHandlerContext, SecurityRouter } from '../../types'; import { Headers, - IRouter, kibanaResponseFactory, RequestHandler, - RequestHandlerContext, RouteConfig, } from '../../../../../../src/core/server'; import { AuthenticationResult, AuthenticationServiceStart } from '../../authentication'; @@ -27,12 +26,12 @@ import { routeDefinitionParamsMock } from '../index.mock'; import { authenticationServiceMock } from '../../authentication/authentication_service.mock'; describe('Change password', () => { - let router: jest.Mocked; + let router: jest.Mocked; let authc: DeeplyMockedKeys; let session: jest.Mocked>; - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; let routeConfig: RouteConfig; - let mockContext: DeeplyMockedKeys; + let mockContext: DeeplyMockedKeys; function checkPasswordChangeAPICall(username: string, headers?: Headers) { expect( @@ -49,7 +48,10 @@ describe('Change password', () => { beforeEach(() => { const routeParamsMock = routeDefinitionParamsMock.create(); router = routeParamsMock.router; - session = routeParamsMock.session; + + session = sessionMock.create(); + routeParamsMock.getSession.mockReturnValue(session); + authc = authenticationServiceMock.createStart(); routeParamsMock.getAuthenticationService.mockReturnValue(authc); diff --git a/x-pack/plugins/security/server/routes/users/change_password.ts b/x-pack/plugins/security/server/routes/users/change_password.ts index 7b53afceb48f..1c9086862c37 100644 --- a/x-pack/plugins/security/server/routes/users/change_password.ts +++ b/x-pack/plugins/security/server/routes/users/change_password.ts @@ -16,7 +16,7 @@ import { RouteDefinitionParams } from '..'; export function defineChangeUserPasswordRoutes({ getAuthenticationService, - session, + getSession, router, }: RouteDefinitionParams) { router.post( @@ -37,7 +37,7 @@ export function defineChangeUserPasswordRoutes({ const currentUser = getAuthenticationService().getCurrentUser(request); const isUserChangingOwnPassword = currentUser && currentUser.username === username && canUserChangePassword(currentUser); - const currentSession = isUserChangingOwnPassword ? await session.get(request) : null; + const currentSession = isUserChangingOwnPassword ? await getSession().get(request) : null; // If user is changing their own password they should provide a proof of knowledge their // current password via sending it in `Authorization: Basic base64(username:current password)` diff --git a/x-pack/plugins/security/server/routes/views/access_agreement.test.ts b/x-pack/plugins/security/server/routes/views/access_agreement.test.ts index dfe5faa95ae1..a471f5f4e84c 100644 --- a/x-pack/plugins/security/server/routes/views/access_agreement.test.ts +++ b/x-pack/plugins/security/server/routes/views/access_agreement.test.ts @@ -8,16 +8,15 @@ import { RequestHandler, RouteConfig, kibanaResponseFactory, - IRouter, HttpResources, HttpResourcesRequestHandler, - RequestHandlerContext, } from '../../../../../../src/core/server'; import { SecurityLicense, SecurityLicenseFeatures } from '../../../common/licensing'; import type { AuthenticationProvider } from '../../../common/model'; import { ConfigType } from '../../config'; import { Session } from '../../session_management'; import { defineAccessAgreementRoutes } from './access_agreement'; +import type { SecurityRouter, SecurityRequestHandlerContext } from '../../types'; import { httpResourcesMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { sessionMock } from '../../session_management/session.mock'; @@ -25,19 +24,21 @@ import { routeDefinitionParamsMock } from '../index.mock'; describe('Access agreement view routes', () => { let httpResources: jest.Mocked; - let router: jest.Mocked; + let router: jest.Mocked; let config: ConfigType; let session: jest.Mocked>; let license: jest.Mocked; - let mockContext: RequestHandlerContext; + let mockContext: SecurityRequestHandlerContext; beforeEach(() => { const routeParamsMock = routeDefinitionParamsMock.create(); router = routeParamsMock.router; httpResources = routeParamsMock.httpResources; - session = routeParamsMock.session; config = routeParamsMock.config; license = routeParamsMock.license; + session = sessionMock.create(); + routeParamsMock.getSession.mockReturnValue(session); + license.getFeatures.mockReturnValue({ allowAccessAgreement: true, } as SecurityLicenseFeatures); @@ -46,7 +47,7 @@ describe('Access agreement view routes', () => { licensing: { license: { check: jest.fn().mockReturnValue({ check: 'valid' }) }, }, - } as unknown) as RequestHandlerContext; + } as unknown) as SecurityRequestHandlerContext; defineAccessAgreementRoutes(routeParamsMock); }); @@ -93,7 +94,7 @@ describe('Access agreement view routes', () => { }); describe('Access agreement state route', () => { - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; let routeConfig: RouteConfig; beforeEach(() => { const [loginStateRouteConfig, loginStateRouteHandler] = router.get.mock.calls.find( diff --git a/x-pack/plugins/security/server/routes/views/access_agreement.ts b/x-pack/plugins/security/server/routes/views/access_agreement.ts index 80a1c2a20cf5..c7f694eca68c 100644 --- a/x-pack/plugins/security/server/routes/views/access_agreement.ts +++ b/x-pack/plugins/security/server/routes/views/access_agreement.ts @@ -12,7 +12,7 @@ import { RouteDefinitionParams } from '..'; * Defines routes required for the Access Agreement view. */ export function defineAccessAgreementRoutes({ - session, + getSession, httpResources, license, config, @@ -46,7 +46,7 @@ export function defineAccessAgreementRoutes({ // authenticated with the help of HTTP authentication), that means we should safely check if // we have it and can get a corresponding configuration. try { - const sessionValue = await session.get(request); + const sessionValue = await getSession().get(request); const accessAgreement = (sessionValue && config.authc.providers[ diff --git a/x-pack/plugins/security/server/routes/views/capture_url.test.ts b/x-pack/plugins/security/server/routes/views/capture_url.test.ts index 2b2aab3407eb..74a30cfb2465 100644 --- a/x-pack/plugins/security/server/routes/views/capture_url.test.ts +++ b/x-pack/plugins/security/server/routes/views/capture_url.test.ts @@ -9,12 +9,12 @@ import { RouteConfig, HttpResources, HttpResourcesRequestHandler, - RequestHandlerContext, } from '../../../../../../src/core/server'; import { defineCaptureURLRoutes } from './capture_url'; import { httpResourcesMock, httpServerMock } from '../../../../../../src/core/server/mocks'; import { routeDefinitionParamsMock } from '../index.mock'; +import type { SecurityRequestHandlerContext } from '../../types'; describe('Capture URL view routes', () => { let httpResources: jest.Mocked; @@ -92,7 +92,7 @@ describe('Capture URL view routes', () => { const request = httpServerMock.createKibanaRequest(); const responseFactory = httpResourcesMock.createResponseFactory(); - await routeHandler(({} as unknown) as RequestHandlerContext, request, responseFactory); + await routeHandler(({} as unknown) as SecurityRequestHandlerContext, request, responseFactory); expect(responseFactory.renderAnonymousCoreApp).toHaveBeenCalledWith(); }); diff --git a/x-pack/plugins/security/server/routes/views/logged_out.test.ts b/x-pack/plugins/security/server/routes/views/logged_out.test.ts index 31096bc33d68..7cc534663e2f 100644 --- a/x-pack/plugins/security/server/routes/views/logged_out.test.ts +++ b/x-pack/plugins/security/server/routes/views/logged_out.test.ts @@ -18,7 +18,8 @@ describe('LoggedOut view routes', () => { let routeConfig: RouteConfig; beforeEach(() => { const routeParamsMock = routeDefinitionParamsMock.create(); - session = routeParamsMock.session; + session = sessionMock.create(); + routeParamsMock.getSession.mockReturnValue(session); defineLoggedOutRoutes(routeParamsMock); diff --git a/x-pack/plugins/security/server/routes/views/logged_out.ts b/x-pack/plugins/security/server/routes/views/logged_out.ts index b35154e6a0f2..97357118907d 100644 --- a/x-pack/plugins/security/server/routes/views/logged_out.ts +++ b/x-pack/plugins/security/server/routes/views/logged_out.ts @@ -17,7 +17,7 @@ import { RouteDefinitionParams } from '..'; */ export function defineLoggedOutRoutes({ logger, - session, + getSession, httpResources, basePath, }: RouteDefinitionParams) { @@ -30,7 +30,7 @@ export function defineLoggedOutRoutes({ async (context, request, response) => { // Authentication flow isn't triggered automatically for this route, so we should explicitly // check whether user has an active session already. - const isUserAlreadyLoggedIn = (await session.get(request)) !== null; + const isUserAlreadyLoggedIn = (await getSession().get(request)) !== null; if (isUserAlreadyLoggedIn) { logger.debug('User is already authenticated, redirecting...'); return response.redirected({ diff --git a/x-pack/plugins/security/server/routes/views/login.test.ts b/x-pack/plugins/security/server/routes/views/login.test.ts index e79420fa05da..dfaf935f6497 100644 --- a/x-pack/plugins/security/server/routes/views/login.test.ts +++ b/x-pack/plugins/security/server/routes/views/login.test.ts @@ -9,7 +9,6 @@ import { Type } from '@kbn/config-schema'; import { HttpResources, HttpResourcesRequestHandler, - IRouter, RequestHandler, kibanaResponseFactory, RouteConfig, @@ -18,6 +17,7 @@ import { SecurityLicense } from '../../../common/licensing'; import { LoginSelectorProvider } from '../../../common/login_state'; import { ConfigType } from '../../config'; import { defineLoginRoutes } from './login'; +import type { SecurityRouter, SecurityRequestHandlerContext } from '../../types'; import { coreMock, @@ -28,7 +28,7 @@ import { routeDefinitionParamsMock } from '../index.mock'; describe('Login view routes', () => { let httpResources: jest.Mocked; - let router: jest.Mocked; + let router: jest.Mocked; let license: jest.Mocked; let config: ConfigType; beforeEach(() => { @@ -145,7 +145,7 @@ describe('Login view routes', () => { return routeDefinitionParamsMock.create({ authc: { ...authcConfig } }).config.authc; } - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; let routeConfig: RouteConfig; beforeEach(() => { const [loginStateRouteConfig, loginStateRouteHandler] = router.get.mock.calls.find( diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts index 15ca8bac89bd..5c421776d54f 100644 --- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts +++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts @@ -175,6 +175,7 @@ const expectObjectNamespaceFiltering = async ( // we don't know which base client method will be called; mock them all clientOpts.baseClient.create.mockReturnValue(returnValue as any); clientOpts.baseClient.get.mockReturnValue(returnValue as any); + // 'resolve' is excluded because it has a specific test case written for it clientOpts.baseClient.update.mockReturnValue(returnValue as any); clientOpts.baseClient.addToNamespaces.mockReturnValue(returnValue as any); clientOpts.baseClient.deleteFromNamespaces.mockReturnValue(returnValue as any); @@ -985,6 +986,82 @@ describe('#get', () => { }); }); +describe('#resolve', () => { + const type = 'foo'; + const id = `${type}-id`; + const namespace = 'some-ns'; + const resolvedId = 'another-id'; // success audit records include the resolved ID, not the requested ID + const mockResult = { saved_object: { id: resolvedId } }; // mock result needs to have ID for audit logging + + test(`throws decorated GeneralError when hasPrivileges rejects promise`, async () => { + await expectGeneralError(client.resolve, { type, id }); + }); + + test(`throws decorated ForbiddenError when unauthorized`, async () => { + const options = { namespace }; + await expectForbiddenError(client.resolve, { type, id, options }, 'resolve'); + }); + + test(`returns result of baseClient.resolve when authorized`, async () => { + const apiCallReturnValue = mockResult; + clientOpts.baseClient.resolve.mockReturnValue(apiCallReturnValue as any); + + const options = { namespace }; + const result = await expectSuccess(client.resolve, { type, id, options }, 'resolve'); + expect(result).toEqual(apiCallReturnValue); + }); + + test(`checks privileges for user, actions, and namespace`, async () => { + const options = { namespace }; + await expectPrivilegeCheck(client.resolve, { type, id, options }, namespace); + }); + + test(`filters namespaces that the user doesn't have access to`, async () => { + const options = { namespace }; + + clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockImplementationOnce( + getMockCheckPrivilegesSuccess // privilege check for authorization + ); + clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockImplementation( + getMockCheckPrivilegesFailure // privilege check for namespace filtering + ); + + const namespaces = ['some-other-namespace', '*', namespace]; + const returnValue = { saved_object: { namespaces, id: resolvedId, foo: 'bar' } }; + clientOpts.baseClient.resolve.mockReturnValue(returnValue as any); + + const result = await client.resolve(type, id, options); + // we will never redact the "All Spaces" ID + expect(result).toEqual({ + saved_object: expect.objectContaining({ namespaces: ['*', namespace, '?'] }), + }); + + expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenCalledTimes(2); + expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenLastCalledWith( + 'login:', + ['some-other-namespace'] + // when we check what namespaces to redact, we don't check privileges for '*', only actual space IDs + // we don't check privileges for authorizedNamespace either, as that was already checked earlier in the operation + ); + }); + + test(`adds audit event when successful`, async () => { + const apiCallReturnValue = mockResult; + clientOpts.baseClient.resolve.mockReturnValue(apiCallReturnValue as any); + const options = { namespace }; + await expectSuccess(client.resolve, { type, id, options }, 'resolve'); + expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1); + expectAuditEvent('saved_object_resolve', EventOutcome.SUCCESS, { type, id: resolvedId }); + }); + + test(`adds audit event when not successful`, async () => { + clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockRejectedValue(new Error()); + await expect(() => client.resolve(type, id, { namespace })).rejects.toThrow(); + expect(clientOpts.auditLogger.log).toHaveBeenCalledTimes(1); + expectAuditEvent('saved_object_resolve', EventOutcome.FAILURE, { type, id }); + }); +}); + describe('#deleteFromNamespaces', () => { const type = 'foo'; const id = `${type}-id`; diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts index 765274a839ef..e53bb742e217 100644 --- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts +++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts @@ -335,6 +335,42 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra return await this.redactSavedObjectNamespaces(savedObject, [options.namespace]); } + public async resolve( + type: string, + id: string, + options: SavedObjectsBaseOptions = {} + ) { + try { + const args = { type, id, options }; + await this.ensureAuthorized(type, 'get', options.namespace, { args, auditAction: 'resolve' }); + } catch (error) { + this.auditLogger.log( + savedObjectEvent({ + action: SavedObjectAction.RESOLVE, + savedObject: { type, id }, + error, + }) + ); + throw error; + } + + const resolveResult = await this.baseClient.resolve(type, id, options); + + this.auditLogger.log( + savedObjectEvent({ + action: SavedObjectAction.RESOLVE, + savedObject: { type, id: resolveResult.saved_object.id }, + }) + ); + + return { + ...resolveResult, + saved_object: await this.redactSavedObjectNamespaces(resolveResult.saved_object, [ + options.namespace, + ]), + }; + } + public async update( type: string, id: string, diff --git a/x-pack/plugins/security/server/session_management/index.ts b/x-pack/plugins/security/server/session_management/index.ts index ee7ed914947a..1d256885f49f 100644 --- a/x-pack/plugins/security/server/session_management/index.ts +++ b/x-pack/plugins/security/server/session_management/index.ts @@ -6,6 +6,6 @@ export { Session, SessionValue } from './session'; export { - SessionManagementServiceSetup, + SessionManagementServiceStart, SessionManagementService, } from './session_management_service'; diff --git a/x-pack/plugins/security/server/session_management/session_index.test.ts b/x-pack/plugins/security/server/session_management/session_index.test.ts index 1dd47c7ff66e..51abcfe00253 100644 --- a/x-pack/plugins/security/server/session_management/session_index.test.ts +++ b/x-pack/plugins/security/server/session_management/session_index.test.ts @@ -4,27 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ILegacyClusterClient } from '../../../../../src/core/server'; +import { errors } from '@elastic/elasticsearch'; +import { DeeplyMockedKeys } from '@kbn/utility-types/jest'; +import { ElasticsearchClient } from '../../../../../src/core/server'; import { ConfigSchema, createConfig } from '../config'; import { getSessionIndexTemplate, SessionIndex } from './session_index'; import { loggingSystemMock, elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; +import { securityMock } from '../mocks'; import { sessionIndexMock } from './session_index.mock'; describe('Session index', () => { - let mockClusterClient: jest.Mocked; + let mockElasticsearchClient: DeeplyMockedKeys; let sessionIndex: SessionIndex; const indexName = '.kibana_some_tenant_security_session_1'; const indexTemplateName = '.kibana_some_tenant_security_session_index_template_1'; beforeEach(() => { - mockClusterClient = elasticsearchServiceMock.createLegacyClusterClient(); + mockElasticsearchClient = elasticsearchServiceMock.createElasticsearchClient(); const sessionIndexOptions = { logger: loggingSystemMock.createLogger(), kibanaIndexName: '.kibana_some_tenant', config: createConfig(ConfigSchema.validate({}), loggingSystemMock.createLogger(), { isTLSEnabled: false, }), - clusterClient: mockClusterClient, + elasticsearchClient: mockElasticsearchClient, }; sessionIndex = new SessionIndex(sessionIndexOptions); @@ -32,22 +35,21 @@ describe('Session index', () => { describe('#initialize', () => { function assertExistenceChecksPerformed() { - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('indices.existsTemplate', { + expect(mockElasticsearchClient.indices.existsTemplate).toHaveBeenCalledWith({ name: indexTemplateName, }); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('indices.exists', { + expect(mockElasticsearchClient.indices.exists).toHaveBeenCalledWith({ index: getSessionIndexTemplate(indexName).index_patterns, }); } it('debounces initialize calls', async () => { - mockClusterClient.callAsInternalUser.mockImplementation(async (method) => { - if (method === 'indices.existsTemplate' || method === 'indices.exists') { - return true; - } - - throw new Error('Unexpected call'); - }); + mockElasticsearchClient.indices.existsTemplate.mockResolvedValue( + securityMock.createApiResponse({ body: true }) + ); + mockElasticsearchClient.indices.exists.mockResolvedValue( + securityMock.createApiResponse({ body: true }) + ); await Promise.all([ sessionIndex.initialize(), @@ -56,112 +58,102 @@ describe('Session index', () => { sessionIndex.initialize(), ]); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); assertExistenceChecksPerformed(); }); it('creates neither index template nor index if they exist', async () => { - mockClusterClient.callAsInternalUser.mockImplementation(async (method) => { - if (method === 'indices.existsTemplate' || method === 'indices.exists') { - return true; - } - - throw new Error('Unexpected call'); - }); + mockElasticsearchClient.indices.existsTemplate.mockResolvedValue( + securityMock.createApiResponse({ body: true }) + ); + mockElasticsearchClient.indices.exists.mockResolvedValue( + securityMock.createApiResponse({ body: true }) + ); await sessionIndex.initialize(); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); assertExistenceChecksPerformed(); }); it('creates both index template and index if they do not exist', async () => { - mockClusterClient.callAsInternalUser.mockImplementation(async (method) => { - if (method === 'indices.existsTemplate' || method === 'indices.exists') { - return false; - } - }); + mockElasticsearchClient.indices.existsTemplate.mockResolvedValue( + securityMock.createApiResponse({ body: false }) + ); + mockElasticsearchClient.indices.exists.mockResolvedValue( + securityMock.createApiResponse({ body: false }) + ); await sessionIndex.initialize(); const expectedIndexTemplate = getSessionIndexTemplate(indexName); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(4); assertExistenceChecksPerformed(); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('indices.putTemplate', { + expect(mockElasticsearchClient.indices.putTemplate).toHaveBeenCalledWith({ name: indexTemplateName, body: expectedIndexTemplate, }); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('indices.create', { + expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith({ index: expectedIndexTemplate.index_patterns, }); }); it('creates only index template if it does not exist even if index exists', async () => { - mockClusterClient.callAsInternalUser.mockImplementation(async (method) => { - if (method === 'indices.existsTemplate') { - return false; - } - - if (method === 'indices.exists') { - return true; - } - }); + mockElasticsearchClient.indices.existsTemplate.mockResolvedValue( + securityMock.createApiResponse({ body: false }) + ); + mockElasticsearchClient.indices.exists.mockResolvedValue( + securityMock.createApiResponse({ body: true }) + ); await sessionIndex.initialize(); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(3); assertExistenceChecksPerformed(); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('indices.putTemplate', { + expect(mockElasticsearchClient.indices.putTemplate).toHaveBeenCalledWith({ name: indexTemplateName, body: getSessionIndexTemplate(indexName), }); }); it('creates only index if it does not exist even if index template exists', async () => { - mockClusterClient.callAsInternalUser.mockImplementation(async (method) => { - if (method === 'indices.existsTemplate') { - return true; - } - - if (method === 'indices.exists') { - return false; - } - }); + mockElasticsearchClient.indices.existsTemplate.mockResolvedValue( + securityMock.createApiResponse({ body: true }) + ); + mockElasticsearchClient.indices.exists.mockResolvedValue( + securityMock.createApiResponse({ body: false }) + ); await sessionIndex.initialize(); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(3); assertExistenceChecksPerformed(); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('indices.create', { + expect(mockElasticsearchClient.indices.create).toHaveBeenCalledWith({ index: getSessionIndexTemplate(indexName).index_patterns, }); }); it('does not fail if tries to create index when it exists already', async () => { - mockClusterClient.callAsInternalUser.mockImplementation(async (method) => { - if (method === 'indices.existsTemplate') { - return true; - } - - if (method === 'indices.exists') { - return false; - } - - if (method === 'indices.create') { - // eslint-disable-next-line no-throw-literal - throw { body: { error: { type: 'resource_already_exists_exception' } } }; - } - }); + mockElasticsearchClient.indices.existsTemplate.mockResolvedValue( + securityMock.createApiResponse({ body: true }) + ); + mockElasticsearchClient.indices.exists.mockResolvedValue( + securityMock.createApiResponse({ body: false }) + ); + mockElasticsearchClient.indices.create.mockRejectedValue( + new errors.ResponseError( + securityMock.createApiResponse({ + body: { error: { type: 'resource_already_exists_exception' } }, + }) + ) + ); await sessionIndex.initialize(); }); it('works properly after failure', async () => { - const unexpectedError = new Error('Uh! Oh!'); - mockClusterClient.callAsInternalUser.mockImplementationOnce(() => - Promise.reject(unexpectedError) + const unexpectedError = new errors.ResponseError( + securityMock.createApiResponse(securityMock.createApiResponse({ body: { type: 'Uh oh.' } })) + ); + mockElasticsearchClient.indices.existsTemplate.mockRejectedValueOnce(unexpectedError); + mockElasticsearchClient.indices.existsTemplate.mockResolvedValueOnce( + securityMock.createApiResponse({ body: true }) ); - mockClusterClient.callAsInternalUser.mockImplementationOnce(() => Promise.resolve(true)); await expect(sessionIndex.initialize()).rejects.toBe(unexpectedError); await expect(sessionIndex.initialize()).resolves.toBe(undefined); @@ -171,13 +163,17 @@ describe('Session index', () => { describe('cleanUp', () => { const now = 123456; beforeEach(() => { - mockClusterClient.callAsInternalUser.mockResolvedValue({}); + mockElasticsearchClient.deleteByQuery.mockResolvedValue( + securityMock.createApiResponse({ body: {} }) + ); jest.spyOn(Date, 'now').mockImplementation(() => now); }); it('throws if call to Elasticsearch fails', async () => { - const failureReason = new Error('Uh oh.'); - mockClusterClient.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse(securityMock.createApiResponse({ body: { type: 'Uh oh.' } })) + ); + mockElasticsearchClient.deleteByQuery.mockRejectedValue(failureReason); await expect(sessionIndex.cleanUp()).rejects.toBe(failureReason); }); @@ -185,53 +181,55 @@ describe('Session index', () => { it('when neither `lifespan` nor `idleTimeout` is configured', async () => { await sessionIndex.cleanUp(); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('deleteByQuery', { - index: indexName, - refresh: 'wait_for', - ignore: [409, 404], - body: { - query: { - bool: { - should: [ - // All expired sessions based on the lifespan, no matter which provider they belong to. - { range: { lifespanExpiration: { lte: now } } }, - // All sessions that belong to the providers that aren't configured. - { - bool: { - must_not: { - bool: { - should: [ - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic' } }, - ], + expect(mockElasticsearchClient.deleteByQuery).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.deleteByQuery).toHaveBeenCalledWith( + { + index: indexName, + refresh: true, + body: { + query: { + bool: { + should: [ + // All expired sessions based on the lifespan, no matter which provider they belong to. + { range: { lifespanExpiration: { lte: now } } }, + // All sessions that belong to the providers that aren't configured. + { + bool: { + must_not: { + bool: { + should: [ + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic' } }, + ], + }, }, - }, - ], - minimum_should_match: 1, + ], + minimum_should_match: 1, + }, }, }, }, - }, - // The sessions that belong to a particular provider that are expired based on the idle timeout. - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic' } }, - ], - should: [{ range: { idleTimeoutExpiration: { lte: now } } }], - minimum_should_match: 1, + // The sessions that belong to a particular provider that are expired based on the idle timeout. + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic' } }, + ], + should: [{ range: { idleTimeoutExpiration: { lte: now } } }], + minimum_should_match: 1, + }, }, - }, - ], + ], + }, }, }, }, - }); + { ignore: [409, 404] } + ); }); it('when only `lifespan` is configured', async () => { @@ -243,68 +241,70 @@ describe('Session index', () => { loggingSystemMock.createLogger(), { isTLSEnabled: false } ), - clusterClient: mockClusterClient, + elasticsearchClient: mockElasticsearchClient, }); await sessionIndex.cleanUp(); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('deleteByQuery', { - index: indexName, - refresh: 'wait_for', - ignore: [409, 404], - body: { - query: { - bool: { - should: [ - // All expired sessions based on the lifespan, no matter which provider they belong to. - { range: { lifespanExpiration: { lte: now } } }, - // All sessions that belong to the providers that aren't configured. - { - bool: { - must_not: { - bool: { - should: [ - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic' } }, - ], + expect(mockElasticsearchClient.deleteByQuery).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.deleteByQuery).toHaveBeenCalledWith( + { + index: indexName, + refresh: true, + body: { + query: { + bool: { + should: [ + // All expired sessions based on the lifespan, no matter which provider they belong to. + { range: { lifespanExpiration: { lte: now } } }, + // All sessions that belong to the providers that aren't configured. + { + bool: { + must_not: { + bool: { + should: [ + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic' } }, + ], + }, }, - }, - ], - minimum_should_match: 1, + ], + minimum_should_match: 1, + }, }, }, }, - }, - // The sessions that belong to a particular provider but don't have a configured lifespan. - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic' } }, - ], - must_not: { exists: { field: 'lifespanExpiration' } }, + // The sessions that belong to a particular provider but don't have a configured lifespan. + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic' } }, + ], + must_not: { exists: { field: 'lifespanExpiration' } }, + }, }, - }, - // The sessions that belong to a particular provider that are expired based on the idle timeout. - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic' } }, - ], - should: [{ range: { idleTimeoutExpiration: { lte: now } } }], - minimum_should_match: 1, + // The sessions that belong to a particular provider that are expired based on the idle timeout. + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic' } }, + ], + should: [{ range: { idleTimeoutExpiration: { lte: now } } }], + minimum_should_match: 1, + }, }, - }, - ], + ], + }, }, }, }, - }); + { ignore: [409, 404] } + ); }); it('when only `idleTimeout` is configured', async () => { @@ -317,62 +317,64 @@ describe('Session index', () => { loggingSystemMock.createLogger(), { isTLSEnabled: false } ), - clusterClient: mockClusterClient, + elasticsearchClient: mockElasticsearchClient, }); await sessionIndex.cleanUp(); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('deleteByQuery', { - index: indexName, - refresh: 'wait_for', - ignore: [409, 404], - body: { - query: { - bool: { - should: [ - // All expired sessions based on the lifespan, no matter which provider they belong to. - { range: { lifespanExpiration: { lte: now } } }, - // All sessions that belong to the providers that aren't configured. - { - bool: { - must_not: { - bool: { - should: [ - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic' } }, - ], + expect(mockElasticsearchClient.deleteByQuery).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.deleteByQuery).toHaveBeenCalledWith( + { + index: indexName, + refresh: true, + body: { + query: { + bool: { + should: [ + // All expired sessions based on the lifespan, no matter which provider they belong to. + { range: { lifespanExpiration: { lte: now } } }, + // All sessions that belong to the providers that aren't configured. + { + bool: { + must_not: { + bool: { + should: [ + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic' } }, + ], + }, }, - }, - ], - minimum_should_match: 1, + ], + minimum_should_match: 1, + }, }, }, }, - }, - // The sessions that belong to a particular provider that are either expired based on the idle timeout - // or don't have it configured at all. - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic' } }, - ], - should: [ - { range: { idleTimeoutExpiration: { lte: now - 3 * idleTimeout } } }, - { bool: { must_not: { exists: { field: 'idleTimeoutExpiration' } } } }, - ], - minimum_should_match: 1, + // The sessions that belong to a particular provider that are either expired based on the idle timeout + // or don't have it configured at all. + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic' } }, + ], + should: [ + { range: { idleTimeoutExpiration: { lte: now - 3 * idleTimeout } } }, + { bool: { must_not: { exists: { field: 'idleTimeoutExpiration' } } } }, + ], + minimum_should_match: 1, + }, }, - }, - ], + ], + }, }, }, }, - }); + { ignore: [409, 404] } + ); }); it('when both `lifespan` and `idleTimeout` are configured', async () => { @@ -385,72 +387,74 @@ describe('Session index', () => { loggingSystemMock.createLogger(), { isTLSEnabled: false } ), - clusterClient: mockClusterClient, + elasticsearchClient: mockElasticsearchClient, }); await sessionIndex.cleanUp(); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('deleteByQuery', { - index: indexName, - refresh: 'wait_for', - ignore: [409, 404], - body: { - query: { - bool: { - should: [ - // All expired sessions based on the lifespan, no matter which provider they belong to. - { range: { lifespanExpiration: { lte: now } } }, - // All sessions that belong to the providers that aren't configured. - { - bool: { - must_not: { - bool: { - should: [ - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic' } }, - ], + expect(mockElasticsearchClient.deleteByQuery).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.deleteByQuery).toHaveBeenCalledWith( + { + index: indexName, + refresh: true, + body: { + query: { + bool: { + should: [ + // All expired sessions based on the lifespan, no matter which provider they belong to. + { range: { lifespanExpiration: { lte: now } } }, + // All sessions that belong to the providers that aren't configured. + { + bool: { + must_not: { + bool: { + should: [ + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic' } }, + ], + }, }, - }, - ], - minimum_should_match: 1, + ], + minimum_should_match: 1, + }, }, }, }, - }, - // The sessions that belong to a particular provider but don't have a configured lifespan. - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic' } }, - ], - must_not: { exists: { field: 'lifespanExpiration' } }, + // The sessions that belong to a particular provider but don't have a configured lifespan. + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic' } }, + ], + must_not: { exists: { field: 'lifespanExpiration' } }, + }, }, - }, - // The sessions that belong to a particular provider that are either expired based on the idle timeout - // or don't have it configured at all. - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic' } }, - ], - should: [ - { range: { idleTimeoutExpiration: { lte: now - 3 * idleTimeout } } }, - { bool: { must_not: { exists: { field: 'idleTimeoutExpiration' } } } }, - ], - minimum_should_match: 1, + // The sessions that belong to a particular provider that are either expired based on the idle timeout + // or don't have it configured at all. + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic' } }, + ], + should: [ + { range: { idleTimeoutExpiration: { lte: now - 3 * idleTimeout } } }, + { bool: { must_not: { exists: { field: 'idleTimeoutExpiration' } } } }, + ], + minimum_should_match: 1, + }, }, - }, - ], + ], + }, }, }, }, - }); + { ignore: [409, 404] } + ); }); it('when both `lifespan` and `idleTimeout` are configured and multiple providers are enabled', async () => { @@ -478,127 +482,132 @@ describe('Session index', () => { loggingSystemMock.createLogger(), { isTLSEnabled: false } ), - clusterClient: mockClusterClient, + elasticsearchClient: mockElasticsearchClient, }); await sessionIndex.cleanUp(); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('deleteByQuery', { - index: indexName, - refresh: 'wait_for', - ignore: [409, 404], - body: { - query: { - bool: { - should: [ - // All expired sessions based on the lifespan, no matter which provider they belong to. - { range: { lifespanExpiration: { lte: now } } }, - // All sessions that belong to the providers that aren't configured. - { - bool: { - must_not: { - bool: { - should: [ - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic1' } }, - ], + expect(mockElasticsearchClient.deleteByQuery).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.deleteByQuery).toHaveBeenCalledWith( + { + index: indexName, + refresh: true, + body: { + query: { + bool: { + should: [ + // All expired sessions based on the lifespan, no matter which provider they belong to. + { range: { lifespanExpiration: { lte: now } } }, + // All sessions that belong to the providers that aren't configured. + { + bool: { + must_not: { + bool: { + should: [ + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic1' } }, + ], + }, }, - }, - { - bool: { - must: [ - { term: { 'provider.type': 'saml' } }, - { term: { 'provider.name': 'saml1' } }, - ], + { + bool: { + must: [ + { term: { 'provider.type': 'saml' } }, + { term: { 'provider.name': 'saml1' } }, + ], + }, }, - }, - ], - minimum_should_match: 1, + ], + minimum_should_match: 1, + }, }, }, }, - }, - // The sessions that belong to a Basic provider but don't have a configured lifespan. - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic1' } }, - ], - must_not: { exists: { field: 'lifespanExpiration' } }, + // The sessions that belong to a Basic provider but don't have a configured lifespan. + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic1' } }, + ], + must_not: { exists: { field: 'lifespanExpiration' } }, + }, }, - }, - // The sessions that belong to a Basic provider that are either expired based on the idle timeout - // or don't have it configured at all. - { - bool: { - must: [ - { term: { 'provider.type': 'basic' } }, - { term: { 'provider.name': 'basic1' } }, - ], - should: [ - { range: { idleTimeoutExpiration: { lte: now - 3 * globalIdleTimeout } } }, - { bool: { must_not: { exists: { field: 'idleTimeoutExpiration' } } } }, - ], - minimum_should_match: 1, + // The sessions that belong to a Basic provider that are either expired based on the idle timeout + // or don't have it configured at all. + { + bool: { + must: [ + { term: { 'provider.type': 'basic' } }, + { term: { 'provider.name': 'basic1' } }, + ], + should: [ + { range: { idleTimeoutExpiration: { lte: now - 3 * globalIdleTimeout } } }, + { bool: { must_not: { exists: { field: 'idleTimeoutExpiration' } } } }, + ], + minimum_should_match: 1, + }, }, - }, - // The sessions that belong to a SAML provider but don't have a configured lifespan. - { - bool: { - must: [ - { term: { 'provider.type': 'saml' } }, - { term: { 'provider.name': 'saml1' } }, - ], - must_not: { exists: { field: 'lifespanExpiration' } }, + // The sessions that belong to a SAML provider but don't have a configured lifespan. + { + bool: { + must: [ + { term: { 'provider.type': 'saml' } }, + { term: { 'provider.name': 'saml1' } }, + ], + must_not: { exists: { field: 'lifespanExpiration' } }, + }, }, - }, - // The sessions that belong to a SAML provider that are either expired based on the idle timeout - // or don't have it configured at all. - { - bool: { - must: [ - { term: { 'provider.type': 'saml' } }, - { term: { 'provider.name': 'saml1' } }, - ], - should: [ - { range: { idleTimeoutExpiration: { lte: now - 3 * samlIdleTimeout } } }, - { bool: { must_not: { exists: { field: 'idleTimeoutExpiration' } } } }, - ], - minimum_should_match: 1, + // The sessions that belong to a SAML provider that are either expired based on the idle timeout + // or don't have it configured at all. + { + bool: { + must: [ + { term: { 'provider.type': 'saml' } }, + { term: { 'provider.name': 'saml1' } }, + ], + should: [ + { range: { idleTimeoutExpiration: { lte: now - 3 * samlIdleTimeout } } }, + { bool: { must_not: { exists: { field: 'idleTimeoutExpiration' } } } }, + ], + minimum_should_match: 1, + }, }, - }, - ], + ], + }, }, }, }, - }); + { ignore: [409, 404] } + ); }); }); describe('#get', () => { it('throws if call to Elasticsearch fails', async () => { - const failureReason = new Error('Uh oh.'); - mockClusterClient.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse(securityMock.createApiResponse({ body: { type: 'Uh oh.' } })) + ); + mockElasticsearchClient.get.mockRejectedValue(failureReason); await expect(sessionIndex.get('some-sid')).rejects.toBe(failureReason); }); it('returns `null` if index is not found', async () => { - mockClusterClient.callAsInternalUser.mockResolvedValue({ status: 404 }); + mockElasticsearchClient.get.mockResolvedValue( + securityMock.createApiResponse({ statusCode: 404, body: { status: 404 } }) + ); await expect(sessionIndex.get('some-sid')).resolves.toBeNull(); }); it('returns `null` if session index value document is not found', async () => { - mockClusterClient.callAsInternalUser.mockResolvedValue({ - found: false, - status: 200, - }); + mockElasticsearchClient.get.mockResolvedValue( + securityMock.createApiResponse({ body: { status: 200, found: false } }) + ); await expect(sessionIndex.get('some-sid')).resolves.toBeNull(); }); @@ -612,13 +621,17 @@ describe('Session index', () => { content: 'some-encrypted-content', }; - mockClusterClient.callAsInternalUser.mockResolvedValue({ - found: true, - status: 200, - _source: indexDocumentSource, - _primary_term: 1, - _seq_no: 456, - }); + mockElasticsearchClient.get.mockResolvedValue( + securityMock.createApiResponse({ + body: { + found: true, + status: 200, + _source: indexDocumentSource, + _primary_term: 1, + _seq_no: 456, + }, + }) + ); await expect(sessionIndex.get('some-sid')).resolves.toEqual({ ...indexDocumentSource, @@ -626,19 +639,20 @@ describe('Session index', () => { metadata: { primaryTerm: 1, sequenceNumber: 456 }, }); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('get', { - id: 'some-sid', - ignore: [404], - index: indexName, - }); + expect(mockElasticsearchClient.get).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.get).toHaveBeenCalledWith( + { id: 'some-sid', index: indexName }, + { ignore: [404] } + ); }); }); describe('#create', () => { it('throws if call to Elasticsearch fails', async () => { - const failureReason = new Error('Uh oh.'); - mockClusterClient.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse(securityMock.createApiResponse({ body: { type: 'Uh oh.' } })) + ); + mockElasticsearchClient.create.mockRejectedValue(failureReason); await expect( sessionIndex.create({ @@ -653,10 +667,9 @@ describe('Session index', () => { }); it('properly stores session value in the index', async () => { - mockClusterClient.callAsInternalUser.mockResolvedValue({ - _primary_term: 321, - _seq_no: 654, - }); + mockElasticsearchClient.create.mockResolvedValue( + securityMock.createApiResponse({ body: { _primary_term: 321, _seq_no: 654 } }) + ); const sid = 'some-long-sid'; const sessionValue = { @@ -673,8 +686,8 @@ describe('Session index', () => { metadata: { primaryTerm: 321, sequenceNumber: 654 }, }); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('create', { + expect(mockElasticsearchClient.create).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.create).toHaveBeenCalledWith({ id: sid, index: indexName, body: sessionValue, @@ -685,8 +698,10 @@ describe('Session index', () => { describe('#update', () => { it('throws if call to Elasticsearch fails', async () => { - const failureReason = new Error('Uh oh.'); - mockClusterClient.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse(securityMock.createApiResponse({ body: { type: 'Uh oh.' } })) + ); + mockElasticsearchClient.index.mockRejectedValue(failureReason); await expect(sessionIndex.update(sessionIndexMock.createValue())).rejects.toBe(failureReason); }); @@ -700,21 +715,20 @@ describe('Session index', () => { content: 'some-updated-encrypted-content', }; - mockClusterClient.callAsInternalUser.mockImplementation(async (method) => { - if (method === 'get') { - return { + mockElasticsearchClient.get.mockResolvedValue( + securityMock.createApiResponse({ + body: { found: true, status: 200, _source: latestSessionValue, _primary_term: 321, _seq_no: 654, - }; - } - - if (method === 'index') { - return { status: 409 }; - } - }); + }, + }) + ); + mockElasticsearchClient.index.mockResolvedValue( + securityMock.createApiResponse({ statusCode: 409, body: { status: 409 } }) + ); const sid = 'some-long-sid'; const metadata = { primaryTerm: 123, sequenceNumber: 456 }; @@ -732,24 +746,23 @@ describe('Session index', () => { metadata: { primaryTerm: 321, sequenceNumber: 654 }, }); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(2); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('index', { - id: sid, - index: indexName, - body: sessionValue, - ifSeqNo: 456, - ifPrimaryTerm: 123, - refresh: 'wait_for', - ignore: [409], - }); + expect(mockElasticsearchClient.index).toHaveBeenCalledWith( + { + id: sid, + index: indexName, + body: sessionValue, + if_seq_no: 456, + if_primary_term: 123, + refresh: 'wait_for', + }, + { ignore: [409] } + ); }); it('properly stores session value in the index', async () => { - mockClusterClient.callAsInternalUser.mockResolvedValue({ - _primary_term: 321, - _seq_no: 654, - status: 200, - }); + mockElasticsearchClient.index.mockResolvedValue( + securityMock.createApiResponse({ body: { _primary_term: 321, _seq_no: 654, status: 200 } }) + ); const sid = 'some-long-sid'; const metadata = { primaryTerm: 123, sequenceNumber: 456 }; @@ -767,23 +780,27 @@ describe('Session index', () => { metadata: { primaryTerm: 321, sequenceNumber: 654 }, }); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('index', { - id: sid, - index: indexName, - body: sessionValue, - ifSeqNo: 456, - ifPrimaryTerm: 123, - refresh: 'wait_for', - ignore: [409], - }); + expect(mockElasticsearchClient.index).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.index).toHaveBeenCalledWith( + { + id: sid, + index: indexName, + body: sessionValue, + if_seq_no: 456, + if_primary_term: 123, + refresh: 'wait_for', + }, + { ignore: [409] } + ); }); }); describe('#clear', () => { it('throws if call to Elasticsearch fails', async () => { - const failureReason = new Error('Uh oh.'); - mockClusterClient.callAsInternalUser.mockRejectedValue(failureReason); + const failureReason = new errors.ResponseError( + securityMock.createApiResponse(securityMock.createApiResponse({ body: { type: 'Uh oh.' } })) + ); + mockElasticsearchClient.delete.mockRejectedValue(failureReason); await expect(sessionIndex.clear('some-long-sid')).rejects.toBe(failureReason); }); @@ -791,13 +808,11 @@ describe('Session index', () => { it('properly removes session value from the index', async () => { await sessionIndex.clear('some-long-sid'); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledTimes(1); - expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('delete', { - id: 'some-long-sid', - index: indexName, - refresh: 'wait_for', - ignore: [404], - }); + expect(mockElasticsearchClient.delete).toHaveBeenCalledTimes(1); + expect(mockElasticsearchClient.delete).toHaveBeenCalledWith( + { id: 'some-long-sid', index: indexName, refresh: 'wait_for' }, + { ignore: [404] } + ); }); }); }); diff --git a/x-pack/plugins/security/server/session_management/session_index.ts b/x-pack/plugins/security/server/session_management/session_index.ts index 45b2f4489c19..13250531d391 100644 --- a/x-pack/plugins/security/server/session_management/session_index.ts +++ b/x-pack/plugins/security/server/session_management/session_index.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import type { ILegacyClusterClient, Logger } from '../../../../../src/core/server'; +import type { ElasticsearchClient, Logger } from '../../../../../src/core/server'; import type { AuthenticationProvider } from '../../common/model'; import type { ConfigType } from '../config'; export interface SessionIndexOptions { - readonly clusterClient: ILegacyClusterClient; + readonly elasticsearchClient: ElasticsearchClient; readonly kibanaIndexName: string; readonly config: Pick; readonly logger: Logger; @@ -137,11 +137,10 @@ export class SessionIndex { */ async get(sid: string) { try { - const response = await this.options.clusterClient.callAsInternalUser('get', { - id: sid, - ignore: [404], - index: this.indexName, - }); + const { body: response } = await this.options.elasticsearchClient.get( + { id: sid, index: this.indexName }, + { ignore: [404] } + ); const docNotFound = response.found === false; const indexNotFound = response.status === 404; @@ -176,9 +175,8 @@ export class SessionIndex { const { sid, ...sessionValueToStore } = sessionValue; try { const { - _primary_term: primaryTerm, - _seq_no: sequenceNumber, - } = await this.options.clusterClient.callAsInternalUser('create', { + body: { _primary_term: primaryTerm, _seq_no: sequenceNumber }, + } = await this.options.elasticsearchClient.create({ id: sid, // We cannot control whether index is created automatically during this operation or not. // But we can reduce probability of getting into a weird state when session is being created @@ -203,15 +201,17 @@ export class SessionIndex { async update(sessionValue: Readonly) { const { sid, metadata, ...sessionValueToStore } = sessionValue; try { - const response = await this.options.clusterClient.callAsInternalUser('index', { - id: sid, - index: this.indexName, - body: sessionValueToStore, - ifSeqNo: metadata.sequenceNumber, - ifPrimaryTerm: metadata.primaryTerm, - refresh: 'wait_for', - ignore: [409], - }); + const { body: response } = await this.options.elasticsearchClient.index( + { + id: sid, + index: this.indexName, + body: sessionValueToStore, + if_seq_no: metadata.sequenceNumber, + if_primary_term: metadata.primaryTerm, + refresh: 'wait_for', + }, + { ignore: [409] } + ); // We don't want to override changes that were made after we fetched session value or // re-create it if has been deleted already. If we detect such a case we discard changes and @@ -242,12 +242,10 @@ export class SessionIndex { try { // We don't specify primary term and sequence number as delete should always take precedence // over any updates that could happen in the meantime. - await this.options.clusterClient.callAsInternalUser('delete', { - id: sid, - index: this.indexName, - refresh: 'wait_for', - ignore: [404], - }); + await this.options.elasticsearchClient.delete( + { id: sid, index: this.indexName, refresh: 'wait_for' }, + { ignore: [404] } + ); } catch (err) { this.options.logger.error(`Failed to clear session value: ${err.message}`); throw err; @@ -267,10 +265,11 @@ export class SessionIndex { // Check if required index template exists. let indexTemplateExists = false; try { - indexTemplateExists = await this.options.clusterClient.callAsInternalUser( - 'indices.existsTemplate', - { name: sessionIndexTemplateName } - ); + indexTemplateExists = ( + await this.options.elasticsearchClient.indices.existsTemplate({ + name: sessionIndexTemplateName, + }) + ).body; } catch (err) { this.options.logger.error( `Failed to check if session index template exists: ${err.message}` @@ -283,7 +282,7 @@ export class SessionIndex { this.options.logger.debug('Session index template already exists.'); } else { try { - await this.options.clusterClient.callAsInternalUser('indices.putTemplate', { + await this.options.elasticsearchClient.indices.putTemplate({ name: sessionIndexTemplateName, body: getSessionIndexTemplate(this.indexName), }); @@ -298,9 +297,9 @@ export class SessionIndex { // always enabled, so we create session index explicitly. let indexExists = false; try { - indexExists = await this.options.clusterClient.callAsInternalUser('indices.exists', { - index: this.indexName, - }); + indexExists = ( + await this.options.elasticsearchClient.indices.exists({ index: this.indexName }) + ).body; } catch (err) { this.options.logger.error(`Failed to check if session index exists: ${err.message}`); return reject(err); @@ -311,9 +310,7 @@ export class SessionIndex { this.options.logger.debug('Session index already exists.'); } else { try { - await this.options.clusterClient.callAsInternalUser('indices.create', { - index: this.indexName, - }); + await this.options.elasticsearchClient.indices.create({ index: this.indexName }); this.options.logger.debug('Successfully created session index.'); } catch (err) { // There can be a race condition if index is created by another Kibana instance. @@ -399,12 +396,14 @@ export class SessionIndex { } try { - const response = await this.options.clusterClient.callAsInternalUser('deleteByQuery', { - index: this.indexName, - refresh: 'wait_for', - ignore: [409, 404], - body: { query: { bool: { should: deleteQueries } } }, - }); + const { body: response } = await this.options.elasticsearchClient.deleteByQuery( + { + index: this.indexName, + refresh: true, + body: { query: { bool: { should: deleteQueries } } }, + }, + { ignore: [409, 404] } + ); if (response.deleted > 0) { this.options.logger.debug( diff --git a/x-pack/plugins/security/server/session_management/session_management_service.test.ts b/x-pack/plugins/security/server/session_management/session_management_service.test.ts index 08d4c491d155..d3e5c876c997 100644 --- a/x-pack/plugins/security/server/session_management/session_management_service.test.ts +++ b/x-pack/plugins/security/server/session_management/session_management_service.test.ts @@ -21,7 +21,7 @@ import { loggingSystemMock, } from '../../../../../src/core/server/mocks'; import { taskManagerMock } from '../../../task_manager/server/mocks'; -import { TaskManagerStartContract } from '../../../task_manager/server'; +import { TaskManagerStartContract, TaskRunCreatorFunction } from '../../../task_manager/server'; describe('SessionManagementService', () => { let service: SessionManagementService; @@ -30,21 +30,19 @@ describe('SessionManagementService', () => { }); describe('setup()', () => { - it('exposes proper contract', () => { + it('registers cleanup task', () => { const mockCoreSetup = coreMock.createSetup(); const mockTaskManager = taskManagerMock.createSetup(); expect( service.setup({ - clusterClient: elasticsearchServiceMock.createLegacyClusterClient(), http: mockCoreSetup.http, config: createConfig(ConfigSchema.validate({}), loggingSystemMock.createLogger(), { isTLSEnabled: false, }), - kibanaIndexName: '.kibana', taskManager: mockTaskManager, }) - ).toEqual({ session: expect.any(Session) }); + ).toBeUndefined(); expect(mockTaskManager.registerTaskDefinitions).toHaveBeenCalledTimes(1); expect(mockTaskManager.registerTaskDefinitions).toHaveBeenCalledWith({ @@ -54,60 +52,35 @@ describe('SessionManagementService', () => { }, }); }); - - it('registers proper session index cleanup task runner', () => { - const mockSessionIndexCleanUp = jest.spyOn(SessionIndex.prototype, 'cleanUp'); - const mockTaskManager = taskManagerMock.createSetup(); - - const mockClusterClient = elasticsearchServiceMock.createLegacyClusterClient(); - mockClusterClient.callAsInternalUser.mockResolvedValue({}); - service.setup({ - clusterClient: mockClusterClient, - http: coreMock.createSetup().http, - config: createConfig(ConfigSchema.validate({}), loggingSystemMock.createLogger(), { - isTLSEnabled: false, - }), - kibanaIndexName: '.kibana', - taskManager: mockTaskManager, - }); - - const [ - [ - { - [SESSION_INDEX_CLEANUP_TASK_NAME]: { createTaskRunner }, - }, - ], - ] = mockTaskManager.registerTaskDefinitions.mock.calls; - expect(mockSessionIndexCleanUp).not.toHaveBeenCalled(); - - const runner = createTaskRunner({} as any); - runner.run(); - expect(mockSessionIndexCleanUp).toHaveBeenCalledTimes(1); - - runner.run(); - expect(mockSessionIndexCleanUp).toHaveBeenCalledTimes(2); - }); }); describe('start()', () => { let mockSessionIndexInitialize: jest.SpyInstance; let mockTaskManager: jest.Mocked; + let sessionCleanupTaskRunCreator: TaskRunCreatorFunction; beforeEach(() => { mockSessionIndexInitialize = jest.spyOn(SessionIndex.prototype, 'initialize'); mockTaskManager = taskManagerMock.createStart(); mockTaskManager.ensureScheduled.mockResolvedValue(undefined as any); - const mockCoreSetup = coreMock.createSetup(); + const mockTaskManagerSetup = taskManagerMock.createSetup(); service.setup({ - clusterClient: elasticsearchServiceMock.createLegacyClusterClient(), - http: mockCoreSetup.http, + http: coreMock.createSetup().http, config: createConfig(ConfigSchema.validate({}), loggingSystemMock.createLogger(), { isTLSEnabled: false, }), - kibanaIndexName: '.kibana', - taskManager: taskManagerMock.createSetup(), + taskManager: mockTaskManagerSetup, }); + + const [ + [ + { + [SESSION_INDEX_CLEANUP_TASK_NAME]: { createTaskRunner }, + }, + ], + ] = mockTaskManagerSetup.registerTaskDefinitions.mock.calls; + sessionCleanupTaskRunCreator = createTaskRunner; }); afterEach(() => { @@ -117,13 +90,43 @@ describe('SessionManagementService', () => { it('exposes proper contract', () => { const mockStatusSubject = new Subject(); expect( - service.start({ online$: mockStatusSubject.asObservable(), taskManager: mockTaskManager }) - ).toBeUndefined(); + service.start({ + elasticsearchClient: elasticsearchServiceMock.createElasticsearchClient(), + kibanaIndexName: '.kibana', + online$: mockStatusSubject.asObservable(), + taskManager: mockTaskManager, + }) + ).toEqual({ session: expect.any(Session) }); + }); + + it('registers proper session index cleanup task runner', () => { + const mockSessionIndexCleanUp = jest.spyOn(SessionIndex.prototype, 'cleanUp'); + const mockStatusSubject = new Subject(); + service.start({ + elasticsearchClient: elasticsearchServiceMock.createElasticsearchClient(), + kibanaIndexName: '.kibana', + online$: mockStatusSubject.asObservable(), + taskManager: mockTaskManager, + }); + + expect(mockSessionIndexCleanUp).not.toHaveBeenCalled(); + + const runner = sessionCleanupTaskRunCreator({} as any); + runner.run(); + expect(mockSessionIndexCleanUp).toHaveBeenCalledTimes(1); + + runner.run(); + expect(mockSessionIndexCleanUp).toHaveBeenCalledTimes(2); }); it('initializes session index and schedules session index cleanup task when Elasticsearch goes online', async () => { const mockStatusSubject = new Subject(); - service.start({ online$: mockStatusSubject.asObservable(), taskManager: mockTaskManager }); + service.start({ + elasticsearchClient: elasticsearchServiceMock.createElasticsearchClient(), + kibanaIndexName: '.kibana', + online$: mockStatusSubject.asObservable(), + taskManager: mockTaskManager, + }); // ES isn't online yet. expect(mockSessionIndexInitialize).not.toHaveBeenCalled(); @@ -155,7 +158,12 @@ describe('SessionManagementService', () => { it('removes old cleanup task if cleanup interval changes', async () => { const mockStatusSubject = new Subject(); - service.start({ online$: mockStatusSubject.asObservable(), taskManager: mockTaskManager }); + service.start({ + elasticsearchClient: elasticsearchServiceMock.createElasticsearchClient(), + kibanaIndexName: '.kibana', + online$: mockStatusSubject.asObservable(), + taskManager: mockTaskManager, + }); mockTaskManager.get.mockResolvedValue({ schedule: { interval: '2000s' } } as any); @@ -185,7 +193,12 @@ describe('SessionManagementService', () => { it('does not remove old cleanup task if cleanup interval does not change', async () => { const mockStatusSubject = new Subject(); - service.start({ online$: mockStatusSubject.asObservable(), taskManager: mockTaskManager }); + service.start({ + elasticsearchClient: elasticsearchServiceMock.createElasticsearchClient(), + kibanaIndexName: '.kibana', + online$: mockStatusSubject.asObservable(), + taskManager: mockTaskManager, + }); mockTaskManager.get.mockResolvedValue({ schedule: { interval: '3600s' } } as any); @@ -206,7 +219,12 @@ describe('SessionManagementService', () => { it('schedules retry if index initialization fails', async () => { const mockStatusSubject = new Subject(); - service.start({ online$: mockStatusSubject.asObservable(), taskManager: mockTaskManager }); + service.start({ + elasticsearchClient: elasticsearchServiceMock.createElasticsearchClient(), + kibanaIndexName: '.kibana', + online$: mockStatusSubject.asObservable(), + taskManager: mockTaskManager, + }); mockSessionIndexInitialize.mockRejectedValue(new Error('ugh :/')); @@ -237,7 +255,12 @@ describe('SessionManagementService', () => { it('schedules retry if cleanup task registration fails', async () => { const mockStatusSubject = new Subject(); - service.start({ online$: mockStatusSubject.asObservable(), taskManager: mockTaskManager }); + service.start({ + elasticsearchClient: elasticsearchServiceMock.createElasticsearchClient(), + kibanaIndexName: '.kibana', + online$: mockStatusSubject.asObservable(), + taskManager: mockTaskManager, + }); mockTaskManager.ensureScheduled.mockRejectedValue(new Error('ugh :/')); @@ -277,12 +300,10 @@ describe('SessionManagementService', () => { const mockCoreSetup = coreMock.createSetup(); service.setup({ - clusterClient: elasticsearchServiceMock.createLegacyClusterClient(), http: mockCoreSetup.http, config: createConfig(ConfigSchema.validate({}), loggingSystemMock.createLogger(), { isTLSEnabled: false, }), - kibanaIndexName: '.kibana', taskManager: taskManagerMock.createSetup(), }); }); @@ -293,7 +314,12 @@ describe('SessionManagementService', () => { it('properly unsubscribes from status updates', () => { const mockStatusSubject = new Subject(); - service.start({ online$: mockStatusSubject.asObservable(), taskManager: mockTaskManager }); + service.start({ + elasticsearchClient: elasticsearchServiceMock.createElasticsearchClient(), + kibanaIndexName: '.kibana', + online$: mockStatusSubject.asObservable(), + taskManager: mockTaskManager, + }); service.stop(); diff --git a/x-pack/plugins/security/server/session_management/session_management_service.ts b/x-pack/plugins/security/server/session_management/session_management_service.ts index fc2e85d683d5..6bd9d8cb3a8f 100644 --- a/x-pack/plugins/security/server/session_management/session_management_service.ts +++ b/x-pack/plugins/security/server/session_management/session_management_service.ts @@ -6,8 +6,8 @@ import { Observable, Subscription } from 'rxjs'; import { + ElasticsearchClient, HttpServiceSetup, - ILegacyClusterClient, Logger, SavedObjectsErrorHelpers, } from '../../../../../src/core/server'; @@ -21,17 +21,17 @@ import { Session } from './session'; export interface SessionManagementServiceSetupParams { readonly http: Pick; readonly config: ConfigType; - readonly clusterClient: ILegacyClusterClient; - readonly kibanaIndexName: string; readonly taskManager: TaskManagerSetupContract; } export interface SessionManagementServiceStartParams { + readonly elasticsearchClient: ElasticsearchClient; + readonly kibanaIndexName: string; readonly online$: Observable; readonly taskManager: TaskManagerStartContract; } -export interface SessionManagementServiceSetup { +export interface SessionManagementServiceStart { readonly session: Session; } @@ -46,34 +46,22 @@ export const SESSION_INDEX_CLEANUP_TASK_NAME = 'session_cleanup'; export class SessionManagementService { private statusSubscription?: Subscription; private sessionIndex!: SessionIndex; + private sessionCookie!: SessionCookie; private config!: ConfigType; private isCleanupTaskScheduled = false; constructor(private readonly logger: Logger) {} - setup({ - config, - clusterClient, - http, - kibanaIndexName, - taskManager, - }: SessionManagementServiceSetupParams): SessionManagementServiceSetup { + setup({ config, http, taskManager }: SessionManagementServiceSetupParams) { this.config = config; - const sessionCookie = new SessionCookie({ + this.sessionCookie = new SessionCookie({ config, createCookieSessionStorageFactory: http.createCookieSessionStorageFactory, serverBasePath: http.basePath.serverBasePath || '/', logger: this.logger.get('cookie'), }); - this.sessionIndex = new SessionIndex({ - config, - clusterClient, - kibanaIndexName, - logger: this.logger.get('index'), - }); - // Register task that will perform periodic session index cleanup. taskManager.registerTaskDefinitions({ [SESSION_INDEX_CLEANUP_TASK_NAME]: { @@ -81,18 +69,21 @@ export class SessionManagementService { createTaskRunner: () => ({ run: () => this.sessionIndex.cleanUp() }), }, }); - - return { - session: new Session({ - logger: this.logger, - sessionCookie, - sessionIndex: this.sessionIndex, - config, - }), - }; } - start({ online$, taskManager }: SessionManagementServiceStartParams) { + start({ + elasticsearchClient, + kibanaIndexName, + online$, + taskManager, + }: SessionManagementServiceStartParams): SessionManagementServiceStart { + this.sessionIndex = new SessionIndex({ + config: this.config, + elasticsearchClient, + kibanaIndexName, + logger: this.logger.get('index'), + }); + this.statusSubscription = online$.subscribe(async ({ scheduleRetry }) => { try { await Promise.all([this.sessionIndex.initialize(), this.scheduleCleanupTask(taskManager)]); @@ -100,6 +91,15 @@ export class SessionManagementService { scheduleRetry(); } }); + + return { + session: new Session({ + logger: this.logger, + sessionCookie: this.sessionCookie, + sessionIndex: this.sessionIndex, + config: this.config, + }), + }; } stop() { diff --git a/x-pack/plugins/security/server/types.ts b/x-pack/plugins/security/server/types.ts new file mode 100644 index 000000000000..74c4c6b9cab7 --- /dev/null +++ b/x-pack/plugins/security/server/types.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. + */ +import type { IRouter, RequestHandlerContext } from 'src/core/server'; +import type { LicensingApiRequestHandlerContext } from '../../licensing/server'; + +/** + * @internal + */ +export interface SecurityRequestHandlerContext extends RequestHandlerContext { + licensing: LicensingApiRequestHandlerContext; +} + +/** + * @internal + */ +export type SecurityRouter = IRouter; diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/local_storage.sepc.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/local_storage.spec.ts similarity index 100% rename from x-pack/plugins/security_solution/cypress/integration/timelines/local_storage.sepc.ts rename to x-pack/plugins/security_solution/cypress/integration/timelines/local_storage.spec.ts diff --git a/x-pack/plugins/security_solution/server/endpoint/ingest_integration.test.ts b/x-pack/plugins/security_solution/server/endpoint/ingest_integration.test.ts index d2ce0908b91f..d287ada74eeb 100644 --- a/x-pack/plugins/security_solution/server/endpoint/ingest_integration.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/ingest_integration.test.ts @@ -15,7 +15,7 @@ import { getPackagePolicyCreateCallback, getPackagePolicyUpdateCallback, } from './ingest_integration'; -import { KibanaRequest, RequestHandlerContext } from 'kibana/server'; +import { KibanaRequest } from 'kibana/server'; import { createMockConfig, requestContextMock } from '../lib/detection_engine/routes/__mocks__'; import { EndpointAppContextServiceStartContract } from './endpoint_app_context_services'; import { createMockEndpointAppContextServiceStartContract } from './mocks'; @@ -25,13 +25,14 @@ import { Subject } from 'rxjs'; import { ILicense } from '../../../licensing/common/types'; import { EndpointDocGenerator } from '../../common/endpoint/generate_data'; import { ProtectionModes } from '../../common/endpoint/types'; +import type { SecuritySolutionRequestHandlerContext } from '../types'; import { getExceptionListClientMock } from '../../../lists/server/services/exception_lists/exception_list_client.mock'; import { ExceptionListClient } from '../../../lists/server'; describe('ingest_integration tests ', () => { let endpointAppContextMock: EndpointAppContextServiceStartContract; let req: KibanaRequest; - let ctx: RequestHandlerContext; + let ctx: SecuritySolutionRequestHandlerContext; const exceptionListClient: ExceptionListClient = getExceptionListClientMock(); const maxTimelineImportExportSize = createMockConfig().maxTimelineImportExportSize; let licenseEmitter: Subject; diff --git a/x-pack/plugins/security_solution/server/endpoint/ingest_integration.ts b/x-pack/plugins/security_solution/server/endpoint/ingest_integration.ts index 194d93df4b43..1e7f440ed678 100644 --- a/x-pack/plugins/security_solution/server/endpoint/ingest_integration.ts +++ b/x-pack/plugins/security_solution/server/endpoint/ingest_integration.ts @@ -100,10 +100,15 @@ export const getPackagePolicyCreateCallback = ( // prep for detection rules creation const appClient = appClientFactory.create(request); + // This callback is called by fleet plugin. + // It doesn't have access to SecuritySolutionRequestHandlerContext in runtime. + // Muting the error to have green CI. + // @ts-expect-error const frameworkRequest = await buildFrameworkRequest(context, securitySetup, request); // Create detection index & rules (if necessary). move past any failure, this is just a convenience try { + // @ts-expect-error await createDetectionIndex(context, appClient); } catch (err) { if (err.statusCode !== 409) { @@ -117,6 +122,7 @@ export const getPackagePolicyCreateCallback = ( // this checks to make sure index exists first, safe to try in case of failure above // may be able to recover from minor errors await createPrepackagedRules( + // @ts-expect-error context, appClient, alerts.getAlertsClientWithRequest(request), diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts index 5773b88fa2be..225592fa8e68 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.test.ts @@ -5,7 +5,11 @@ */ import { Subject } from 'rxjs'; -import { loggingSystemMock, savedObjectsServiceMock } from 'src/core/server/mocks'; +import { + elasticsearchServiceMock, + loggingSystemMock, + savedObjectsServiceMock, +} from 'src/core/server/mocks'; import { LicenseService } from '../../../../common/license/license'; import { createPackagePolicyServiceMock } from '../../../../../fleet/server/mocks'; import { PolicyWatcher } from './license_watch'; @@ -31,6 +35,7 @@ const MockPPWithEndpointPolicy = (cb?: (p: PolicyConfig) => PolicyConfig): Packa describe('Policy-Changing license watcher', () => { const logger = loggingSystemMock.create().get('license_watch.test'); const soStartMock = savedObjectsServiceMock.createStartContract(); + const esStartMock = elasticsearchServiceMock.createStart(); let packagePolicySvcMock: jest.Mocked; const Platinum = licenseMock.createLicense({ license: { type: 'platinum', mode: 'platinum' } }); @@ -45,7 +50,7 @@ describe('Policy-Changing license watcher', () => { // mock a license-changing service to test reactivity const licenseEmitter: Subject = new Subject(); const licenseService = new LicenseService(); - const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, logger); + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, esStartMock, logger); // swap out watch function, just to ensure it gets called when a license change happens const mockWatch = jest.fn(); @@ -90,7 +95,7 @@ describe('Policy-Changing license watcher', () => { perPage: 100, }); - const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, logger); + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, esStartMock, logger); await pw.watch(Gold); // just manually trigger with a given license expect(packagePolicySvcMock.list.mock.calls.length).toBe(3); // should have asked for 3 pages of resuts @@ -119,14 +124,14 @@ describe('Policy-Changing license watcher', () => { perPage: 100, }); - const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, logger); + const pw = new PolicyWatcher(packagePolicySvcMock, soStartMock, esStartMock, logger); // emulate a license change below paid tier await pw.watch(Basic); expect(packagePolicySvcMock.update).toHaveBeenCalled(); expect( - packagePolicySvcMock.update.mock.calls[0][2].inputs[0].config!.policy.value.windows.popup + packagePolicySvcMock.update.mock.calls[0][3].inputs[0].config!.policy.value.windows.popup .malware.message ).not.toEqual(CustomMessage); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts index 2f0c3bf8fd5b..a8aa0f25b078 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/policy/license_watch.ts @@ -7,6 +7,8 @@ import { Subscription } from 'rxjs'; import { + ElasticsearchClient, + ElasticsearchServiceStart, KibanaRequest, Logger, SavedObjectsClientContract, @@ -28,15 +30,18 @@ import { isAtLeast, LicenseService } from '../../../../common/license/license'; export class PolicyWatcher { private logger: Logger; private soClient: SavedObjectsClientContract; + private esClient: ElasticsearchClient; private policyService: PackagePolicyServiceInterface; private subscription: Subscription | undefined; constructor( policyService: PackagePolicyServiceInterface, soStart: SavedObjectsServiceStart, + esStart: ElasticsearchServiceStart, logger: Logger ) { this.policyService = policyService; this.soClient = this.makeInternalSOClient(soStart); + this.esClient = esStart.client.asInternalUser; this.logger = logger; } @@ -113,11 +118,16 @@ export class PolicyWatcher { license ); try { - await this.policyService.update(this.soClient, policy.id, updatePolicy); + await this.policyService.update(this.soClient, this.esClient, policy.id, updatePolicy); } catch (e) { // try again for transient issues try { - await this.policyService.update(this.soClient, policy.id, updatePolicy); + await this.policyService.update( + this.soClient, + this.esClient, + policy.id, + updatePolicy + ); } catch (ee) { this.logger.warn( `Unable to remove platinum features from policy ${policy.id}: ${ee.message}` diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks.ts index 2161ccf0eb78..04bca2f0627b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks.ts @@ -28,6 +28,7 @@ import { EndpointAppContext } from './types'; import { MetadataRequestContext } from './routes/metadata/handlers'; // import { licenseMock } from '../../../licensing/common/licensing.mock'; import { LicenseService } from '../../common/license/license'; +import { SecuritySolutionRequestHandlerContext } from '../types'; /** * Creates a mocked EndpointAppContext. @@ -118,7 +119,7 @@ export const createMockMetadataRequestContext = (): jest.Mocked, }; }; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.test.ts index 4454da855569..cb26339e3b0f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/artifacts/download_exception_list.test.ts @@ -5,7 +5,7 @@ */ import { deflateSync, inflateSync } from 'zlib'; import LRU from 'lru-cache'; -import { +import type { ILegacyClusterClient, IRouter, SavedObjectsClientContract, @@ -13,7 +13,6 @@ import { RouteConfig, RequestHandler, KibanaResponseFactory, - RequestHandlerContext, SavedObject, } from 'kibana/server'; import { @@ -29,6 +28,7 @@ import { EndpointAppContextService } from '../../endpoint_app_context_services'; import { createMockEndpointAppContextServiceStartContract } from '../../mocks'; import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__'; import { WrappedTranslatedExceptionList } from '../../schemas/artifacts/lists'; +import type { SecuritySolutionRequestHandlerContext } from '../../../types'; const mockArtifactName = `${ArtifactConstants.GLOBAL_ALLOWLIST_NAME}-windows-v1`; const expectedEndpointExceptions: WrappedTranslatedExceptionList = { @@ -173,7 +173,7 @@ describe('test alerts route', () => { client: mockSavedObjectClient, }, }, - } as unknown) as RequestHandlerContext, + } as unknown) as SecuritySolutionRequestHandlerContext, mockRequest, mockResponse ); @@ -217,7 +217,7 @@ describe('test alerts route', () => { client: mockSavedObjectClient, }, }, - } as unknown) as RequestHandlerContext, + } as unknown) as SecuritySolutionRequestHandlerContext, mockRequest, mockResponse ); @@ -251,7 +251,7 @@ describe('test alerts route', () => { client: mockSavedObjectClient, }, }, - } as unknown) as RequestHandlerContext, + } as unknown) as SecuritySolutionRequestHandlerContext, mockRequest, mockResponse ); @@ -279,7 +279,7 @@ describe('test alerts route', () => { client: mockSavedObjectClient, }, }, - } as unknown) as RequestHandlerContext, + } as unknown) as SecuritySolutionRequestHandlerContext, mockRequest, mockResponse ); @@ -313,7 +313,7 @@ describe('test alerts route', () => { client: mockSavedObjectClient, }, }, - } as unknown) as RequestHandlerContext, + } as unknown) as SecuritySolutionRequestHandlerContext, mockRequest, mockResponse ); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts index a79175b178c3..4a94e2422439 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts @@ -5,7 +5,7 @@ */ import Boom from '@hapi/boom'; -import { RequestHandlerContext, Logger, RequestHandler } from 'kibana/server'; +import type { Logger, RequestHandler } from 'kibana/server'; import { TypeOf } from '@kbn/config-schema'; import { HostInfo, @@ -14,6 +14,8 @@ import { HostStatus, MetadataQueryStrategyVersions, } from '../../../../common/endpoint/types'; +import type { SecuritySolutionRequestHandlerContext } from '../../../types'; + import { getESQueryHostMetadataByID, kibanaRequestToMetadataListESQuery } from './query_builders'; import { Agent, AgentStatus, PackagePolicy } from '../../../../../fleet/common/types/models'; import { EndpointAppContext, HostListQueryResult } from '../../types'; @@ -25,7 +27,7 @@ import { EndpointAppContextService } from '../../endpoint_app_context_services'; export interface MetadataRequestContext { endpointAppContextService: EndpointAppContextService; logger: Logger; - requestHandlerContext: RequestHandlerContext; + requestHandlerContext: SecuritySolutionRequestHandlerContext; } const HOST_STATUS_MAPPING = new Map([ @@ -52,7 +54,12 @@ export const getMetadataListRequestHandler = function ( endpointAppContext: EndpointAppContext, logger: Logger, queryStrategyVersion?: MetadataQueryStrategyVersions -): RequestHandler> { +): RequestHandler< + unknown, + unknown, + TypeOf, + SecuritySolutionRequestHandlerContext +> { return async (context, request, response) => { try { const agentService = endpointAppContext.service.getAgentService(); @@ -68,13 +75,15 @@ export const getMetadataListRequestHandler = function ( const unenrolledAgentIds = await findAllUnenrolledAgentIds( agentService, - context.core.savedObjects.client + context.core.savedObjects.client, + context.core.elasticsearch.client.asCurrentUser ); const statusIDs = request?.body?.filters?.host_status?.length ? await findAgentIDsByStatus( agentService, context.core.savedObjects.client, + context.core.elasticsearch.client.asCurrentUser, request.body?.filters?.host_status ) : undefined; @@ -110,7 +119,12 @@ export const getMetadataRequestHandler = function ( endpointAppContext: EndpointAppContext, logger: Logger, queryStrategyVersion?: MetadataQueryStrategyVersions -): RequestHandler, undefined, undefined> { +): RequestHandler< + TypeOf, + unknown, + unknown, + SecuritySolutionRequestHandlerContext +> { return async (context, request, response) => { const agentService = endpointAppContext.service.getAgentService(); if (agentService === undefined) { @@ -193,6 +207,7 @@ async function findAgent( ?.getAgentService() ?.getAgent( metadataRequestContext.requestHandlerContext.core.savedObjects.client, + metadataRequestContext.requestHandlerContext.core.elasticsearch.client.asCurrentUser, hostMetadata.elastic.agent.id ); } catch (e) { @@ -267,6 +282,7 @@ export async function enrichHostMetadata( ?.getAgentService() ?.getAgentStatusById( metadataRequestContext.requestHandlerContext.core.savedObjects.client, + metadataRequestContext.requestHandlerContext.core.elasticsearch.client.asCurrentUser, elasticAgentId ); hostStatus = HOST_STATUS_MAPPING.get(status!) || HostStatus.ERROR; @@ -289,6 +305,7 @@ export async function enrichHostMetadata( ?.getAgentService() ?.getAgent( metadataRequestContext.requestHandlerContext.core.savedObjects.client, + metadataRequestContext.requestHandlerContext.core.elasticsearch.client.asCurrentUser, elasticAgentId ); const agentPolicy = await metadataRequestContext.endpointAppContextService diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts index bfb2a6a828e6..6bcde49ea80b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; import { schema } from '@kbn/config-schema'; import { HostStatus, MetadataQueryStrategyVersions } from '../../../../common/endpoint/types'; import { EndpointAppContext } from '../../types'; import { getLogger, getMetadataListRequestHandler, getMetadataRequestHandler } from './handlers'; +import type { SecuritySolutionPluginRouter } from '../../../types'; export const BASE_ENDPOINT_ROUTE = '/api/endpoint'; export const METADATA_REQUEST_V1_ROUTE = `${BASE_ENDPOINT_ROUTE}/v1/metadata`; @@ -60,7 +60,10 @@ export const GetMetadataListRequestSchema = { ), }; -export function registerEndpointRoutes(router: IRouter, endpointAppContext: EndpointAppContext) { +export function registerEndpointRoutes( + router: SecuritySolutionPluginRouter, + endpointAppContext: EndpointAppContext +) { const logger = getLogger(endpointAppContext); router.post( { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index 25de64aac525..5049104a9640 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -5,7 +5,6 @@ */ import { ILegacyClusterClient, - IRouter, ILegacyScopedClusterClient, KibanaResponseFactory, RequestHandler, @@ -43,16 +42,17 @@ import { import { createV1SearchResponse, createV2SearchResponse } from './support/test_support'; import { PackageService } from '../../../../../fleet/server/services'; import { metadataTransformPrefix } from '../../../../common/endpoint/constants'; +import type { SecuritySolutionPluginRouter } from '../../../types'; describe('test endpoint route', () => { - let routerMock: jest.Mocked; + let routerMock: jest.Mocked; let mockResponse: jest.Mocked; let mockClusterClient: jest.Mocked; let mockScopedClient: jest.Mocked; let mockSavedObjectClient: jest.Mocked; let mockPackageService: jest.Mocked; // eslint-disable-next-line @typescript-eslint/no-explicit-any - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; // eslint-disable-next-line @typescript-eslint/no-explicit-any let routeConfig: RouteConfig; // tests assume that fleet is enabled, and thus agentService is available diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata_v1.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata_v1.test.ts index 44776bfddd61..b879272862d4 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata_v1.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata_v1.test.ts @@ -5,7 +5,6 @@ */ import { ILegacyClusterClient, - IRouter, ILegacyScopedClusterClient, KibanaResponseFactory, RequestHandler, @@ -38,16 +37,17 @@ import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data' import { Agent, EsAssetReference } from '../../../../../fleet/common/types/models'; import { createV1SearchResponse } from './support/test_support'; import { PackageService } from '../../../../../fleet/server/services'; +import type { SecuritySolutionPluginRouter } from '../../../types'; describe('test endpoint route v1', () => { - let routerMock: jest.Mocked; + let routerMock: jest.Mocked; let mockResponse: jest.Mocked; let mockClusterClient: jest.Mocked; let mockScopedClient: jest.Mocked; let mockSavedObjectClient: jest.Mocked; let mockPackageService: jest.Mocked; // eslint-disable-next-line @typescript-eslint/no-explicit-any - let routeHandler: RequestHandler; + let routeHandler: RequestHandler; // eslint-disable-next-line @typescript-eslint/no-explicit-any let routeConfig: RouteConfig; // tests assume that fleet is enabled, and thus agentService is available diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts index e9a1f1e24fa5..d7fe8b75cbc8 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.test.ts @@ -4,9 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server'; import { findAgentIDsByStatus } from './agent_status'; -import { savedObjectsClientMock } from '../../../../../../../../src/core/server/mocks'; +import { + elasticsearchServiceMock, + savedObjectsClientMock, +} from '../../../../../../../../src/core/server/mocks'; import { AgentService } from '../../../../../../fleet/server/services'; import { createMockAgentService } from '../../../../../../fleet/server/mocks'; import { Agent } from '../../../../../../fleet/common/types/models'; @@ -14,9 +17,11 @@ import { AgentStatusKueryHelper } from '../../../../../../fleet/common/services' describe('test filtering endpoint hosts by agent status', () => { let mockSavedObjectClient: jest.Mocked; + let mockElasticsearchClient: jest.Mocked; let mockAgentService: jest.Mocked; beforeEach(() => { mockSavedObjectClient = savedObjectsClientMock.create(); + mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockAgentService = createMockAgentService(); }); @@ -30,7 +35,12 @@ describe('test filtering endpoint hosts by agent status', () => { }) ); - const result = await findAgentIDsByStatus(mockAgentService, mockSavedObjectClient, ['online']); + const result = await findAgentIDsByStatus( + mockAgentService, + mockSavedObjectClient, + mockElasticsearchClient, + ['online'] + ); expect(result).toBeDefined(); }); @@ -53,9 +63,14 @@ describe('test filtering endpoint hosts by agent status', () => { }) ); - const result = await findAgentIDsByStatus(mockAgentService, mockSavedObjectClient, ['offline']); + const result = await findAgentIDsByStatus( + mockAgentService, + mockSavedObjectClient, + mockElasticsearchClient, + ['offline'] + ); const offlineKuery = AgentStatusKueryHelper.buildKueryForOfflineAgents(); - expect(mockAgentService.listAgents.mock.calls[0][1].kuery).toEqual( + expect(mockAgentService.listAgents.mock.calls[0][2].kuery).toEqual( expect.stringContaining(offlineKuery) ); expect(result).toBeDefined(); @@ -81,13 +96,15 @@ describe('test filtering endpoint hosts by agent status', () => { }) ); - const result = await findAgentIDsByStatus(mockAgentService, mockSavedObjectClient, [ - 'unenrolling', - 'error', - ]); + const result = await findAgentIDsByStatus( + mockAgentService, + mockSavedObjectClient, + mockElasticsearchClient, + ['unenrolling', 'error'] + ); const unenrollKuery = AgentStatusKueryHelper.buildKueryForUnenrollingAgents(); const errorKuery = AgentStatusKueryHelper.buildKueryForErrorAgents(); - expect(mockAgentService.listAgents.mock.calls[0][1].kuery).toEqual( + expect(mockAgentService.listAgents.mock.calls[0][2].kuery).toEqual( expect.stringContaining(`${unenrollKuery} OR ${errorKuery}`) ); expect(result).toBeDefined(); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.ts index 395b05c0887e..4d3fd806dc63 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/agent_status.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server'; import { AgentService } from '../../../../../../fleet/server'; import { AgentStatusKueryHelper } from '../../../../../../fleet/common/services'; import { Agent } from '../../../../../../fleet/common/types/models'; @@ -20,6 +20,7 @@ const STATUS_QUERY_MAP = new Map([ export async function findAgentIDsByStatus( agentService: AgentService, soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, status: string[], pageSize: number = 1000 ): Promise { @@ -39,7 +40,7 @@ export async function findAgentIDsByStatus( let hasMore = true; while (hasMore) { - const agents = await agentService.listAgents(soClient, searchOptions(page++)); + const agents = await agentService.listAgents(soClient, esClient, searchOptions(page++)); result.push(...agents.agents.map((agent: Agent) => agent.id)); hasMore = agents.agents.length > 0; } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/unenroll.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/unenroll.test.ts index c88f11422d0f..ea68f6270e73 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/unenroll.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/unenroll.test.ts @@ -4,18 +4,23 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server'; import { findAllUnenrolledAgentIds } from './unenroll'; -import { savedObjectsClientMock } from '../../../../../../../../src/core/server/mocks'; +import { + elasticsearchServiceMock, + savedObjectsClientMock, +} from '../../../../../../../../src/core/server/mocks'; import { AgentService } from '../../../../../../fleet/server/services'; import { createMockAgentService } from '../../../../../../fleet/server/mocks'; import { Agent } from '../../../../../../fleet/common/types/models'; describe('test find all unenrolled Agent id', () => { let mockSavedObjectClient: jest.Mocked; + let mockElasticsearchClient: jest.Mocked; let mockAgentService: jest.Mocked; beforeEach(() => { mockSavedObjectClient = savedObjectsClientMock.create(); + mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; mockAgentService = createMockAgentService(); }); @@ -53,7 +58,11 @@ describe('test find all unenrolled Agent id', () => { perPage: 1, }) ); - const agentIds = await findAllUnenrolledAgentIds(mockAgentService, mockSavedObjectClient); + const agentIds = await findAllUnenrolledAgentIds( + mockAgentService, + mockSavedObjectClient, + mockElasticsearchClient + ); expect(agentIds).toBeTruthy(); expect(agentIds).toEqual(['id1', 'id2']); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/unenroll.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/unenroll.ts index 1abea86c1a49..45664f087f1b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/unenroll.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/support/unenroll.ts @@ -4,13 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'kibana/server'; +import { ElasticsearchClient, SavedObjectsClientContract } from 'kibana/server'; import { AgentService } from '../../../../../../fleet/server'; import { Agent } from '../../../../../../fleet/common/types/models'; export async function findAllUnenrolledAgentIds( agentService: AgentService, soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, pageSize: number = 1000 ): Promise { const searchOptions = (pageNum: number) => { @@ -29,7 +30,11 @@ export async function findAllUnenrolledAgentIds( let hasMore = true; while (hasMore) { - const unenrolledAgents = await agentService.listAgents(soClient, searchOptions(page++)); + const unenrolledAgents = await agentService.listAgents( + soClient, + esClient, + searchOptions(page++) + ); result.push(...unenrolledAgents.agents.map((agent: Agent) => agent.id)); hasMore = unenrolledAgents.agents.length > 0; } diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.ts index 728e3279c52a..46b67706c99a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.ts @@ -44,6 +44,7 @@ export const getAgentPolicySummaryHandler = function ( const result = await getAgentPolicySummary( endpointAppContext, context.core.savedObjects.client, + context.core.elasticsearch.client.asCurrentUser, request.query.package_name, request.query?.policy_id || undefined ); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/policy/service.ts b/x-pack/plugins/security_solution/server/endpoint/routes/policy/service.ts index dd4ade1906bc..f52535053a53 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/policy/service.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/policy/service.ts @@ -5,7 +5,11 @@ */ import { SearchResponse } from 'elasticsearch'; -import { ILegacyScopedClusterClient, SavedObjectsClientContract } from 'kibana/server'; +import { + ElasticsearchClient, + ILegacyScopedClusterClient, + SavedObjectsClientContract, +} from 'kibana/server'; import { GetHostPolicyResponse, HostPolicyResponse } from '../../../../common/endpoint/types'; import { INITIAL_POLICY_ID } from './index'; import { Agent } from '../../../../../fleet/common/types/models'; @@ -73,6 +77,7 @@ const transformAgentVersionMap = (versionMap: Map): { [key: stri export async function getAgentPolicySummary( endpointAppContext: EndpointAppContext, soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, packageName: string, policyId?: string, pageSize: number = 1000 @@ -83,6 +88,7 @@ export async function getAgentPolicySummary( await agentVersionsMap( endpointAppContext, soClient, + esClient, `${agentQuery} AND ${AGENT_SAVED_OBJECT_TYPE}.policy_id:${policyId}`, pageSize ) @@ -90,13 +96,14 @@ export async function getAgentPolicySummary( } return transformAgentVersionMap( - await agentVersionsMap(endpointAppContext, soClient, agentQuery, pageSize) + await agentVersionsMap(endpointAppContext, soClient, esClient, agentQuery, pageSize) ); } export async function agentVersionsMap( endpointAppContext: EndpointAppContext, soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient, kqlQuery: string, pageSize: number = 1000 ): Promise> { @@ -115,7 +122,7 @@ export async function agentVersionsMap( while (hasMore) { const queryResult = await endpointAppContext.service .getAgentService()! - .listAgents(soClient, searchOptions(page++)); + .listAgents(soClient, esClient, searchOptions(page++)); queryResult.agents.forEach((agent: Agent) => { const agentVersion = agent.local_metadata?.elastic?.agent?.version; if (result.has(agentVersion)) { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts index de78313c6ca2..feee872b90ac 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts @@ -22,6 +22,7 @@ import { getTrustedAppsListRouteHandler, getTrustedAppsSummaryRouteHandler, } from './handlers'; +import type { SecuritySolutionRequestHandlerContext } from '../../../types'; const exceptionsListClient = listMock.getExceptionListClient() as jest.Mocked; @@ -31,13 +32,14 @@ const createAppContextMock = () => ({ config: () => Promise.resolve(createMockConfig()), }); -const createHandlerContextMock = () => ({ - ...xpackMocks.createRequestHandlerContext(), - lists: { - getListClient: jest.fn(), - getExceptionListClient: jest.fn().mockReturnValue(exceptionsListClient), - }, -}); +const createHandlerContextMock = () => + (({ + ...xpackMocks.createRequestHandlerContext(), + lists: { + getListClient: jest.fn(), + getExceptionListClient: jest.fn().mockReturnValue(exceptionsListClient), + }, + } as unknown) as jest.Mocked); const assertResponse = ( response: jest.Mocked, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.ts index aa2167a49329..825ae57b2fa4 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandler, RequestHandlerContext } from 'kibana/server'; +import type { RequestHandler } from 'kibana/server'; +import type { SecuritySolutionRequestHandlerContext } from '../../../types'; import { ExceptionListClient } from '../../../../../lists/server'; @@ -23,7 +24,9 @@ import { MissingTrustedAppException, } from './service'; -const exceptionListClientFromContext = (context: RequestHandlerContext): ExceptionListClient => { +const exceptionListClientFromContext = ( + context: SecuritySolutionRequestHandlerContext +): ExceptionListClient => { const exceptionLists = context.lists?.getExceptionListClient(); if (!exceptionLists) { @@ -35,7 +38,12 @@ const exceptionListClientFromContext = (context: RequestHandlerContext): Excepti export const getTrustedAppsDeleteRouteHandler = ( endpointAppContext: EndpointAppContext -): RequestHandler => { +): RequestHandler< + DeleteTrustedAppsRequestParams, + unknown, + unknown, + SecuritySolutionRequestHandlerContext +> => { const logger = endpointAppContext.logFactory.get('trusted_apps'); return async (context, req, res) => { @@ -56,7 +64,12 @@ export const getTrustedAppsDeleteRouteHandler = ( export const getTrustedAppsListRouteHandler = ( endpointAppContext: EndpointAppContext -): RequestHandler => { +): RequestHandler< + unknown, + GetTrustedAppsListRequest, + unknown, + SecuritySolutionRequestHandlerContext +> => { const logger = endpointAppContext.logFactory.get('trusted_apps'); return async (context, req, res) => { @@ -73,7 +86,12 @@ export const getTrustedAppsListRouteHandler = ( export const getTrustedAppsCreateRouteHandler = ( endpointAppContext: EndpointAppContext -): RequestHandler => { +): RequestHandler< + unknown, + unknown, + PostTrustedAppCreateRequest, + SecuritySolutionRequestHandlerContext +> => { const logger = endpointAppContext.logFactory.get('trusted_apps'); return async (context, req, res) => { @@ -90,7 +108,12 @@ export const getTrustedAppsCreateRouteHandler = ( export const getTrustedAppsSummaryRouteHandler = ( endpointAppContext: EndpointAppContext -): RequestHandler => { +): RequestHandler< + unknown, + unknown, + PostTrustedAppCreateRequest, + SecuritySolutionRequestHandlerContext +> => { const logger = endpointAppContext.logFactory.get('trusted_apps'); return async (context, req, res) => { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/index.ts index ce5f571ce9be..7e318030f1f7 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; import { DeleteTrustedAppsRequestSchema, GetTrustedAppsRequestSchema, @@ -23,9 +22,10 @@ import { getTrustedAppsSummaryRouteHandler, } from './handlers'; import { EndpointAppContext } from '../../types'; +import { SecuritySolutionPluginRouter } from '../../../types'; export const registerTrustedAppsRoutes = ( - router: IRouter, + router: SecuritySolutionPluginRouter, endpointAppContext: EndpointAppContext ) => { // DELETE one diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts index 9cb3f3d20543..2bd4fcf348f7 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts @@ -191,7 +191,7 @@ describe('manifest_manager', () => { expect(packagePolicyService.update.mock.calls.length).toEqual(2); expect( - packagePolicyService.update.mock.calls[0][2].inputs[0].config!.artifact_manifest.value + packagePolicyService.update.mock.calls[0][3].inputs[0].config!.artifact_manifest.value ).toEqual({ manifest_version: '1.0.1', schema_version: 'v1', diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts index 9f45f39a392f..44a0fb15e48d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts @@ -293,7 +293,13 @@ export class ManifestManager { }; try { - await this.packagePolicyService.update(this.savedObjectsClient, id, newPackagePolicy); + await this.packagePolicyService.update( + this.savedObjectsClient, + // @ts-ignore + undefined, + id, + newPackagePolicy + ); this.logger.debug( `Updated package policy ${id} with manifest version ${manifest.getSemanticVersion()}` ); diff --git a/x-pack/plugins/security_solution/server/index.ts b/x-pack/plugins/security_solution/server/index.ts index 94764fd15936..0fa037ca77d0 100644 --- a/x-pack/plugins/security_solution/server/index.ts +++ b/x-pack/plugins/security_solution/server/index.ts @@ -57,3 +57,4 @@ export { getIndexExists } from './lib/detection_engine/index/get_index_exists'; export { buildRouteValidation } from './utils/build_validation/route_validation'; export { transformError, buildSiemResponse } from './lib/detection_engine/routes/utils'; export { readPrivileges } from './lib/detection_engine/privileges/read_privileges'; +export type { AppRequestContext } from './types'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index 8e379e5caa89..511897094a1e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionRequestHandlerContext } from '../../../../types'; import { coreMock, elasticsearchServiceMock, @@ -40,7 +40,7 @@ const createRequestContextMock = ( }, licensing: clients.licensing, securitySolution: { getAppClient: jest.fn(() => clients.appClient) }, - } as unknown) as RequestHandlerContext; + } as unknown) as SecuritySolutionRequestHandlerContext; }; const createTools = () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/server.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/server.ts index d0683f3baabe..37b18fbcc2b0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/server.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/server.ts @@ -8,13 +8,13 @@ import { RequestHandler, RouteConfig, KibanaRequest, - RequestHandlerContext, } from '../../../../../../../../src/core/server'; import { httpServiceMock } from '../../../../../../../../src/core/server/mocks'; import { requestContextMock } from './request_context'; import { responseMock as responseFactoryMock } from './response_factory'; import { requestMock } from '.'; import { responseAdapter } from './test_adapters'; +import { SecuritySolutionRequestHandlerContext } from '../../../../types'; interface Route { config: RouteConfig; @@ -53,7 +53,10 @@ class MockServer { return this.resultMock; } - public async inject(request: KibanaRequest, context: RequestHandlerContext = this.contextMock) { + public async inject( + request: KibanaRequest, + context: SecuritySolutionRequestHandlerContext = this.contextMock + ) { const validatedRequest = this.validateRequest(request); const [rejection] = this.resultMock.badRequest.mock.calls; if (rejection) { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts index 8280e86bdf2c..81be361ed485 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts @@ -4,8 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AppClient } from '../../../../types'; -import { IRouter, RequestHandlerContext } from '../../../../../../../../src/core/server'; +import type { + AppClient, + SecuritySolutionPluginRouter, + SecuritySolutionRequestHandlerContext, +} from '../../../../types'; import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants'; import { transformError, buildSiemResponse } from '../utils'; import { getIndexExists } from '../../index/get_index_exists'; @@ -20,7 +23,7 @@ import { templateNeedsUpdate } from './check_template_version'; import { getIndexVersion } from './get_index_version'; import { isOutdated } from '../../migrations/helpers'; -export const createIndexRoute = (router: IRouter) => { +export const createIndexRoute = (router: SecuritySolutionPluginRouter) => { router.post( { path: DETECTION_ENGINE_INDEX_URL, @@ -59,7 +62,7 @@ class CreateIndexError extends Error { } export const createDetectionIndex = async ( - context: RequestHandlerContext, + context: SecuritySolutionRequestHandlerContext, siemClient: AppClient ): Promise => { const clusterClient = context.core.elasticsearch.legacy.client; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/delete_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/delete_index_route.ts index b58103bf15f3..6b9a96b5efe2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/delete_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/delete_index_route.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants'; import { transformError, buildSiemResponse } from '../utils'; import { getIndexExists } from '../../index/get_index_exists'; @@ -24,7 +24,7 @@ import { deleteTemplate } from '../../index/delete_template'; * * And ensuring they're all gone */ -export const deleteIndexRoute = (router: IRouter) => { +export const deleteIndexRoute = (router: SecuritySolutionPluginRouter) => { router.delete( { path: DETECTION_ENGINE_INDEX_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts index d898d3fd5924..908313ad88e3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/read_index_route.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants'; import { transformError, buildSiemResponse } from '../utils'; import { getIndexExists } from '../../index/get_index_exists'; @@ -12,7 +12,7 @@ import { SIGNALS_TEMPLATE_VERSION } from './get_signals_template'; import { getIndexVersion } from './get_index_version'; import { isOutdated } from '../../migrations/helpers'; -export const readIndexRoute = (router: IRouter) => { +export const readIndexRoute = (router: SecuritySolutionPluginRouter) => { router.get( { path: DETECTION_ENGINE_INDEX_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts index 945be0c58413..76e68d3cd9b3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts @@ -74,6 +74,7 @@ describe('read_privileges route', () => { const { securitySolution, ...contextWithoutSecuritySolution } = context; const response = await server.inject( getPrivilegeRequest({ auth: { isAuthenticated: false } }), + // @ts-expect-error contextWithoutSecuritySolution ); expect(response.status).toEqual(404); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.ts index 174aa4911ba1..58d8ef26faff 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.ts @@ -6,12 +6,15 @@ import { merge } from 'lodash/fp'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_PRIVILEGES_URL } from '../../../../../common/constants'; import { buildSiemResponse, transformError } from '../utils'; import { readPrivileges } from '../../privileges/read_privileges'; -export const readPrivilegesRoute = (router: IRouter, usingEphemeralEncryptionKey: boolean) => { +export const readPrivilegesRoute = ( + router: SecuritySolutionPluginRouter, + usingEphemeralEncryptionKey: boolean +) => { router.get( { path: DETECTION_ENGINE_PRIVILEGES_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts index 3dc25583579c..a4c35c87ca3b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts @@ -141,6 +141,7 @@ describe('add_prepackaged_rules_route', () => { const { securitySolution, ...contextWithoutSecuritySolution } = context; const response = await server.inject( addPrepackagedRulesRequest(), + // @ts-expect-error contextWithoutSecuritySolution ); expect(response.status).toEqual(404); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts index 210ec87ade2d..a360d43d5016 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -4,8 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AppClient } from '../../../../types'; -import { IRouter, RequestHandlerContext } from '../../../../../../../../src/core/server'; +import type { + AppClient, + SecuritySolutionPluginRouter, + SecuritySolutionRequestHandlerContext, +} from '../../../../types'; import { validate } from '../../../../../common/validate'; import { @@ -35,7 +38,7 @@ import { FrameworkRequest } from '../../../framework'; import { ExceptionListClient } from '../../../../../../lists/server'; export const addPrepackedRulesRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, config: ConfigType, security: SetupPlugins['security'] ) => { @@ -87,7 +90,7 @@ class PrepackagedRulesError extends Error { } export const createPrepackagedRules = async ( - context: RequestHandlerContext, + context: SecuritySolutionRequestHandlerContext, siemClient: AppClient, alertsClient: AlertsClient, frameworkRequest: FrameworkRequest, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts index 55317fc28afc..31dca3646231 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts @@ -54,6 +54,7 @@ describe('create_rules_bulk', () => { it('returns 404 if siem client is unavailable', async () => { const { securitySolution, ...contextWithoutSecuritySolution } = context; + // @ts-expect-error const response = await server.inject(getReadBulkRequest(), contextWithoutSecuritySolution); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index 3473948b000c..6795abc5c211 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -8,7 +8,7 @@ import { validate } from '../../../../../common/validate'; import { createRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/create_rules_type_dependents'; import { createRulesBulkSchema } from '../../../../../common/detection_engine/schemas/request/create_rules_bulk_schema'; import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; import { buildMlAuthz } from '../../../machine_learning/authz'; @@ -25,7 +25,10 @@ import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters' import { RuleTypeParams } from '../../types'; import { Alert } from '../../../../../../alerts/common'; -export const createRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => { +export const createRulesBulkRoute = ( + router: SecuritySolutionPluginRouter, + ml: SetupPlugins['ml'] +) => { router.post( { path: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts index 40465f4dc745..4f85ecb789a5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts @@ -63,6 +63,7 @@ describe('create_rules', () => { it('returns 404 if siem client is unavailable', async () => { const { securitySolution, ...contextWithoutSecuritySolution } = context; + // @ts-expect-error const response = await server.inject(getCreateRequest(), contextWithoutSecuritySolution); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts index c59d5d2a36fd..bfe32b9ae9a0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -5,9 +5,9 @@ */ import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; -import { IRouter } from '../../../../../../../../src/core/server'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { buildMlAuthz } from '../../../machine_learning/authz'; import { throwHttpError } from '../../../machine_learning/validation'; import { readRules } from '../../rules/read_rules'; @@ -22,7 +22,10 @@ import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters' import { RuleTypeParams } from '../../types'; import { Alert } from '../../../../../../alerts/common'; -export const createRulesRoute = (router: IRouter, ml: SetupPlugins['ml']): void => { +export const createRulesRoute = ( + router: SecuritySolutionPluginRouter, + ml: SetupPlugins['ml'] +): void => { router.post( { path: DETECTION_ENGINE_RULES_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts index 99bf16aadc81..f4d9ac0a4ee8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts @@ -12,7 +12,11 @@ import { QueryRulesBulkSchemaDecoded, } from '../../../../../common/detection_engine/schemas/request/query_rules_bulk_schema'; import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; -import { IRouter, RouteConfig, RequestHandler } from '../../../../../../../../src/core/server'; +import type { RouteConfig, RequestHandler } from '../../../../../../../../src/core/server'; +import type { + SecuritySolutionPluginRouter, + SecuritySolutionRequestHandlerContext, +} from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { getIdBulkError } from './utils'; import { transformValidateBulkError } from './validate'; @@ -23,9 +27,15 @@ import { deleteRuleActionsSavedObject } from '../../rule_actions/delete_rule_act import { ruleStatusSavedObjectsClientFactory } from '../../signals/rule_status_saved_objects_client'; type Config = RouteConfig; -type Handler = RequestHandler; +type Handler = RequestHandler< + unknown, + unknown, + QueryRulesBulkSchemaDecoded, + SecuritySolutionRequestHandlerContext, + 'delete' | 'post' +>; -export const deleteRulesBulkRoute = (router: IRouter) => { +export const deleteRulesBulkRoute = (router: SecuritySolutionPluginRouter) => { const config: Config = { validate: { body: buildRouteValidation( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts index f4aa51c6dcfc..5eeaed6e9561 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_route.ts @@ -10,7 +10,7 @@ import { QueryRulesSchemaDecoded, } from '../../../../../common/detection_engine/schemas/request/query_rules_schema'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { deleteRules } from '../../rules/delete_rules'; import { getIdError } from './utils'; @@ -20,7 +20,7 @@ import { deleteNotifications } from '../../notifications/delete_notifications'; import { deleteRuleActionsSavedObject } from '../../rule_actions/delete_rule_actions_saved_object'; import { ruleStatusSavedObjectsClientFactory } from '../../signals/rule_status_saved_objects_client'; -export const deleteRulesRoute = (router: IRouter) => { +export const deleteRulesRoute = (router: SecuritySolutionPluginRouter) => { router.delete( { path: DETECTION_ENGINE_RULES_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts index 8df9f114559a..9828747ca952 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts @@ -11,7 +11,7 @@ import { ExportRulesSchemaDecoded, } from '../../../../../common/detection_engine/schemas/request/export_rules_schema'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { ConfigType } from '../../../../config'; import { getNonPackagedRulesCount } from '../../rules/get_existing_prepackaged_rules'; @@ -19,7 +19,7 @@ import { getExportByObjectIds } from '../../rules/get_export_by_object_ids'; import { getExportAll } from '../../rules/get_export_all'; import { transformError, buildSiemResponse } from '../utils'; -export const exportRulesRoute = (router: IRouter, config: ConfigType) => { +export const exportRulesRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => { router.post( { path: `${DETECTION_ENGINE_RULES_URL}/_export`, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts index b2074ad20b67..b32497f52d57 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_route.ts @@ -9,7 +9,7 @@ import { findRulesSchema, FindRulesSchemaDecoded, } from '../../../../../common/detection_engine/schemas/request/find_rules_schema'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { findRules } from '../../rules/find_rules'; import { transformValidateFindAlerts } from './validate'; @@ -18,7 +18,7 @@ import { getRuleActionsSavedObject } from '../../rule_actions/get_rule_actions_s import { ruleStatusSavedObjectsClientFactory } from '../../signals/rule_status_saved_objects_client'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; -export const findRulesRoute = (router: IRouter) => { +export const findRulesRoute = (router: SecuritySolutionPluginRouter) => { router.get( { path: `${DETECTION_ENGINE_RULES_URL}/_find`, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts index 3ae228c165a5..add8e5435283 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/find_rules_status_route.ts @@ -5,7 +5,7 @@ */ import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { RuleStatusResponse } from '../../rules/types'; import { transformError, buildSiemResponse, mergeStatuses, getFailingRules } from '../utils'; @@ -22,7 +22,7 @@ import { * @param router * @returns RuleStatusResponse */ -export const findRulesStatusesRoute = (router: IRouter) => { +export const findRulesStatusesRoute = (router: SecuritySolutionPluginRouter) => { router.post( { path: `${DETECTION_ENGINE_RULES_URL}/_find_statuses`, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts index 4cd5238ccb1e..45ed207b8bbe 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts @@ -9,7 +9,7 @@ import { PrePackagedRulesAndTimelinesStatusSchema, prePackagedRulesAndTimelinesStatusSchema, } from '../../../../../common/detection_engine/schemas/response/prepackaged_rules_status_schema'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_PREPACKAGED_URL } from '../../../../../common/constants'; import { transformError, buildSiemResponse } from '../utils'; import { getPrepackagedRules } from '../../rules/get_prepackaged_rules'; @@ -24,7 +24,7 @@ import { checkTimelinesStatus } from '../../../timeline/routes/utils/check_timel import { checkTimelineStatusRt } from '../../../timeline/routes/schemas/check_timelines_status_schema'; export const getPrepackagedRulesStatusRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, config: ConfigType, security: SetupPlugins['security'] ) => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts index a033c16cd5e9..367ad866ae55 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts @@ -76,6 +76,7 @@ describe('import_rules_route', () => { it('returns 404 if siem client is unavailable', async () => { const { securitySolution, ...contextWithoutSecuritySolution } = context; + // @ts-expect-error const response = await server.inject(request, contextWithoutSecuritySolution); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts index adf027a430f8..b782ba1a071d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -20,7 +20,7 @@ import { importRulesSchema as importRulesResponseSchema, } from '../../../../../common/detection_engine/schemas/response/import_rules_schema'; import { isMlRule } from '../../../../../common/machine_learning/helpers'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { ConfigType } from '../../../../config'; import { SetupPlugins } from '../../../../plugin'; @@ -49,7 +49,11 @@ type PromiseFromStreams = ImportRulesSchemaDecoded | Error; const CHUNK_PARSED_OBJECT_SIZE = 50; -export const importRulesRoute = (router: IRouter, config: ConfigType, ml: SetupPlugins['ml']) => { +export const importRulesRoute = ( + router: SecuritySolutionPluginRouter, + config: ConfigType, + ml: SetupPlugins['ml'] +) => { router.post( { path: `${DETECTION_ENGINE_RULES_URL}/_import`, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 7dfb4daa1a0a..380b9f2ae2c3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -12,7 +12,7 @@ import { } from '../../../../../common/detection_engine/schemas/request/patch_rules_bulk_schema'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; import { buildMlAuthz } from '../../../machine_learning/authz'; @@ -26,7 +26,10 @@ import { ruleStatusSavedObjectsClientFactory } from '../../signals/rule_status_s import { readRules } from '../../rules/read_rules'; import { PartialFilter } from '../../types'; -export const patchRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => { +export const patchRulesBulkRoute = ( + router: SecuritySolutionPluginRouter, + ml: SetupPlugins['ml'] +) => { router.patch( { path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts index aadb13ef54e7..66a873860189 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -11,7 +11,7 @@ import { PatchRulesSchemaDecoded, patchRulesSchema, } from '../../../../../common/detection_engine/schemas/request/patch_rules_schema'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; import { buildMlAuthz } from '../../../machine_learning/authz'; @@ -25,7 +25,7 @@ import { ruleStatusSavedObjectsClientFactory } from '../../signals/rule_status_s import { readRules } from '../../rules/read_rules'; import { PartialFilter } from '../../types'; -export const patchRulesRoute = (router: IRouter, ml: SetupPlugins['ml']) => { +export const patchRulesRoute = (router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml']) => { router.patch( { path: DETECTION_ENGINE_RULES_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts index 6fb82166d416..a1f081ffb9e0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/read_rules_route.ts @@ -10,7 +10,7 @@ import { QueryRulesSchemaDecoded, } from '../../../../../common/detection_engine/schemas/request/query_rules_schema'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { getIdError } from './utils'; import { transformValidate } from './validate'; @@ -19,7 +19,7 @@ import { readRules } from '../../rules/read_rules'; import { getRuleActionsSavedObject } from '../../rule_actions/get_rule_actions_saved_object'; import { ruleStatusSavedObjectsClientFactory } from '../../signals/rule_status_saved_objects_client'; -export const readRulesRoute = (router: IRouter) => { +export const readRulesRoute = (router: SecuritySolutionPluginRouter) => { router.get( { path: DETECTION_ENGINE_RULES_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts index 72583a5a7870..3da78c2f9736 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts @@ -68,6 +68,7 @@ describe('update_rules_bulk', () => { it('returns 404 if siem client is unavailable', async () => { const { securitySolution, ...contextWithoutSecuritySolution } = context; + // @ts-expect-error const response = await server.inject(getUpdateBulkRequest(), contextWithoutSecuritySolution); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index 5f9789220bc1..dc5f7855cae2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -9,7 +9,7 @@ import { updateRuleValidateTypeDependents } from '../../../../../common/detectio import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; import { updateRulesBulkSchema } from '../../../../../common/detection_engine/schemas/request/update_rules_bulk_schema'; import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; import { buildMlAuthz } from '../../../machine_learning/authz'; @@ -21,7 +21,10 @@ import { updateRules } from '../../rules/update_rules'; import { updateRulesNotifications } from '../../rules/update_rules_notifications'; import { ruleStatusSavedObjectsClientFactory } from '../../signals/rule_status_saved_objects_client'; -export const updateRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => { +export const updateRulesBulkRoute = ( + router: SecuritySolutionPluginRouter, + ml: SetupPlugins['ml'] +) => { router.put( { path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts index 96710b6f1d76..0c1500fecd6e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts @@ -75,6 +75,7 @@ describe('update_rules', () => { it('returns 404 if siem client is unavailable', async () => { const { securitySolution, ...contextWithoutSecuritySolution } = context; + // @ts-expect-error const response = await server.inject(getUpdateRequest(), contextWithoutSecuritySolution); expect(response.status).toEqual(404); expect(response.body).toEqual({ message: 'Not Found', status_code: 404 }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts index aa85747e3ce4..d3b3b23627a9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -6,7 +6,7 @@ import { updateRulesSchema } from '../../../../../common/detection_engine/schemas/request'; import { updateRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/update_rules_type_dependents'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; import { buildMlAuthz } from '../../../machine_learning/authz'; @@ -19,7 +19,7 @@ import { updateRulesNotifications } from '../../rules/update_rules_notifications import { ruleStatusSavedObjectsClientFactory } from '../../signals/rule_status_saved_objects_client'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; -export const updateRulesRoute = (router: IRouter, ml: SetupPlugins['ml']) => { +export const updateRulesRoute = (router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml']) => { router.put( { path: DETECTION_ENGINE_RULES_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts index 313cc37b20d8..4d3394cb0b77 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { SetupPlugins } from '../../../../plugin'; import { DETECTION_ENGINE_SIGNALS_MIGRATION_URL } from '../../../../../common/constants'; import { createSignalsMigrationSchema } from '../../../../../common/detection_engine/schemas/request/create_signals_migration_schema'; @@ -20,7 +20,7 @@ import { getSignalVersionsByIndex } from '../../migrations/get_signal_versions_b import { SIGNALS_TEMPLATE_VERSION } from '../index/get_signals_template'; export const createSignalsMigrationRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, security: SetupPlugins['security'] ) => { router.post( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts index 2515a5fabe99..b1be5d5df6bf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { SetupPlugins } from '../../../../plugin'; import { DETECTION_ENGINE_SIGNALS_MIGRATION_URL } from '../../../../../common/constants'; import { deleteSignalsMigrationSchema } from '../../../../../common/detection_engine/schemas/request/delete_signals_migration_schema'; @@ -14,7 +14,7 @@ import { signalsMigrationService } from '../../migrations/migration_service'; import { getMigrationSavedObjectsById } from '../../migrations/get_migration_saved_objects_by_id'; export const deleteSignalsMigrationRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, security: SetupPlugins['security'] ) => { router.delete( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts index 2c02c0768dad..186116bdc97e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { SetupPlugins } from '../../../../plugin'; import { DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL } from '../../../../../common/constants'; import { finalizeSignalsMigrationSchema } from '../../../../../common/detection_engine/schemas/request/finalize_signals_migration_schema'; @@ -16,7 +16,7 @@ import { buildSiemResponse, transformError } from '../utils'; import { getMigrationSavedObjectsById } from '../../migrations/get_migration_saved_objects_by_id'; export const finalizeSignalsMigrationRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, security: SetupPlugins['security'] ) => { router.post( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts index ed6546b0bf4f..d36fa643964a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL } from '../../../../../common/constants'; import { getSignalsMigrationStatusSchema } from '../../../../../common/detection_engine/schemas/request/get_signals_migration_status_schema'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; @@ -17,7 +17,7 @@ import { isOutdated, signalsAreOutdated } from '../../migrations/helpers'; import { getTemplateVersion } from '../index/check_template_version'; import { buildSiemResponse, transformError } from '../utils'; -export const getSignalsMigrationStatusRoute = (router: IRouter) => { +export const getSignalsMigrationStatusRoute = (router: SecuritySolutionPluginRouter) => { router.get( { path: DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts index 97b63025a86e..5db9c75b6371 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts @@ -44,6 +44,7 @@ describe('set signal status', () => { const { securitySolution, ...contextWithoutSecuritySolution } = context; const response = await server.inject( getSetSignalStatusByQueryRequest(), + // @ts-expect-error contextWithoutSecuritySolution ); expect(response.status).toEqual(404); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts index be6e57aee6d0..4201a0d1ebe5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts @@ -9,12 +9,12 @@ import { SetSignalsStatusSchemaDecoded, setSignalsStatusSchema, } from '../../../../../common/detection_engine/schemas/request/set_signal_status_schema'; -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_SIGNALS_STATUS_URL } from '../../../../../common/constants'; import { transformError, buildSiemResponse } from '../utils'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; -export const setSignalsStatusRoute = (router: IRouter) => { +export const setSignalsStatusRoute = (router: SecuritySolutionPluginRouter) => { router.post( { path: DETECTION_ENGINE_SIGNALS_STATUS_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts index 3ab4775f890a..d1010a80c5b9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../common/constants'; import { transformError, buildSiemResponse } from '../utils'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; @@ -14,7 +14,7 @@ import { QuerySignalsSchemaDecoded, } from '../../../../../common/detection_engine/schemas/request/query_signals_index_schema'; -export const querySignalsRoute = (router: IRouter) => { +export const querySignalsRoute = (router: SecuritySolutionPluginRouter) => { router.post( { path: DETECTION_ENGINE_QUERY_SIGNALS_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts index 9c508f99244c..9be3a3b5f2f9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/tags/read_tags_route.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from '../../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_TAGS_URL } from '../../../../../common/constants'; import { transformError, buildSiemResponse } from '../utils'; import { readTags } from '../../tags/read_tags'; -export const readTagsRoute = (router: IRouter) => { +export const readTagsRoute = (router: SecuritySolutionPluginRouter) => { router.get( { path: DETECTION_ENGINE_TAGS_URL, diff --git a/x-pack/plugins/security_solution/server/lib/framework/kibana_framework_adapter.ts b/x-pack/plugins/security_solution/server/lib/framework/kibana_framework_adapter.ts index 8327af846d1a..7aff8352d633 100644 --- a/x-pack/plugins/security_solution/server/lib/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/security_solution/server/lib/framework/kibana_framework_adapter.ts @@ -7,16 +7,18 @@ import { GraphQLSchema } from 'graphql'; import { runHttpQuery } from 'apollo-server-core'; import { schema as configSchema } from '@kbn/config-schema'; -import { +import type { CoreSetup, - IRouter, KibanaResponseFactory, - RequestHandlerContext, KibanaRequest, } from '../../../../../../src/core/server'; import { IndexPatternsFetcher, UI_SETTINGS } from '../../../../../../src/plugins/data/server'; import { AuthenticatedUser } from '../../../../security/common/model'; import { SetupPlugins } from '../../plugin'; +import type { + SecuritySolutionRequestHandlerContext, + SecuritySolutionPluginRouter, +} from '../../types'; import { FrameworkAdapter, @@ -27,7 +29,7 @@ import { import { buildSiemResponse } from '../detection_engine/routes/utils'; export class KibanaBackendFrameworkAdapter implements FrameworkAdapter { - private router: IRouter; + private router: SecuritySolutionPluginRouter; private security: SetupPlugins['security']; constructor(core: CoreSetup, plugins: SetupPlugins) { @@ -125,7 +127,7 @@ export class KibanaBackendFrameworkAdapter implements FrameworkAdapter { export function wrapRequest( request: KibanaRequest, - context: RequestHandlerContext, + context: SecuritySolutionRequestHandlerContext, user: AuthenticatedUser | null ): FrameworkRequest { return { diff --git a/x-pack/plugins/security_solution/server/lib/framework/types.ts b/x-pack/plugins/security_solution/server/lib/framework/types.ts index 1f626d9fb2dc..b1973e15ef95 100644 --- a/x-pack/plugins/security_solution/server/lib/framework/types.ts +++ b/x-pack/plugins/security_solution/server/lib/framework/types.ts @@ -7,9 +7,10 @@ import { IndicesGetMappingParams } from 'elasticsearch'; import { GraphQLSchema } from 'graphql'; -import { RequestHandlerContext, KibanaRequest } from '../../../../../../src/core/server'; +import { KibanaRequest } from '../../../../../../src/core/server'; import { AuthenticatedUser } from '../../../../security/common/model'; import { ESQuery } from '../../../common/typed_json'; +import type { SecuritySolutionRequestHandlerContext } from '../../types'; import { PaginationInput, PaginationInputPaginated, @@ -45,7 +46,7 @@ export interface FrameworkAdapter { export interface FrameworkRequest extends Pick { [internalFrameworkRequest]: KibanaRequest; - context: RequestHandlerContext; + context: SecuritySolutionRequestHandlerContext; user: AuthenticatedUser | null; } diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/clean_draft_timelines_route.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/clean_draft_timelines_route.ts index 67fc3167a4a2..2a366576608a 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/clean_draft_timelines_route.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/clean_draft_timelines_route.ts @@ -5,7 +5,7 @@ */ import uuid from 'uuid'; -import { IRouter } from '../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../types'; import { ConfigType } from '../../..'; import { transformError, buildSiemResponse } from '../../detection_engine/routes/utils'; import { TIMELINE_DRAFT_URL } from '../../../../common/constants'; @@ -18,7 +18,7 @@ import { cleanDraftTimelineSchema } from './schemas/clean_draft_timelines_schema import { TimelineType } from '../../../../common/types/timeline'; export const cleanDraftTimelinesRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, config: ConfigType, security: SetupPlugins['security'] ) => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/create_timelines_route.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/create_timelines_route.ts index 77cd49406baa..7f9a32a2275d 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/create_timelines_route.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/create_timelines_route.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from '../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../types'; import { TIMELINE_URL } from '../../../../common/constants'; @@ -23,7 +23,7 @@ import { createTimelines } from './utils/create_timelines'; import { DEFAULT_ERROR } from './utils/failure_cases'; export const createTimelinesRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, config: ConfigType, security: SetupPlugins['security'] ) => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/export_timelines_route.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/export_timelines_route.ts index 38ee51fb7aa0..f24a9234dd8f 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/export_timelines_route.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/export_timelines_route.ts @@ -5,7 +5,7 @@ */ import { TIMELINE_EXPORT_URL } from '../../../../common/constants'; -import { IRouter } from '../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../types'; import { ConfigType } from '../../../config'; import { transformError, buildSiemResponse } from '../../detection_engine/routes/utils'; @@ -19,7 +19,7 @@ import { buildFrameworkRequest } from './utils/common'; import { SetupPlugins } from '../../../plugin'; export const exportTimelinesRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, config: ConfigType, security: SetupPlugins['security'] ) => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/get_draft_timelines_route.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/get_draft_timelines_route.ts index 43129f0e15f0..59179dd33f5a 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/get_draft_timelines_route.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/get_draft_timelines_route.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from '../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../types'; import { ConfigType } from '../../..'; import { transformError, buildSiemResponse } from '../../detection_engine/routes/utils'; import { TIMELINE_DRAFT_URL } from '../../../../common/constants'; @@ -16,7 +16,7 @@ import { draftTimelineDefaults } from '../default_timeline'; import { getDraftTimelineSchema } from './schemas/get_draft_timelines_schema'; export const getDraftTimelinesRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, config: ConfigType, security: SetupPlugins['security'] ) => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/get_timeline_route.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/get_timeline_route.ts index e46a644d6820..9496513ed4cc 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/get_timeline_route.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/get_timeline_route.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from '../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../types'; import { TIMELINE_URL } from '../../../../common/constants'; @@ -21,7 +21,7 @@ import { getAllTimeline } from '../saved_object'; import { TimelineStatus } from '../../../../common/types/timeline'; export const getTimelineRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, config: ConfigType, security: SetupPlugins['security'] ) => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.ts index 811d4531b86a..519767786424 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/import_timelines_route.ts @@ -7,7 +7,7 @@ import { extname } from 'path'; import { Readable } from 'stream'; -import { IRouter } from '../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../types'; import { TIMELINE_IMPORT_URL } from '../../../../common/constants'; @@ -21,7 +21,7 @@ import { ImportTimelinesPayloadSchemaRt } from './schemas/import_timelines_schem import { buildFrameworkRequest } from './utils/common'; export const importTimelinesRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, config: ConfigType, security: SetupPlugins['security'] ) => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/install_prepacked_timelines_route.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/install_prepacked_timelines_route.ts index aba05054abfe..04608a821ca7 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/install_prepacked_timelines_route.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/install_prepacked_timelines_route.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from '../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../types'; import { TIMELINE_PREPACKAGED_URL } from '../../../../common/constants'; @@ -22,7 +22,7 @@ import { checkTimelineStatusRt } from './schemas/check_timelines_status_schema'; import { buildFrameworkRequest } from './utils/common'; export const installPrepackedTimelinesRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, config: ConfigType, security: SetupPlugins['security'] ) => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/update_timelines_route.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/update_timelines_route.ts index 6b8ceea80c31..2c650db0d690 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/update_timelines_route.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/update_timelines_route.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from '../../../../../../../src/core/server'; +import type { SecuritySolutionPluginRouter } from '../../../types'; import { TIMELINE_URL } from '../../../../common/constants'; @@ -20,7 +20,7 @@ import { createTimelines } from './utils/create_timelines'; import { CompareTimelinesStatus } from './utils/compare_timelines_status'; export const updateTimelinesRoute = ( - router: IRouter, + router: SecuritySolutionPluginRouter, config: ConfigType, security: SetupPlugins['security'] ) => { diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts index c230e36e4c89..b6cbe92123ad 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/utils/common.ts @@ -9,13 +9,14 @@ import fs from 'fs'; import { Readable } from 'stream'; import { createListStream } from '@kbn/utils'; -import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; +import { KibanaRequest } from 'src/core/server'; import { SetupPlugins } from '../../../../plugin'; +import type { SecuritySolutionRequestHandlerContext } from '../../../../types'; import { FrameworkRequest } from '../../../framework'; export const buildFrameworkRequest = async ( - context: RequestHandlerContext, + context: SecuritySolutionRequestHandlerContext, security: SetupPlugins['security'], request: KibanaRequest ): Promise => { @@ -25,7 +26,7 @@ export const buildFrameworkRequest = async ( return set( 'user', user, - set( + set( 'context.core.savedObjects.client', savedObjectsClient, request diff --git a/x-pack/plugins/security_solution/server/lib/types.ts b/x-pack/plugins/security_solution/server/lib/types.ts index 618710ebd5fc..06ef82de5715 100644 --- a/x-pack/plugins/security_solution/server/lib/types.ts +++ b/x-pack/plugins/security_solution/server/lib/types.ts @@ -5,8 +5,8 @@ */ import { AuthenticatedUser } from '../../../security/common/model'; -import { RequestHandlerContext } from '../../../../../src/core/server'; export { ConfigType as Configuration } from '../config'; +import type { SecuritySolutionRequestHandlerContext } from '../types'; import { FrameworkAdapter, FrameworkRequest } from './framework'; import { Hosts } from './hosts'; @@ -36,7 +36,7 @@ export interface AppBackendLibs extends AppDomainLibs { export interface SiemContext { req: FrameworkRequest; - context: RequestHandlerContext; + context: SecuritySolutionRequestHandlerContext; user: AuthenticatedUser | null; } diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index d51346ee9645..4e1521cb0f8d 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -64,7 +64,7 @@ import { EndpointAppContextService } from './endpoint/endpoint_app_context_servi import { EndpointAppContext } from './endpoint/types'; import { registerDownloadExceptionListRoute } from './endpoint/routes/artifacts'; import { initUsageCollectors } from './usage'; -import { AppRequestContext } from './types'; +import type { SecuritySolutionRequestHandlerContext } from './types'; import { registerTrustedAppsRoutes } from './endpoint/routes/trusted_apps'; import { securitySolutionSearchStrategyProvider } from './search_strategy/security_solution'; import { securitySolutionIndexFieldsProvider } from './search_strategy/index_fields'; @@ -168,10 +168,10 @@ export class Plugin implements IPlugin => Promise.resolve(config), }; - const router = core.http.createRouter(); - core.http.registerRouteHandlerContext( + const router = core.http.createRouter(); + core.http.registerRouteHandlerContext( APP_ID, - (context, request, response): AppRequestContext => ({ + (context, request, response) => ({ getAppClient: () => this.appClientFactory.create(request), }) ); @@ -355,6 +355,7 @@ export class Plugin implements IPlugin AppClient; } -declare module 'src/core/server' { - interface RequestHandlerContext { - securitySolution?: AppRequestContext; - } -} +export type SecuritySolutionRequestHandlerContext = RequestHandlerContext & { + securitySolution: AppRequestContext; + licensing: LicensingApiRequestHandlerContext; + alerting: AlertingApiRequestHandlerContext; + lists?: ListsApiRequestHandlerContext; +}; + +export type SecuritySolutionPluginRouter = IRouter; diff --git a/x-pack/plugins/snapshot_restore/server/plugin.ts b/x-pack/plugins/snapshot_restore/server/plugin.ts index 4e3d743f5372..baf39b25af4c 100644 --- a/x-pack/plugins/snapshot_restore/server/plugin.ts +++ b/x-pack/plugins/snapshot_restore/server/plugin.ts @@ -3,12 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -declare module 'kibana/server' { - interface RequestHandlerContext { - snapshotRestore?: SnapshotRestoreContext; - } -} - import { first } from 'rxjs/operators'; import { i18n } from '@kbn/i18n'; import { @@ -17,7 +11,6 @@ import { Plugin, Logger, PluginInitializerContext, - ILegacyScopedClusterClient, } from 'kibana/server'; import { PLUGIN, APP_REQUIRED_CLUSTER_PRIVILEGES } from '../common'; @@ -26,13 +19,9 @@ import { ApiRoutes } from './routes'; import { wrapEsError } from './lib'; import { isEsError } from './shared_imports'; import { elasticsearchJsPlugin } from './client/elasticsearch_sr'; -import { Dependencies } from './types'; +import type { Dependencies, SnapshotRestoreRequestHandlerContext } from './types'; import { SnapshotRestoreConfig } from './config'; -export interface SnapshotRestoreContext { - client: ILegacyScopedClusterClient; -} - async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { const [core] = await getStartServices(); const esClientConfig = { plugins: [elasticsearchJsPlugin] }; @@ -65,7 +54,7 @@ export class SnapshotRestoreServerPlugin implements Plugin return; } - const router = http.createRouter(); + const router = http.createRouter(); this.license.setup( { @@ -95,13 +84,16 @@ export class SnapshotRestoreServerPlugin implements Plugin ], }); - http.registerRouteHandlerContext('snapshotRestore', async (ctx, request) => { - this.snapshotRestoreESClient = - this.snapshotRestoreESClient ?? (await getCustomEsClient(getStartServices)); - return { - client: this.snapshotRestoreESClient.asScoped(request), - }; - }); + http.registerRouteHandlerContext( + 'snapshotRestore', + async (ctx, request) => { + this.snapshotRestoreESClient = + this.snapshotRestoreESClient ?? (await getCustomEsClient(getStartServices)); + return { + client: this.snapshotRestoreESClient.asScoped(request), + }; + } + ); this.apiRoutes.setup({ router, diff --git a/x-pack/plugins/snapshot_restore/server/services/license.ts b/x-pack/plugins/snapshot_restore/server/services/license.ts index 9b68acd073c4..4c1478340c30 100644 --- a/x-pack/plugins/snapshot_restore/server/services/license.ts +++ b/x-pack/plugins/snapshot_restore/server/services/license.ts @@ -4,15 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import { Logger } from 'src/core/server'; -import { - KibanaRequest, - KibanaResponseFactory, - RequestHandler, - RequestHandlerContext, -} from 'kibana/server'; +import type { KibanaRequest, KibanaResponseFactory, RequestHandler } from 'kibana/server'; import { LicensingPluginSetup } from '../../../licensing/server'; import { LicenseType } from '../../../licensing/common/types'; +import type { SnapshotRestoreRequestHandlerContext } from '../types'; export interface LicenseStatus { isValid: boolean; @@ -53,11 +49,13 @@ export class License { }); } - guardApiRoute(handler: RequestHandler) { + guardApiRoute( + handler: RequestHandler + ) { const license = this; return function licenseCheck( - ctx: RequestHandlerContext, + ctx: Context, request: KibanaRequest, response: KibanaResponseFactory ) { diff --git a/x-pack/plugins/snapshot_restore/server/types.ts b/x-pack/plugins/snapshot_restore/server/types.ts index eb51f086deac..dcef4b54e31b 100644 --- a/x-pack/plugins/snapshot_restore/server/types.ts +++ b/x-pack/plugins/snapshot_restore/server/types.ts @@ -3,7 +3,12 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { LegacyScopedClusterClient, IRouter } from 'src/core/server'; +import type { + LegacyScopedClusterClient, + ILegacyScopedClusterClient, + IRouter, + RequestHandlerContext, +} from 'src/core/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { SecurityPluginSetup } from '../../security/server'; import { CloudSetup } from '../../cloud/server'; @@ -20,7 +25,7 @@ export interface Dependencies { } export interface RouteDependencies { - router: IRouter; + router: SnapshotRestoreRouter; license: License; config: { isSlmEnabled: boolean; @@ -50,3 +55,22 @@ export interface ResolveIndexResponseFromES { } export type CallAsCurrentUser = LegacyScopedClusterClient['callAsCurrentUser']; + +/** + * @internal + */ +export interface SnapshotRestoreContext { + client: ILegacyScopedClusterClient; +} + +/** + * @internal + */ +export interface SnapshotRestoreRequestHandlerContext extends RequestHandlerContext { + snapshotRestore: SnapshotRestoreContext; +} + +/** + * @internal + */ +export type SnapshotRestoreRouter = IRouter; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx index 5ede5f8a3879..2c6ec23290bb 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx @@ -177,6 +177,7 @@ describe('CopyToSpaceFlyout', () => { 'space-1': { success: true, successCount: 3, + warnings: [], }, 'space-2': { success: false, @@ -195,6 +196,7 @@ describe('CopyToSpaceFlyout', () => { meta: {}, }, ], + warnings: [], }, }); @@ -259,10 +261,12 @@ describe('CopyToSpaceFlyout', () => { 'space-1': { success: true, successCount: 3, + warnings: [], }, 'space-2': { success: true, successCount: 3, + warnings: [], }, }); @@ -319,6 +323,7 @@ describe('CopyToSpaceFlyout', () => { 'space-1': { success: true, successCount: 5, + warnings: [], }, 'space-2': { success: false, @@ -359,6 +364,7 @@ describe('CopyToSpaceFlyout', () => { meta: {}, }, ], + warnings: [], }, }); @@ -366,6 +372,7 @@ describe('CopyToSpaceFlyout', () => { 'space-2': { success: true, successCount: 2, + warnings: [], }, }); @@ -490,6 +497,7 @@ describe('CopyToSpaceFlyout', () => { }, ], successResults: [{ type: savedObjectToCopy.type, id: savedObjectToCopy.id, meta: {} }], + warnings: [], }, }); @@ -571,6 +579,7 @@ describe('CopyToSpaceFlyout', () => { 'space-1': { success: true, successCount: 3, + warnings: [], }, 'space-2': { success: false, @@ -583,6 +592,7 @@ describe('CopyToSpaceFlyout', () => { meta: {}, }, ], + warnings: [], }, }); diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts index db731713811b..e06ba3499d9a 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.test.ts @@ -109,6 +109,7 @@ describe('copySavedObjectsToSpaces', () => { success: true, successCount: filteredObjects.length, successResults: [('Some success(es) occurred!' as unknown) as SavedObjectsImportSuccess], + warnings: [], }; return Promise.resolve(response); @@ -166,6 +167,7 @@ describe('copySavedObjectsToSpaces', () => { `); expect(savedObjectsExporter.exportByObjects).toHaveBeenCalledWith({ + request: expect.any(Object), excludeExportDetails: true, includeReferencesDeep: true, namespace, @@ -201,6 +203,7 @@ describe('copySavedObjectsToSpaces', () => { success: true, successCount: filteredObjects.length, successResults: [('Some success(es) occurred!' as unknown) as SavedObjectsImportSuccess], + warnings: [], }); }, }); diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts index 852f680b0245..39f31c5f8517 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/copy_to_spaces.ts @@ -29,6 +29,7 @@ export function copySavedObjectsToSpacesFactory( options: Pick ) => { const objectStream = await savedObjectsExporter.exportByObjects({ + request, namespace: spaceIdToNamespace(sourceSpaceId), includeReferencesDeep: options.includeReferences, excludeExportDetails: true, diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts index a558044d413d..6c24394b540d 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.test.ts @@ -109,6 +109,7 @@ describe('resolveCopySavedObjectsToSpacesConflicts', () => { success: true, successCount: filteredObjects.length, successResults: [('Some success(es) occurred!' as unknown) as SavedObjectsImportSuccess], + warnings: [], }; return response; @@ -173,6 +174,7 @@ describe('resolveCopySavedObjectsToSpacesConflicts', () => { `); expect(savedObjectsExporter.exportByObjects).toHaveBeenCalledWith({ + request: expect.any(Object), excludeExportDetails: true, includeReferencesDeep: true, namespace, @@ -209,6 +211,7 @@ describe('resolveCopySavedObjectsToSpacesConflicts', () => { success: true, successCount: filteredObjects.length, successResults: [('Some success(es) occurred!' as unknown) as SavedObjectsImportSuccess], + warnings: [], }); }, }); diff --git a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts index 2a671b1423e8..6033e369d3ea 100644 --- a/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts +++ b/x-pack/plugins/spaces/server/lib/copy_to_spaces/resolve_copy_conflicts.ts @@ -29,6 +29,7 @@ export function resolveCopySavedObjectsToSpacesConflictsFactory( options: Pick ) => { const objectStream = await savedObjectsExporter.exportByObjects({ + request, namespace: spaceIdToNamespace(sourceSpaceId), includeReferencesDeep: options.includeReferences, excludeExportDetails: true, diff --git a/x-pack/plugins/spaces/server/plugin.ts b/x-pack/plugins/spaces/server/plugin.ts index e50ab60a2773..24bf96e81ce1 100644 --- a/x-pack/plugins/spaces/server/plugin.ts +++ b/x-pack/plugins/spaces/server/plugin.ts @@ -36,6 +36,7 @@ import { SpacesClientService, SpacesClientWrapper, } from './spaces_client'; +import type { SpacesRequestHandlerContext } from './types'; export interface PluginsSetup { features: FeaturesPluginSetup; @@ -123,7 +124,7 @@ export class Plugin { logger: this.log, }); - const externalRouter = core.http.createRouter(); + const externalRouter = core.http.createRouter(); initExternalSpacesApi({ externalRouter, log: this.log, @@ -132,7 +133,7 @@ export class Plugin { usageStatsServicePromise, }); - const internalRouter = core.http.createRouter(); + const internalRouter = core.http.createRouter(); initInternalSpacesApi({ internalRouter, getSpacesService, diff --git a/x-pack/plugins/spaces/server/routes/api/external/index.ts b/x-pack/plugins/spaces/server/routes/api/external/index.ts index d481c2e0675f..6ece39f62180 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/index.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Logger, IRouter, CoreSetup } from 'src/core/server'; +import { Logger, CoreSetup } from 'src/core/server'; import { initDeleteSpacesApi } from './delete'; import { initGetSpaceApi } from './get'; import { initGetAllSpacesApi } from './get_all'; @@ -14,9 +14,10 @@ import { SpacesServiceStart } from '../../../spaces_service'; import { UsageStatsServiceSetup } from '../../../usage_stats'; import { initCopyToSpacesApi } from './copy_to_space'; import { initShareToSpacesApi } from './share_to_space'; +import type { SpacesRouter } from '../../../types'; export interface ExternalRouteDeps { - externalRouter: IRouter; + externalRouter: SpacesRouter; getStartServices: CoreSetup['getStartServices']; getSpacesService: () => SpacesServiceStart; usageStatsServicePromise: Promise; diff --git a/x-pack/plugins/spaces/server/routes/api/internal/index.ts b/x-pack/plugins/spaces/server/routes/api/internal/index.ts index 675cdb548543..574b7a25f2d1 100644 --- a/x-pack/plugins/spaces/server/routes/api/internal/index.ts +++ b/x-pack/plugins/spaces/server/routes/api/internal/index.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'src/core/server'; +import type { SpacesRouter } from '../../../types'; import { SpacesServiceStart } from '../../../spaces_service/spaces_service'; import { initGetActiveSpaceApi } from './get_active_space'; export interface InternalRouteDeps { - internalRouter: IRouter; + internalRouter: SpacesRouter; getSpacesService: () => SpacesServiceStart; } diff --git a/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts b/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts index d56414a12b83..d507802011dc 100644 --- a/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts +++ b/x-pack/plugins/spaces/server/routes/lib/licensed_route_handler.ts @@ -4,10 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandler } from 'kibana/server'; +import type { RequestHandler, RequestHandlerContext } from 'kibana/server'; +import type { LicensingApiRequestHandlerContext } from '../../../../licensing/server'; -export const createLicensedRouteHandler = (handler: RequestHandler) => { - const licensedRouteHandler: RequestHandler = (context, request, responseToolkit) => { +export const createLicensedRouteHandler = < + P, + Q, + B, + Context extends RequestHandlerContext & { licensing: LicensingApiRequestHandlerContext } +>( + handler: RequestHandler +) => { + const licensedRouteHandler: RequestHandler = ( + context, + request, + responseToolkit + ) => { const { license } = context.licensing; const licenseCheck = license.check('spaces', 'basic'); if (licenseCheck.state === 'unavailable' || licenseCheck.state === 'invalid') { diff --git a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts index 4fd952950733..a79651c1ae9a 100644 --- a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts +++ b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.test.ts @@ -103,6 +103,37 @@ const ERROR_NAMESPACE_SPECIFIED = 'Spaces currently determines the namespaces'; }); }); + describe('#resolve', () => { + test(`throws error if options.namespace is specified`, async () => { + const { client } = createSpacesSavedObjectsClient(); + + await expect(client.resolve('foo', '', { namespace: 'bar' })).rejects.toThrow( + ERROR_NAMESPACE_SPECIFIED + ); + }); + + test(`supplements options with the current namespace`, async () => { + const { client, baseClient } = createSpacesSavedObjectsClient(); + const expectedReturnValue = { + saved_object: createMockResponse(), + outcome: 'exactMatch' as 'exactMatch', // outcome doesn't matter, just including it for type safety + }; + baseClient.resolve.mockReturnValue(Promise.resolve(expectedReturnValue)); + + const type = Symbol(); + const id = Symbol(); + const options = Object.freeze({ foo: 'bar' }); + // @ts-expect-error + const actualReturnValue = await client.resolve(type, id, options); + + expect(actualReturnValue).toBe(expectedReturnValue); + expect(baseClient.resolve).toHaveBeenCalledWith(type, id, { + foo: 'bar', + namespace: currentSpace.expectedNamespace, + }); + }); + }); + describe('#bulkGet', () => { test(`throws error if options.namespace is specified`, async () => { const { client } = createSpacesSavedObjectsClient(); diff --git a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts index 049bd88085ed..bd09b8237a46 100644 --- a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts +++ b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts @@ -246,6 +246,28 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract { }); } + /** + * Resolves a single object, using any legacy URL alias if it exists + * + * @param type - The type of SavedObject to retrieve + * @param id - The ID of the SavedObject to retrieve + * @param {object} [options={}] + * @property {string} [options.namespace] + * @returns {promise} - { saved_object, outcome } + */ + public async resolve( + type: string, + id: string, + options: SavedObjectsBaseOptions = {} + ) { + throwErrorIfNamespaceSpecified(options); + + return await this.client.resolve(type, id, { + ...options, + namespace: spaceIdToNamespace(this.spaceId), + }); + } + /** * Updates an object * diff --git a/x-pack/plugins/spaces/server/types.ts b/x-pack/plugins/spaces/server/types.ts new file mode 100644 index 000000000000..c43a589ed577 --- /dev/null +++ b/x-pack/plugins/spaces/server/types.ts @@ -0,0 +1,20 @@ +/* + * 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 type { IRouter, RequestHandlerContext } from 'src/core/server'; +import type { LicensingApiRequestHandlerContext } from '../../licensing/server'; + +/** + * @internal + */ +export interface SpacesRequestHandlerContext extends RequestHandlerContext { + licensing: LicensingApiRequestHandlerContext; +} + +/** + * @internal + */ +export type SpacesRouter = IRouter; diff --git a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/__snapshots__/geo_containment_alert_type_expression.test.tsx.snap b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/__snapshots__/geo_containment_alert_type_expression.test.tsx.snap index 535c883aed53..860686b5211d 100644 --- a/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/__snapshots__/geo_containment_alert_type_expression.test.tsx.snap +++ b/x-pack/plugins/stack_alerts/public/alert_types/geo_containment/query_builder/__snapshots__/geo_containment_alert_type_expression.test.tsx.snap @@ -77,7 +77,7 @@ exports[`should render BoundaryIndexExpression 1`] = ` exports[`should render EntityIndexExpression 1`] = ` = ({ = ({ = { [IntervalCadence.Second]: 1000, @@ -24,7 +37,7 @@ function isCadence(cadence: IntervalCadence | string): cadence is IntervalCadenc return VALID_CADENCE.has(cadence as IntervalCadence); } -export function asInterval(ms: number): string { +export function asInterval(ms: number): Interval { const secondsRemainder = ms % 1000; const minutesRemainder = ms % 60000; return secondsRemainder ? `${ms}ms` : minutesRemainder ? `${ms / 1000}s` : `${ms / 60000}m`; @@ -34,9 +47,9 @@ export function asInterval(ms: number): string { * Returns a date that is the specified interval from now. Currently, * only minute-intervals and second-intervals are supported. * - * @param {string} interval - An interval of the form `Nm` such as `5m` + * @param {Interval} interval - An interval of the form `Nm` such as `5m` */ -export function intervalFromNow(interval?: string): Date | undefined { +export function intervalFromNow(interval?: Interval): Date | undefined { if (interval === undefined) { return; } @@ -48,9 +61,9 @@ export function intervalFromNow(interval?: string): Date | undefined { * only minute-intervals and second-intervals are supported. * * @param {Date} date - The date to add interval to - * @param {string} interval - An interval of the form `Nm` such as `5m` + * @param {Interval} interval - An interval of the form `Nm` such as `5m` */ -export function intervalFromDate(date: Date, interval?: string): Date | undefined { +export function intervalFromDate(date: Date, interval?: Interval): Date | undefined { if (interval === undefined) { return; } @@ -59,9 +72,11 @@ export function intervalFromDate(date: Date, interval?: string): Date | undefine export function maxIntervalFromDate( date: Date, - ...intervals: Array + ...intervals: Array ): Date | undefined { - const maxSeconds = Math.max(...intervals.filter(isString).map(parseIntervalAsSecond)); + const maxSeconds = Math.max( + ...intervals.filter(isString).map((interval) => parseIntervalAsSecond(interval as Interval)) + ); if (!isNaN(maxSeconds)) { return secondsFromDate(date, maxSeconds); } @@ -91,14 +106,14 @@ export function secondsFromDate(date: Date, secs: number): Date { /** * Verifies that the specified interval matches our expected format. * - * @param {string} interval - An interval such as `5m` or `10s` + * @param {Interval} interval - An interval such as `5m` or `10s` * @returns {number} The interval as seconds */ -export const parseIntervalAsSecond = memoize((interval: string): number => { +export const parseIntervalAsSecond = memoize((interval: Interval): number => { return Math.round(parseIntervalAsMillisecond(interval) / 1000); }); -export const parseIntervalAsMillisecond = memoize((interval: string): number => { +export const parseIntervalAsMillisecond = memoize((interval: Interval): number => { const numericAsStr: string = interval.slice(0, -1); const numeric: number = parseInt(numericAsStr, 10); const cadence: IntervalCadence | string = interval.slice(-1); diff --git a/x-pack/plugins/task_manager/server/lib/result_type.ts b/x-pack/plugins/task_manager/server/lib/result_type.ts index d21c17d3bb5b..cd1d417c7949 100644 --- a/x-pack/plugins/task_manager/server/lib/result_type.ts +++ b/x-pack/plugins/task_manager/server/lib/result_type.ts @@ -39,6 +39,14 @@ export function isErr(result: Result): result is Err { return !isOk(result); } +export function tryAsResult(fn: () => T): Result { + try { + return asOk(fn()); + } catch (e) { + return asErr(e); + } +} + export async function promiseResult(future: Promise): Promise> { try { return asOk(await future); diff --git a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.test.ts b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.test.ts index 21ea72cbbb00..625776db3250 100644 --- a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.test.ts +++ b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.test.ts @@ -11,7 +11,12 @@ import sinon from 'sinon'; import { take, tap, bufferCount, skip, map } from 'rxjs/operators'; import { ConcreteTaskInstance, TaskStatus } from '../task'; -import { asTaskRunEvent, asTaskPollingCycleEvent, TaskTiming } from '../task_events'; +import { + asTaskRunEvent, + asTaskPollingCycleEvent, + TaskTiming, + asTaskManagerStatEvent, +} from '../task_events'; import { asOk } from '../lib/result_type'; import { TaskLifecycleEvent } from '../polling_lifecycle'; import { TaskRunResult } from '../task_running'; @@ -530,6 +535,7 @@ describe('Task Run Statistics', () => { events$.next( asTaskPollingCycleEvent(asOk({ result: FillPoolResult.NoTasksClaimed, timing })) ); + events$.next(asTaskManagerStatEvent('pollingDelay', asOk(0))); events$.next( asTaskPollingCycleEvent(asOk({ result: FillPoolResult.NoTasksClaimed, timing })) ); diff --git a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts index b881759d9103..82fe0ec81343 100644 --- a/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts +++ b/x-pack/plugins/task_manager/server/monitoring/task_run_statistics.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { combineLatest, merge, Observable, of } from 'rxjs'; +import { combineLatest, Observable } from 'rxjs'; import { filter, startWith, map } from 'rxjs/operators'; import { JsonObject } from 'src/plugins/kibana_utils/common'; import { isNumber, mapValues } from 'lodash'; @@ -160,19 +160,12 @@ export function createTaskRunAggregator( }) ), // get DateTime of latest polling delay refresh - merge( - /** - * as `combineLatest` hangs until it has its first value and we're not likely to reconfigure the delay in normal deployments, we needed some initial value. - I've used _now_ (`new Date().toISOString()`) as it made the most sense (it would be the time Kibana started), but it _could_ be confusing in the future. - */ - of(new Date().toISOString()), - taskPollingLifecycle.events.pipe( - filter( - (taskEvent: TaskLifecycleEvent) => - isTaskManagerStatEvent(taskEvent) && taskEvent.id === 'pollingDelay' - ), - map(() => new Date().toISOString()) - ) + taskPollingLifecycle.events.pipe( + filter( + (taskEvent: TaskLifecycleEvent) => + isTaskManagerStatEvent(taskEvent) && taskEvent.id === 'pollingDelay' + ), + map(() => new Date().toISOString()) ), ]).pipe( map(([{ polling }, pollingDelay]) => ({ diff --git a/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.test.ts b/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.test.ts index 9f0eeedf0588..6b57f3470aec 100644 --- a/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.test.ts +++ b/x-pack/plugins/task_manager/server/polling/delay_on_claim_conflicts.test.ts @@ -20,9 +20,9 @@ describe('delayOnClaimConflicts', () => { test( 'initializes with a delay of 0', - fakeSchedulers(async (advance) => { + fakeSchedulers(async () => { const pollInterval = 100; - const maxWorkers = 100; + const maxWorkers = 10; const taskLifecycleEvents$ = new Subject(); const delays = delayOnClaimConflicts( of(maxWorkers), @@ -40,9 +40,9 @@ describe('delayOnClaimConflicts', () => { test( 'emits a random delay whenever p50 of claim clashes exceed 80% of available max_workers', - fakeSchedulers(async (advance) => { + fakeSchedulers(async () => { const pollInterval = 100; - const maxWorkers = 100; + const maxWorkers = 10; const taskLifecycleEvents$ = new Subject(); const delays$ = delayOnClaimConflicts( @@ -61,7 +61,7 @@ describe('delayOnClaimConflicts', () => { result: FillPoolResult.PoolFilled, stats: { tasksUpdated: 0, - tasksConflicted: 80, + tasksConflicted: 8, tasksClaimed: 0, }, docs: [], @@ -80,9 +80,9 @@ describe('delayOnClaimConflicts', () => { test( 'doesnt emit a new delay when conflicts have reduced', - fakeSchedulers(async (advance) => { + fakeSchedulers(async () => { const pollInterval = 100; - const maxWorkers = 100; + const maxWorkers = 10; const taskLifecycleEvents$ = new Subject(); const handler = jest.fn(); @@ -104,7 +104,7 @@ describe('delayOnClaimConflicts', () => { result: FillPoolResult.PoolFilled, stats: { tasksUpdated: 0, - tasksConflicted: 80, + tasksConflicted: 8, tasksClaimed: 0, }, docs: [], @@ -124,7 +124,7 @@ describe('delayOnClaimConflicts', () => { result: FillPoolResult.PoolFilled, stats: { tasksUpdated: 0, - tasksConflicted: 70, + tasksConflicted: 7, tasksClaimed: 0, }, docs: [], @@ -135,14 +135,14 @@ describe('delayOnClaimConflicts', () => { await sleep(0); expect(handler.mock.calls.length).toEqual(2); - // shift average back up to threshold (70 + 90) / 2 = 80 + // shift average back up to threshold (7 + 9) / 2 = 80% of maxWorkers taskLifecycleEvents$.next( asTaskPollingCycleEvent( asOk({ result: FillPoolResult.PoolFilled, stats: { tasksUpdated: 0, - tasksConflicted: 90, + tasksConflicted: 9, tasksClaimed: 0, }, docs: [], diff --git a/x-pack/plugins/task_manager/server/polling/task_poller.ts b/x-pack/plugins/task_manager/server/polling/task_poller.ts index fac0137f38ba..076dc8306cd9 100644 --- a/x-pack/plugins/task_manager/server/polling/task_poller.ts +++ b/x-pack/plugins/task_manager/server/polling/task_poller.ts @@ -84,8 +84,9 @@ export function createTaskPoller({ }) ), ]).pipe( - // pollDelay can only shift `timer` at the scale of `period`, so we round - // the delay to modulo the interval period + // We don't have control over `pollDelay` in the poller, and a change to `delayOnClaimConflicts` could accidentally cause us to pause Task Manager + // polling for a far longer duration that we intended. + // Since the goal is to shift it within the range of `period`, we use modulo as a safe guard to ensure this doesn't happen. switchMap(([period, pollDelay]) => timer(period + (pollDelay % period), period)), mapTo(none) ) diff --git a/x-pack/plugins/task_manager/server/polling_lifecycle.ts b/x-pack/plugins/task_manager/server/polling_lifecycle.ts index 1133d1c269ca..d698686a2166 100644 --- a/x-pack/plugins/task_manager/server/polling_lifecycle.ts +++ b/x-pack/plugins/task_manager/server/polling_lifecycle.ts @@ -129,10 +129,7 @@ export class TaskPollingLifecycle { this.events$, config.version_conflict_threshold, config.monitored_stats_running_average_window - ); - pollIntervalDelay$.subscribe((delay) => { - emitEvent(asTaskManagerStatEvent('pollingDelay', asOk(delay))); - }); + ).pipe(tap((delay) => emitEvent(asTaskManagerStatEvent('pollingDelay', asOk(delay))))); // the task poller that polls for work on fixed intervals and on demand const poller$: Observable< diff --git a/x-pack/plugins/task_manager/server/task.ts b/x-pack/plugins/task_manager/server/task.ts index e832a95ac3ca..9e2a2a2074a8 100644 --- a/x-pack/plugins/task_manager/server/task.ts +++ b/x-pack/plugins/task_manager/server/task.ts @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import Joi from 'joi'; +import { schema, TypeOf } from '@kbn/config-schema'; +import { Interval, isInterval, parseIntervalAsMillisecond } from './lib/intervals'; +import { isErr, tryAsResult } from './lib/result_type'; /* * Type definitions and validations for tasks. @@ -83,17 +85,8 @@ export interface FailedTaskResult { status: TaskStatus.Failed; } -export const validateRunResult = Joi.object({ - runAt: Joi.date().optional(), - schedule: Joi.object().optional(), - error: Joi.object().optional(), - state: Joi.object().optional(), -}).optional(); - export type RunFunction = () => Promise; - export type CancelFunction = () => Promise; - export interface CancellableTask { run: RunFunction; cancel?: CancelFunction; @@ -101,40 +94,53 @@ export interface CancellableTask { export type TaskRunCreatorFunction = (context: RunContext) => CancellableTask; +export const taskDefinitionSchema = schema.object( + { + /** + * A unique identifier for the type of task being defined. + */ + type: schema.string(), + /** + * A brief, human-friendly title for this task. + */ + title: schema.maybe(schema.string()), + /** + * An optional more detailed description of what this task does. + */ + description: schema.maybe(schema.string()), + /** + * How long, in minutes or seconds, the system should wait for the task to complete + * before it is considered to be timed out. (e.g. '5m', the default). If + * the task takes longer than this, Kibana will send it a kill command and + * the task will be re-attempted. + */ + timeout: schema.string({ + defaultValue: '5m', + }), + /** + * Up to how many times the task should retry when it fails to run. This will + * default to the global variable. + */ + maxAttempts: schema.maybe( + schema.number({ + min: 1, + }) + ), + }, + { + validate({ timeout }) { + if (!isInterval(timeout) || isErr(tryAsResult(() => parseIntervalAsMillisecond(timeout)))) { + return `Invalid timeout "${timeout}". Timeout must be of the form "{number}{cadance}" where number is an integer. Example: 5m.`; + } + }, + } +); + /** * Defines a task which can be scheduled and run by the Kibana * task manager. */ -export interface TaskDefinition { - /** - * A unique identifier for the type of task being defined. - */ - type: string; - - /** - * A brief, human-friendly title for this task. - */ - title: string; - - /** - * An optional more detailed description of what this task does. - */ - description?: string; - - /** - * How long, in minutes or seconds, the system should wait for the task to complete - * before it is considered to be timed out. (e.g. '5m', the default). If - * the task takes longer than this, Kibana will send it a kill command and - * the task will be re-attempted. - */ - timeout?: string; - - /** - * Up to how many times the task should retry when it fails to run. This will - * default to the global variable. - */ - maxAttempts?: number; - +export type TaskDefinition = TypeOf & { /** * Function that customizes how the task should behave when the task fails. This * function can return `true`, `false` or a Date. True will tell task manager @@ -149,17 +155,7 @@ export interface TaskDefinition { * and an optional cancel function which cancels the task. */ createTaskRunner: TaskRunCreatorFunction; -} - -export const validateTaskDefinition = Joi.object({ - type: Joi.string().required(), - title: Joi.string().optional(), - description: Joi.string().optional(), - timeout: Joi.string().default('5m'), - maxAttempts: Joi.number().min(1).optional(), - createTaskRunner: Joi.func().required(), - getRetry: Joi.func().optional(), -}).default(); +}; export enum TaskStatus { Idle = 'idle', @@ -174,12 +170,11 @@ export enum TaskLifecycleResult { } export type TaskLifecycle = TaskStatus | TaskLifecycleResult; - export interface IntervalSchedule { /** * An interval in minutes (e.g. '5m'). If specified, this is a recurring task. * */ - interval: string; + interval: Interval; } /* diff --git a/x-pack/plugins/task_manager/server/task_pool.test.ts b/x-pack/plugins/task_manager/server/task_pool.test.ts index 95768bb2f1af..324e376c32d9 100644 --- a/x-pack/plugins/task_manager/server/task_pool.test.ts +++ b/x-pack/plugins/task_manager/server/task_pool.test.ts @@ -203,14 +203,15 @@ describe('TaskPool', () => { sinon.assert.calledOnce(secondRun); }); - test('run cancels expired tasks prior to running new tasks', async () => { + test.skip('run cancels expired tasks prior to running new tasks', async () => { const logger = loggingSystemMock.create().get(); const pool = new TaskPool({ maxWorkers$: of(2), logger, }); - const expired = resolvable(); + const readyToExpire = resolvable(); + const taskHasExpired = resolvable(); const shouldRun = sinon.spy(() => Promise.resolve()); const shouldNotRun = sinon.spy(() => Promise.resolve()); const now = new Date(); @@ -218,8 +219,9 @@ describe('TaskPool', () => { { ...mockTask(), async run() { + await readyToExpire; this.isExpired = true; - expired.resolve(); + taskHasExpired.resolve(); await sleep(10); return asOk({ state: {} }); }, @@ -246,9 +248,11 @@ describe('TaskPool', () => { expect(pool.occupiedWorkers).toEqual(2); expect(pool.availableWorkers).toEqual(0); - await expired; + readyToExpire.resolve(); + await taskHasExpired; expect(await pool.run([{ ...mockTask() }])).toBeTruthy(); + sinon.assert.calledOnce(shouldRun); sinon.assert.notCalled(shouldNotRun); @@ -260,6 +264,53 @@ describe('TaskPool', () => { ); }); + test('calls to availableWorkers ensures we cancel expired tasks', async () => { + const pool = new TaskPool({ + maxWorkers$: of(1), + logger: loggingSystemMock.create().get(), + }); + + const taskIsRunning = resolvable(); + const taskHasExpired = resolvable(); + const cancel = sinon.spy(() => Promise.resolve()); + const now = new Date(); + expect( + await pool.run([ + { + ...mockTask(), + async run() { + await sleep(10); + this.isExpired = true; + taskIsRunning.resolve(); + await taskHasExpired; + return asOk({ state: {} }); + }, + get expiration() { + return new Date(now.getTime() + 10); + }, + get startedAt() { + return now; + }, + cancel, + }, + ]) + ).toEqual(TaskPoolRunResult.RunningAtCapacity); + + await taskIsRunning; + + sinon.assert.notCalled(cancel); + expect(pool.occupiedWorkers).toEqual(1); + // The call to `availableWorkers` will clear the expired task so it's 1 instead of 0 + expect(pool.availableWorkers).toEqual(1); + sinon.assert.calledOnce(cancel); + + expect(pool.occupiedWorkers).toEqual(0); + expect(pool.availableWorkers).toEqual(1); + // ensure cancel isn't called twice + sinon.assert.calledOnce(cancel); + taskHasExpired.resolve(); + }); + test('logs if cancellation errors', async () => { const logger = loggingSystemMock.create().get(); const pool = new TaskPool({ diff --git a/x-pack/plugins/task_manager/server/task_pool.ts b/x-pack/plugins/task_manager/server/task_pool.ts index 561a222310f3..db17e75639ed 100644 --- a/x-pack/plugins/task_manager/server/task_pool.ts +++ b/x-pack/plugins/task_manager/server/task_pool.ts @@ -85,6 +85,10 @@ export class TaskPool { // this should happen less often than the actual changes to the worker queue // so is lighter than emitting the load every time we add/remove a task from the queue this.load$.next(asTaskManagerStatEvent('load', asOk(this.workerLoad))); + // cancel expired task whenever a call is made to check for capacity + // this ensures that we don't end up with a queue of hung tasks causing both + // the poller and the pool from hanging due to lack of capacity + this.cancelExpiredTasks(); return this.maxWorkers - this.occupiedWorkers; } @@ -96,19 +100,7 @@ export class TaskPool { * @param {TaskRunner[]} tasks * @returns {Promise} */ - public run = (tasks: TaskRunner[]) => { - this.cancelExpiredTasks(); - return this.attemptToRun(tasks); - }; - - public cancelRunningTasks() { - this.logger.debug('Cancelling running tasks.'); - for (const task of this.running) { - this.cancelTask(task); - } - } - - private async attemptToRun(tasks: TaskRunner[]): Promise { + public run = async (tasks: TaskRunner[]): Promise => { const [tasksToRun, leftOverTasks] = partitionListByCount(tasks, this.availableWorkers); if (tasksToRun.length) { performance.mark('attemptToRun_start'); @@ -135,13 +127,20 @@ export class TaskPool { if (leftOverTasks.length) { if (this.availableWorkers) { - return this.attemptToRun(leftOverTasks); + return this.run(leftOverTasks); } return TaskPoolRunResult.RanOutOfCapacity; } else if (!this.availableWorkers) { return TaskPoolRunResult.RunningAtCapacity; } return TaskPoolRunResult.RunningAllClaimedTasks; + }; + + public cancelRunningTasks() { + this.logger.debug('Cancelling running tasks.'); + for (const task of this.running) { + this.cancelTask(task); + } } private handleMarkAsRunning(taskRunner: TaskRunner) { diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts index 3777d89ce63d..42cc71f759bf 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.test.ts @@ -10,10 +10,10 @@ import { secondsFromNow } from '../lib/intervals'; import { asOk, asErr } from '../lib/result_type'; import { TaskManagerRunner, TaskRunResult } from '../task_running'; import { TaskEvent, asTaskRunEvent, asTaskMarkRunningEvent, TaskRun } from '../task_events'; -import { ConcreteTaskInstance, TaskStatus, TaskDefinition, SuccessfulRunResult } from '../task'; +import { ConcreteTaskInstance, TaskStatus } from '../task'; import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; import moment from 'moment'; -import { TaskTypeDictionary } from '../task_type_dictionary'; +import { TaskDefinitionRegistry, TaskTypeDictionary } from '../task_type_dictionary'; import { mockLogger } from '../test_utils'; import { throwUnrecoverableError } from './errors'; @@ -41,24 +41,6 @@ describe('TaskManagerRunner', () => { expect(runner.toString()).toEqual('bar "foo"'); }); - test('warns if the task returns an unexpected result', async () => { - await allowsReturnType(undefined); - await allowsReturnType({}); - await allowsReturnType({ - runAt: new Date(), - }); - await allowsReturnType({ - error: new Error('Dang it!'), - }); - await allowsReturnType({ - state: { shazm: true }, - }); - await disallowsReturnType('hm....'); - await disallowsReturnType({ - whatIsThis: '?!!?', - }); - }); - test('queues a reattempt if the task fails', async () => { const initialAttempts = _.random(0, 2); const id = Date.now().toString(); @@ -684,9 +666,7 @@ describe('TaskManagerRunner', () => { store.update = sinon .stub() - .throws( - SavedObjectsErrorHelpers.decorateConflictError(new Error('repo error')).output.payload - ); + .throws(SavedObjectsErrorHelpers.decorateConflictError(new Error('repo error'))); expect(await runner.markTaskAsRunning()).toEqual(false); }); @@ -717,15 +697,126 @@ describe('TaskManagerRunner', () => { store.update = sinon .stub() - .throws(SavedObjectsErrorHelpers.createGenericNotFoundError('type', 'id').output.payload); - - return expect(runner.markTaskAsRunning()).rejects.toMatchInlineSnapshot(` - Object { - "error": "Not Found", - "message": "Saved object [type/id] not found", - "statusCode": 404, - } - `); + .throws(SavedObjectsErrorHelpers.createGenericNotFoundError('type', 'id')); + + return expect(runner.markTaskAsRunning()).rejects.toMatchInlineSnapshot( + `[Error: Saved object [type/id] not found]` + ); + }); + + test(`it tries to increment a task's attempts when markTaskAsRunning fails for unexpected reasons`, async () => { + const id = _.random(1, 20).toString(); + const initialAttempts = _.random(1, 3); + const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); + const timeoutMinutes = 1; + const getRetryStub = sinon.stub().returns(nextRetry); + const { runner, store } = testOpts({ + instance: { + id, + attempts: initialAttempts, + schedule: undefined, + }, + definitions: { + bar: { + title: 'Bar!', + timeout: `${timeoutMinutes}m`, + getRetry: getRetryStub, + createTaskRunner: () => ({ + run: async () => undefined, + }), + }, + }, + }); + + store.update = sinon.stub(); + store.update.onFirstCall().throws(SavedObjectsErrorHelpers.createBadRequestError('type')); + store.update.onSecondCall().resolves(); + + await expect(runner.markTaskAsRunning()).rejects.toMatchInlineSnapshot( + `[Error: type: Bad Request]` + ); + + sinon.assert.calledWith(store.update, { + ...mockInstance({ + id, + attempts: initialAttempts + 1, + schedule: undefined, + }), + status: TaskStatus.Idle, + startedAt: null, + retryAt: null, + ownerId: null, + }); + }); + + test(`it doesnt try to increment a task's attempts when markTaskAsRunning fails for version conflict`, async () => { + const id = _.random(1, 20).toString(); + const initialAttempts = _.random(1, 3); + const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); + const timeoutMinutes = 1; + const getRetryStub = sinon.stub().returns(nextRetry); + const { runner, store } = testOpts({ + instance: { + id, + attempts: initialAttempts, + schedule: undefined, + }, + definitions: { + bar: { + title: 'Bar!', + timeout: `${timeoutMinutes}m`, + getRetry: getRetryStub, + createTaskRunner: () => ({ + run: async () => undefined, + }), + }, + }, + }); + + store.update = sinon.stub(); + store.update.onFirstCall().throws(SavedObjectsErrorHelpers.createConflictError('type', 'id')); + store.update.onSecondCall().resolves(); + + await expect(runner.markTaskAsRunning()).resolves.toMatchInlineSnapshot(`false`); + + sinon.assert.calledOnce(store.update); + }); + + test(`it doesnt try to increment a task's attempts when markTaskAsRunning fails due to Saved Object not being found`, async () => { + const id = _.random(1, 20).toString(); + const initialAttempts = _.random(1, 3); + const nextRetry = new Date(Date.now() + _.random(15, 100) * 1000); + const timeoutMinutes = 1; + const getRetryStub = sinon.stub().returns(nextRetry); + const { runner, store } = testOpts({ + instance: { + id, + attempts: initialAttempts, + schedule: undefined, + }, + definitions: { + bar: { + title: 'Bar!', + timeout: `${timeoutMinutes}m`, + getRetry: getRetryStub, + createTaskRunner: () => ({ + run: async () => undefined, + }), + }, + }, + }); + + store.update = sinon.stub(); + store.update + .onFirstCall() + .throws(SavedObjectsErrorHelpers.createGenericNotFoundError('type', 'id')); + store.update.onSecondCall().resolves(); + + await expect(runner.markTaskAsRunning()).rejects.toMatchInlineSnapshot( + `[Error: Saved object [type/id] not found]` + ); + + sinon.assert.calledOnce(store.update); }); test('uses getRetry (returning true) to set retryAt when defined', async () => { @@ -1121,7 +1212,7 @@ describe('TaskManagerRunner', () => { interface TestOpts { instance?: Partial; - definitions?: Record>; + definitions?: TaskDefinitionRegistry; onTaskEvent?: (event: TaskEvent) => void; } @@ -1132,12 +1223,8 @@ describe('TaskManagerRunner', () => { }; } - function testOpts(opts: TestOpts) { - const callCluster = sinon.stub(); - const createTaskRunner = sinon.stub(); - const logger = mockLogger(); - - const instance = Object.assign( + function mockInstance(instance: Partial = {}) { + return Object.assign( { id: 'foo', taskType: 'bar', @@ -1155,8 +1242,16 @@ describe('TaskManagerRunner', () => { user: 'example', ownerId: null, }, - opts.instance || {} + instance ); + } + + function testOpts(opts: TestOpts) { + const callCluster = sinon.stub(); + const createTaskRunner = sinon.stub(); + const logger = mockLogger(); + + const instance = mockInstance(opts.instance); const store = { update: sinon.stub(), @@ -1196,34 +1291,4 @@ describe('TaskManagerRunner', () => { instance, }; } - - async function testReturn(result: unknown, shouldBeValid: boolean) { - const { runner, logger } = testOpts({ - definitions: { - bar: { - title: 'Bar!', - createTaskRunner: () => ({ - run: async () => result as SuccessfulRunResult, - }), - }, - }, - }); - - await runner.run(); - - if (shouldBeValid) { - expect(logger.warn).not.toHaveBeenCalled(); - } else { - expect(logger.warn).toHaveBeenCalledTimes(1); - expect(logger.warn.mock.calls[0][0]).toMatch(/invalid task result/i); - } - } - - function allowsReturnType(result: unknown) { - return testReturn(result, true); - } - - function disallowsReturnType(result: unknown) { - return testReturn(result, false); - } }); diff --git a/x-pack/plugins/task_manager/server/task_running/task_runner.ts b/x-pack/plugins/task_manager/server/task_running/task_runner.ts index d281a65da332..e4e2595d4451 100644 --- a/x-pack/plugins/task_manager/server/task_running/task_runner.ts +++ b/x-pack/plugins/task_manager/server/task_running/task_runner.ts @@ -10,14 +10,23 @@ * rescheduling, middleware application, etc. */ -import { Logger } from 'src/core/server'; import apm from 'elastic-apm-node'; import { performance } from 'perf_hooks'; -import Joi from 'joi'; import { identity, defaults, flow } from 'lodash'; +import { Logger, SavedObjectsErrorHelpers } from '../../../../../src/core/server'; import { Middleware } from '../lib/middleware'; -import { asOk, asErr, mapErr, eitherAsync, unwrap, isOk, mapOk, Result } from '../lib/result_type'; +import { + asOk, + asErr, + mapErr, + eitherAsync, + unwrap, + isOk, + mapOk, + Result, + promiseResult, +} from '../lib/result_type'; import { TaskRun, TaskMarkRunning, @@ -36,7 +45,6 @@ import { FailedRunResult, FailedTaskResult, TaskDefinition, - validateRunResult, TaskStatus, } from '../task'; import { TaskTypeDictionary } from '../task_type_dictionary'; @@ -228,17 +236,15 @@ export class TaskManagerRunner implements TaskRunner { 'taskManager' ); - const VERSION_CONFLICT_STATUS = 409; const now = new Date(); + try { + const { taskInstance } = await this.beforeMarkRunning({ + taskInstance: this.instance, + }); - const { taskInstance } = await this.beforeMarkRunning({ - taskInstance: this.instance, - }); - - const attempts = taskInstance.attempts + 1; - const ownershipClaimedUntil = taskInstance.retryAt; + const attempts = taskInstance.attempts + 1; + const ownershipClaimedUntil = taskInstance.retryAt; - try { const { id } = taskInstance; const timeUntilClaimExpires = howManyMsUntilOwnershipClaimExpires(ownershipClaimedUntil); @@ -286,7 +292,16 @@ export class TaskManagerRunner implements TaskRunner { if (apmTrans) apmTrans.end('failure'); performanceStopMarkingTaskAsRunning(); this.onTaskEvent(asTaskMarkRunningEvent(this.id, asErr(error))); - if (error.statusCode !== VERSION_CONFLICT_STATUS) { + if (!SavedObjectsErrorHelpers.isConflictError(error)) { + if (!SavedObjectsErrorHelpers.isNotFoundError(error)) { + // try to release claim as an unknown failure prevented us from marking as running + mapErr((errReleaseClaim: Error) => { + this.logger.error( + `[Task Runner] Task ${this.instance.id} failed to release claim after failure: ${errReleaseClaim}` + ); + }, await this.releaseClaimAndIncrementAttempts()); + } + throw error; } } @@ -311,20 +326,22 @@ export class TaskManagerRunner implements TaskRunner { private validateResult( result?: SuccessfulRunResult | FailedRunResult | void ): Result { - const { error } = Joi.validate(result, validateRunResult); - - if (error) { - this.logger.warn(`Invalid task result for ${this}: ${error.message}`); - return asErr({ - error: new Error(`Invalid task result for ${this}: ${error.message}`), - state: {}, - }); - } - if (!result) { - return asOk(EMPTY_RUN_RESULT); - } + return isFailedRunResult(result) + ? asErr({ ...result, error: result.error }) + : asOk(result || EMPTY_RUN_RESULT); + } - return isFailedRunResult(result) ? asErr({ ...result, error: result.error }) : asOk(result); + private async releaseClaimAndIncrementAttempts(): Promise> { + return promiseResult( + this.bufferedTaskStore.update({ + ...this.instance, + status: TaskStatus.Idle, + attempts: this.instance.attempts + 1, + startedAt: null, + retryAt: null, + ownerId: null, + }) + ); } private shouldTryToScheduleRetry(): boolean { diff --git a/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts b/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts index e1d6ef17f5f9..bd532c38725d 100644 --- a/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts +++ b/x-pack/plugins/task_manager/server/task_type_dictionary.test.ts @@ -6,7 +6,7 @@ import { get } from 'lodash'; import { RunContext, TaskDefinition } from './task'; -import { sanitizeTaskDefinitions } from './task_type_dictionary'; +import { sanitizeTaskDefinitions, TaskDefinitionRegistry } from './task_type_dictionary'; interface Opts { numTasks: number; @@ -73,8 +73,9 @@ describe('taskTypeDictionary', () => { it('throws a validation exception for invalid task definition', () => { const runsanitize = () => { - const taskDefinitions = { + const taskDefinitions: TaskDefinitionRegistry = { some_kind_of_task: { + // @ts-ignore fail: 'extremely', // cause a validation failure type: 'breaky_task', title: 'Test XYZ', @@ -94,6 +95,62 @@ describe('taskTypeDictionary', () => { return sanitizeTaskDefinitions(taskDefinitions); }; - expect(runsanitize).toThrowError(); + expect(runsanitize).toThrowErrorMatchingInlineSnapshot( + `"[fail]: definition for this key is missing"` + ); + }); + + it('throws a validation exception for invalid timeout on task definition', () => { + const runsanitize = () => { + const taskDefinitions: TaskDefinitionRegistry = { + some_kind_of_task: { + title: 'Test XYZ', + timeout: '15 days', + description: `Actually this won't work`, + createTaskRunner() { + return { + async run() { + return { + state: {}, + }; + }, + }; + }, + }, + }; + + return sanitizeTaskDefinitions(taskDefinitions); + }; + + expect(runsanitize).toThrowErrorMatchingInlineSnapshot( + `"Invalid timeout \\"15 days\\". Timeout must be of the form \\"{number}{cadance}\\" where number is an integer. Example: 5m."` + ); + }); + + it('throws a validation exception for invalid floating point timeout on task definition', () => { + const runsanitize = () => { + const taskDefinitions: TaskDefinitionRegistry = { + some_kind_of_task: { + title: 'Test XYZ', + timeout: '1.5h', + description: `Actually this won't work`, + createTaskRunner() { + return { + async run() { + return { + state: {}, + }; + }, + }; + }, + }, + }; + + return sanitizeTaskDefinitions(taskDefinitions); + }; + + expect(runsanitize).toThrowErrorMatchingInlineSnapshot( + `"Invalid timeout \\"1.5h\\". Timeout must be of the form \\"{number}{cadance}\\" where number is an integer. Example: 5m."` + ); }); }); diff --git a/x-pack/plugins/task_manager/server/task_type_dictionary.ts b/x-pack/plugins/task_manager/server/task_type_dictionary.ts index 451b5dd7cad5..c66b117bde88 100644 --- a/x-pack/plugins/task_manager/server/task_type_dictionary.ts +++ b/x-pack/plugins/task_manager/server/task_type_dictionary.ts @@ -3,23 +3,13 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import Joi from 'joi'; -import { TaskDefinition, validateTaskDefinition } from './task'; +import { TaskDefinition, taskDefinitionSchema } from './task'; import { Logger } from '../../../../src/core/server'; -/* - * The TaskManager is the public interface into the task manager system. This glues together - * all of the disparate modules in one integration point. The task manager operates in two different ways: - * - * - pre-init, it allows middleware registration, but disallows task manipulation - * - post-init, it disallows middleware registration, but allows task manipulation - * - * Due to its complexity, this is mostly tested by integration tests (see readme). - */ - -/** - * The public interface into the task manager system. - */ +export type TaskDefinitionRegistry = Record< + string, + Omit & Pick, 'timeout'> +>; export class TaskTypeDictionary { private definitions = new Map(); private logger: Logger; @@ -57,7 +47,7 @@ export class TaskTypeDictionary { * Method for allowing consumers to register task definitions into the system. * @param taskDefinitions - The Kibana task definitions dictionary */ - public registerTaskDefinitions(taskDefinitions: Record>) { + public registerTaskDefinitions(taskDefinitions: TaskDefinitionRegistry) { const duplicate = Object.keys(taskDefinitions).find((type) => this.definitions.has(type)); if (duplicate) { throw new Error(`Task ${duplicate} is already defined!`); @@ -79,10 +69,8 @@ export class TaskTypeDictionary { * * @param taskDefinitions - The Kibana task definitions dictionary */ -export function sanitizeTaskDefinitions( - taskDefinitions: Record> -): TaskDefinition[] { - return Object.entries(taskDefinitions).map(([type, rawDefinition]) => - Joi.attempt({ type, ...rawDefinition }, validateTaskDefinition) - ); +export function sanitizeTaskDefinitions(taskDefinitions: TaskDefinitionRegistry): TaskDefinition[] { + return Object.entries(taskDefinitions).map(([type, rawDefinition]) => { + return taskDefinitionSchema.validate({ type, ...rawDefinition }) as TaskDefinition; + }); } diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_latest_function_config.test.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_latest_function_config.test.ts new file mode 100644 index 000000000000..593e37175a2e --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_latest_function_config.test.ts @@ -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 { LatestFunctionConfigUI } from '../../../../../../../common/types/transform'; + +import { latestConfigMapper, validateLatestConfig } from './use_latest_function_config'; + +describe('useLatestFunctionConfig', () => { + it('should return a valid configuration', () => { + const config: LatestFunctionConfigUI = { + unique_key: [{ label: 'the-unique-key-label', value: 'the-unique-key' }], + sort: { label: 'the-sort-label', value: 'the-sort' }, + }; + + const apiConfig = latestConfigMapper.toAPIConfig(config); + + expect(apiConfig).toEqual({ + unique_key: ['the-unique-key'], + sort: 'the-sort', + }); + expect(validateLatestConfig(apiConfig).isValid).toBe(true); + }); + + it('should return an invalid partial configuration', () => { + const config: LatestFunctionConfigUI = { + unique_key: [{ label: 'the-unique-key-label', value: 'the-unique-key' }], + sort: { label: 'the-sort-label', value: undefined }, + }; + + const apiConfig = latestConfigMapper.toAPIConfig(config); + + expect(apiConfig).toEqual({ + unique_key: ['the-unique-key'], + sort: '', + }); + expect(validateLatestConfig(apiConfig).isValid).toBe(false); + }); + + it('should return false for isValid if no configuration given', () => { + expect(validateLatestConfig().isValid).toBe(false); + }); +}); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_latest_function_config.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_latest_function_config.ts index 7df6b11dc27e..5c2d0cd1b104 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_latest_function_config.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/hooks/use_latest_function_config.ts @@ -18,14 +18,10 @@ import { useAppDependencies } from '../../../../../app_dependencies'; * Latest function config mapper between API and UI */ export const latestConfigMapper = { - toAPIConfig(uiConfig: LatestFunctionConfigUI): LatestFunctionConfig | undefined { - if (uiConfig.sort === undefined || !uiConfig.unique_key?.length) { - return; - } - + toAPIConfig(uiConfig: LatestFunctionConfigUI): LatestFunctionConfig { return { - unique_key: uiConfig.unique_key.map((v) => v.value!), - sort: uiConfig.sort.value!, + unique_key: uiConfig.unique_key?.length ? uiConfig.unique_key.map((v) => v.value!) : [], + sort: uiConfig.sort?.value !== undefined ? uiConfig.sort.value! : '', }; }, toUIConfig() {}, @@ -56,7 +52,8 @@ function getOptions( })); const sortFieldOptions: Array> = indexPattern.fields - .filter((v) => !ignoreFieldNames.has(v.name) && v.sortable) + // The backend API for `latest` allows all field types for sort but the UI will be limited to `date`. + .filter((v) => !ignoreFieldNames.has(v.name) && v.sortable && v.type === 'date') .map((v) => ({ label: v.displayName, value: v.name, @@ -69,7 +66,8 @@ function getOptions( * Validates latest function configuration */ export function validateLatestConfig(config?: LatestFunctionConfig) { - const isValid: boolean = !!config?.unique_key?.length && config?.sort !== undefined; + const isValid: boolean = + !!config?.unique_key?.length && typeof config?.sort === 'string' && config?.sort.length > 0; return { isValid, ...(isValid diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/latest_function_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/latest_function_form.tsx index b4d035940192..0a64b6803f19 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/latest_function_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/latest_function_form.tsx @@ -7,14 +7,20 @@ import React, { FC } from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiComboBox, EuiFormRow } from '@elastic/eui'; +import { EuiButtonIcon, EuiCallOut, EuiComboBox, EuiCopy, EuiFormRow } from '@elastic/eui'; import { LatestFunctionService } from './hooks/use_latest_function_config'; interface LatestFunctionFormProps { + copyToClipboard: string; + copyToClipboardDescription: string; latestFunctionService: LatestFunctionService; } -export const LatestFunctionForm: FC = ({ latestFunctionService }) => { +export const LatestFunctionForm: FC = ({ + copyToClipboard, + copyToClipboardDescription, + latestFunctionService, +}) => { return ( <> = ({ latestFunction defaultMessage="Sort field" /> } + helpText={ + latestFunctionService.sortFieldOptions.length > 0 + ? i18n.translate('xpack.transform.stepDefineForm.sortHelpText', { + defaultMessage: 'Select the date field to be used to identify the latest document.', + }) + : undefined + } > - { - latestFunctionService.updateLatestFunctionConfig({ - sort: { value: selected[0].value, label: selected[0].label as string }, - }); - }} - isClearable={false} - data-test-subj="transformWizardSortFieldSelector" - /> + <> + {latestFunctionService.sortFieldOptions.length > 0 && ( + { + latestFunctionService.updateLatestFunctionConfig({ + sort: { value: selected[0].value, label: selected[0].label as string }, + }); + }} + isClearable={false} + data-test-subj="transformWizardSortFieldSelector" + /> + )} + {latestFunctionService.sortFieldOptions.length === 0 && ( + +

+ {' '} + + {(copy: () => void) => ( + + )} + +

+
+ )} +
); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx index d5c83357f4db..743572632b5a 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx @@ -104,12 +104,6 @@ export const StepDefineForm: FC = React.memo((props) => { : stepDefineForm.latestFunctionConfig.requestPayload ); - const pivotPreviewProps = { - ...usePivotData(indexPattern.title, pivotQuery, validationStatus, requestPayload), - dataTestSubj: 'transformPivotPreview', - toastNotifications, - }; - const copyToClipboardSource = getIndexDevConsoleStatement(pivotQuery, indexPattern.title); const copyToClipboardSourceDescription = i18n.translate( 'xpack.transform.indexPreview.copyClipboardTooltip', @@ -122,10 +116,25 @@ export const StepDefineForm: FC = React.memo((props) => { const copyToClipboardPivotDescription = i18n.translate( 'xpack.transform.pivotPreview.copyClipboardTooltip', { - defaultMessage: 'Copy Dev Console statement of the pivot preview to the clipboard.', + defaultMessage: 'Copy Dev Console statement of the transform preview to the clipboard.', } ); + const pivotPreviewProps = { + ...usePivotData(indexPattern.title, pivotQuery, validationStatus, requestPayload), + dataTestSubj: 'transformPivotPreview', + title: i18n.translate('xpack.transform.pivotPreview.transformPreviewTitle', { + defaultMessage: 'Transform preview', + }), + toastNotifications, + ...(stepDefineForm.transformFunction === TRANSFORM_FUNCTION.LATEST + ? { + copyToClipboard: copyToClipboardPivot, + copyToClipboardDescription: copyToClipboardPivotDescription, + } + : {}), + }; + const applySourceChangesHandler = () => { const sourceConfig = JSON.parse(advancedEditorSourceConfig); stepDefineForm.searchBar.actions.setSearchQuery(sourceConfig); @@ -377,12 +386,21 @@ export const StepDefineForm: FC = React.memo((props) => {
) : null} {stepDefineForm.transformFunction === TRANSFORM_FUNCTION.LATEST ? ( - + ) : null} - - + {(stepDefineForm.transformFunction !== TRANSFORM_FUNCTION.LATEST || + stepDefineForm.latestFunctionConfig.sortFieldOptions.length > 0) && ( + <> + + + + )} ); }); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx index 4d2cb0e71a02..17deaa58ccb7 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx @@ -187,7 +187,8 @@ export const StepDefineSummary: FC = ({ copyToClipboardDescription={i18n.translate( 'xpack.transform.pivotPreview.copyClipboardTooltip', { - defaultMessage: 'Copy Dev Console statement of the pivot preview to the clipboard.', + defaultMessage: + 'Copy Dev Console statement of the transform preview to the clipboard.', } )} dataTestSubj="transformPivotPreview" diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx index b72d70295b10..97af8135f389 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx @@ -485,7 +485,7 @@ export const StepDetailsForm: FC = React.memo( setCreateIndexPattern(!createIndexPattern)} @@ -528,7 +528,7 @@ export const StepDetailsForm: FC = React.memo( label={i18n.translate( 'xpack.transform.stepDetailsForm.continuousModeDateFieldLabel', { - defaultMessage: 'Date field', + defaultMessage: 'Date field for continuous mode', } )} helpText={i18n.translate( diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_time_field.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_time_field.tsx index 8ee2093a1e80..4af92c2147aa 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_time_field.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_time_field.tsx @@ -23,7 +23,7 @@ export const StepDetailsTimeField: FC = ({ const noTimeFieldLabel = i18n.translate( 'xpack.transform.stepDetailsForm.noTimeFieldOptionLabel', { - defaultMessage: "I don't want to use the time filter", + defaultMessage: "I don't want to use the time field option", } ); @@ -43,7 +43,7 @@ export const StepDetailsTimeField: FC = ({ label={ } helpText={ diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 6dd72d179b21..1bbf4b803375 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3184,8 +3184,6 @@ "savedObjectsManagement.importSummary.createdOutcomeLabel": "作成済み", "savedObjectsManagement.importSummary.errorCountHeader": "{errorCount}件のエラー", "savedObjectsManagement.importSummary.errorOutcomeLabel": "{errorMessage}", - "savedObjectsManagement.importSummary.headerLabelPlural": "{importCount}個のオブジェクトがインポートされました", - "savedObjectsManagement.importSummary.headerLabelSingular": "1個のオブジェクトがインポートされました", "savedObjectsManagement.importSummary.overwrittenCountHeader": "{overwrittenCount}件上書きされました", "savedObjectsManagement.importSummary.overwrittenOutcomeLabel": "上書き", "savedObjectsManagement.indexPattern.confirmOverwriteButton": "上書き", @@ -7210,8 +7208,6 @@ "xpack.enterpriseSearch.appSearch.documents.search.indexingGuide": "インデックスガイドをお読みください", "xpack.enterpriseSearch.appSearch.documents.search.noResults": "「{resultSearchTerm}」の結果がありません。", "xpack.enterpriseSearch.appSearch.documents.search.placeholder": "ドキュメントのフィルター...", - "xpack.enterpriseSearch.appSearch.documents.search.recentlyUploadedAsc": "最近アップロードされたドキュメント(昇順)", - "xpack.enterpriseSearch.appSearch.documents.search.recentlyUploadedDesc": "最近アップロードされたドキュメント(降順)", "xpack.enterpriseSearch.appSearch.documents.search.resultsPerPage.ariaLabel": "1 ページに表示する結果数", "xpack.enterpriseSearch.appSearch.documents.search.resultsPerPage.show": "表示:", "xpack.enterpriseSearch.appSearch.documents.search.sortBy": "並べ替え基準", @@ -11404,7 +11400,6 @@ "xpack.lens.indexPattern.ranges.decreaseButtonLabel": "粒度を下げる", "xpack.lens.indexPattern.ranges.deleteRange": "範囲を削除", "xpack.lens.indexPattern.ranges.granularity": "間隔粒度", - "xpack.lens.indexPattern.ranges.granularityDescription": "フィールドを均等な間隔に分割します。", "xpack.lens.indexPattern.ranges.increaseButtonLabel": "粒度を上げる", "xpack.lens.indexPattern.ranges.lessThanOrEqualAppend": "≤", "xpack.lens.indexPattern.ranges.lessThanOrEqualTooltip": "以下", @@ -13127,18 +13122,6 @@ "xpack.ml.fieldDataCard.cardDate.summaryTableTitle": "まとめ", "xpack.ml.fieldDataCard.cardIp.topValuesLabel": "トップの値", "xpack.ml.fieldDataCard.cardKeyword.topValuesLabel": "トップの値", - "xpack.ml.fieldDataCard.cardNumber.details.distributionOfValuesLabel": "分布", - "xpack.ml.fieldDataCard.cardNumber.details.topValuesLabel": "トップの値", - "xpack.ml.fieldDataCard.cardNumber.displayingPercentilesLabel": "{minPercent} - {maxPercent} パーセンタイルを表示中", - "xpack.ml.fieldDataCard.cardNumber.distinctCountDescription": "{cardinality} 個の特徴的な {cardinality, plural, other {値}}", - "xpack.ml.fieldDataCard.cardNumber.documentsCountDescription": "{count, plural, other {# 個のドキュメント}} ({docsPercent}%)", - "xpack.ml.fieldDataCard.cardNumber.maxLabel": "最高", - "xpack.ml.fieldDataCard.cardNumber.medianLabel": "中間", - "xpack.ml.fieldDataCard.cardNumber.minLabel": "分", - "xpack.ml.fieldDataCard.cardNumber.selectMetricDetailsDisplayAriaLabel": "メトリック詳細の表示オプションを選択してください", - "xpack.ml.fieldDataCard.cardOther.cardTypeLabel": "{cardType} タイプ", - "xpack.ml.fieldDataCard.cardOther.distinctCountDescription": "{cardinality} 個の特徴的な {cardinality, plural, other {値}}", - "xpack.ml.fieldDataCard.cardOther.documentsCountDescription": "{count, plural, other {# 個のドキュメント}} ({docsPercent}%)", "xpack.ml.fieldDataCard.cardText.fieldMayBePopulatedDescription": "たとえば、ドキュメントマッピングで {copyToParam} パラメーターを使ったり、{includesParam} と {excludesParam} パラメーターを使用してインデックスした後に {sourceParam} フィールドから切り取ったりして入力される場合があります。", "xpack.ml.fieldDataCard.cardText.fieldNotPresentDescription": "このフィールドはクエリが実行されたドキュメントの {sourceParam} フィールドにありませんでした。", "xpack.ml.fieldDataCard.cardText.noExamplesForFieldsTitle": "このフィールドの例が取得されませんでした", @@ -13222,13 +13205,9 @@ "xpack.ml.fileDatavisualizer.explanationFlyout.closeButton": "閉じる", "xpack.ml.fileDatavisualizer.explanationFlyout.content": "分析結果を生成した論理ステップ。", "xpack.ml.fileDatavisualizer.explanationFlyout.title": "分析説明", - "xpack.ml.fileDatavisualizer.fieldStatsCard.distinctCountDescription": "{fieldCardinality} 個の特徴的な {fieldCardinality, plural, other {値}}", - "xpack.ml.fileDatavisualizer.fieldStatsCard.documentsCountDescription": "{fieldCount, plural, other {# 個のドキュメント}} ({fieldPercent}%)", "xpack.ml.fileDatavisualizer.fieldStatsCard.maxTitle": "最高", "xpack.ml.fileDatavisualizer.fieldStatsCard.medianTitle": "中間", "xpack.ml.fileDatavisualizer.fieldStatsCard.minTitle": "分", - "xpack.ml.fileDatavisualizer.fieldStatsCard.noFieldInformationAvailableDescription": "フィールド情報がありません", - "xpack.ml.fileDatavisualizer.fieldStatsCard.topStatsValuesDescription": "トップの値", "xpack.ml.fileDatavisualizer.fileBeatConfig.paths": "ファイルのパスをここに追加してください", "xpack.ml.fileDatavisualizer.fileBeatConfigFlyout.closeButton": "閉じる", "xpack.ml.fileDatavisualizer.fileBeatConfigFlyout.copyButton": "クリップボードにコピー", @@ -13315,7 +13294,6 @@ "xpack.ml.fileDatavisualizer.resultsLinks.openInDataVisualizerTitle": "データビジュアライザーを開く", "xpack.ml.fileDatavisualizer.resultsLinks.viewIndexInDiscoverTitle": "インデックスをディスカバリで表示", "xpack.ml.fileDatavisualizer.resultsView.analysisExplanationButtonLabel": "分析説明", - "xpack.ml.fileDatavisualizer.resultsView.fileStatsTabName": "ファイル統計", "xpack.ml.fileDatavisualizer.resultsView.overrideSettingsButtonLabel": "上書き設定", "xpack.ml.fileDatavisualizer.simpleImportSettings.createIndexPatternLabel": "インデックスパターンを作成", "xpack.ml.fileDatavisualizer.simpleImportSettings.indexNameAriaLabel": "インデックス名、必須フィールド", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3ab1c7ee56a3..51205a3420be 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3188,8 +3188,6 @@ "savedObjectsManagement.importSummary.createdOutcomeLabel": "已创建", "savedObjectsManagement.importSummary.errorCountHeader": "{errorCount} 个错误", "savedObjectsManagement.importSummary.errorOutcomeLabel": "{errorMessage}", - "savedObjectsManagement.importSummary.headerLabelPlural": "{importCount} 个对象已导入", - "savedObjectsManagement.importSummary.headerLabelSingular": "1 个对象已导入", "savedObjectsManagement.importSummary.overwrittenCountHeader": "{overwrittenCount} 个已覆盖", "savedObjectsManagement.importSummary.overwrittenOutcomeLabel": "已覆盖", "savedObjectsManagement.indexPattern.confirmOverwriteButton": "覆盖", @@ -7229,8 +7227,6 @@ "xpack.enterpriseSearch.appSearch.documents.search.indexingGuide": "请阅读索引指南", "xpack.enterpriseSearch.appSearch.documents.search.noResults": "还没有匹配“{resultSearchTerm}”的结果!", "xpack.enterpriseSearch.appSearch.documents.search.placeholder": "筛选文档......", - "xpack.enterpriseSearch.appSearch.documents.search.recentlyUploadedAsc": "最近上传(升序)", - "xpack.enterpriseSearch.appSearch.documents.search.recentlyUploadedDesc": "最近上传(降序)", "xpack.enterpriseSearch.appSearch.documents.search.resultsPerPage.ariaLabel": "每页要显示的结果数", "xpack.enterpriseSearch.appSearch.documents.search.resultsPerPage.show": "显示:", "xpack.enterpriseSearch.appSearch.documents.search.sortBy": "排序依据", @@ -11433,7 +11429,6 @@ "xpack.lens.indexPattern.ranges.decreaseButtonLabel": "减小粒度", "xpack.lens.indexPattern.ranges.deleteRange": "删除范围", "xpack.lens.indexPattern.ranges.granularity": "时间间隔粒度", - "xpack.lens.indexPattern.ranges.granularityDescription": "将字段分成间隔均匀的时间间隔。", "xpack.lens.indexPattern.ranges.increaseButtonLabel": "增加粒度", "xpack.lens.indexPattern.ranges.lessThanOrEqualAppend": "≤", "xpack.lens.indexPattern.ranges.lessThanOrEqualTooltip": "小于或等于", @@ -13158,18 +13153,6 @@ "xpack.ml.fieldDataCard.cardDate.summaryTableTitle": "摘要", "xpack.ml.fieldDataCard.cardIp.topValuesLabel": "排名最前值", "xpack.ml.fieldDataCard.cardKeyword.topValuesLabel": "排名最前值", - "xpack.ml.fieldDataCard.cardNumber.details.distributionOfValuesLabel": "分布", - "xpack.ml.fieldDataCard.cardNumber.details.topValuesLabel": "排名最前值", - "xpack.ml.fieldDataCard.cardNumber.displayingPercentilesLabel": "显示 {minPercent} - {maxPercent} 百分位数", - "xpack.ml.fieldDataCard.cardNumber.distinctCountDescription": "{cardinality} 个不同的 {cardinality, plural, other {值}}", - "xpack.ml.fieldDataCard.cardNumber.documentsCountDescription": "{count, plural, other {# 个文档}} ({docsPercent}%)", - "xpack.ml.fieldDataCard.cardNumber.maxLabel": "最大值", - "xpack.ml.fieldDataCard.cardNumber.medianLabel": "中值", - "xpack.ml.fieldDataCard.cardNumber.minLabel": "最小值", - "xpack.ml.fieldDataCard.cardNumber.selectMetricDetailsDisplayAriaLabel": "选择指标详情的显示选项", - "xpack.ml.fieldDataCard.cardOther.cardTypeLabel": "{cardType} 类型", - "xpack.ml.fieldDataCard.cardOther.distinctCountDescription": "{cardinality} 个不同的 {cardinality, plural, other {值}}", - "xpack.ml.fieldDataCard.cardOther.documentsCountDescription": "{count, plural, other {# 个文档}} ({docsPercent}%)", "xpack.ml.fieldDataCard.cardText.fieldMayBePopulatedDescription": "例如,可以使用文档映射中的 {copyToParam} 参数进行填充,也可以在索引后通过使用 {includesParam} 和 {excludesParam} 参数从 {sourceParam} 字段中修剪。", "xpack.ml.fieldDataCard.cardText.fieldNotPresentDescription": "查询的文档的 {sourceParam} 字段中不存在此字段。", "xpack.ml.fieldDataCard.cardText.noExamplesForFieldsTitle": "没有获取此字段的示例", @@ -13253,13 +13236,9 @@ "xpack.ml.fileDatavisualizer.explanationFlyout.closeButton": "关闭", "xpack.ml.fileDatavisualizer.explanationFlyout.content": "产生分析结果的逻辑步骤。", "xpack.ml.fileDatavisualizer.explanationFlyout.title": "分析说明", - "xpack.ml.fileDatavisualizer.fieldStatsCard.distinctCountDescription": "{fieldCardinality} 个不同的{fieldCardinality, plural, other {值}}", - "xpack.ml.fileDatavisualizer.fieldStatsCard.documentsCountDescription": "{fieldCount, plural, other {# 个文档}} ({fieldPercent}%)", "xpack.ml.fileDatavisualizer.fieldStatsCard.maxTitle": "最大值", "xpack.ml.fileDatavisualizer.fieldStatsCard.medianTitle": "中值", "xpack.ml.fileDatavisualizer.fieldStatsCard.minTitle": "最小值", - "xpack.ml.fileDatavisualizer.fieldStatsCard.noFieldInformationAvailableDescription": "没有可用的字段信息", - "xpack.ml.fileDatavisualizer.fieldStatsCard.topStatsValuesDescription": "排在前面的值", "xpack.ml.fileDatavisualizer.fileBeatConfig.paths": "在此处将路径添加您的文件中", "xpack.ml.fileDatavisualizer.fileBeatConfigFlyout.closeButton": "关闭", "xpack.ml.fileDatavisualizer.fileBeatConfigFlyout.copyButton": "复制到剪贴板", @@ -13347,7 +13326,6 @@ "xpack.ml.fileDatavisualizer.resultsLinks.openInDataVisualizerTitle": "在数据可视化工具中打开", "xpack.ml.fileDatavisualizer.resultsLinks.viewIndexInDiscoverTitle": "在 Discover 中查看索引", "xpack.ml.fileDatavisualizer.resultsView.analysisExplanationButtonLabel": "分析说明", - "xpack.ml.fileDatavisualizer.resultsView.fileStatsTabName": "文件统计", "xpack.ml.fileDatavisualizer.resultsView.overrideSettingsButtonLabel": "替代设置", "xpack.ml.fileDatavisualizer.simpleImportSettings.createIndexPatternLabel": "创建索引模式", "xpack.ml.fileDatavisualizer.simpleImportSettings.indexNameAriaLabel": "索引名称,必填字段", diff --git a/x-pack/plugins/triggers_actions_ui/tsconfig.json b/x-pack/plugins/triggers_actions_ui/tsconfig.json new file mode 100644 index 000000000000..715ed6848d9b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/tsconfig.json @@ -0,0 +1,29 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "server/**/*", + "public/**/*", + "common/**/*", + "config.ts", + "../../../typings/**/*" + ], + "references": [ + { "path": "../../../src/core/tsconfig.json" }, + { "path": "../alerts/tsconfig.json" }, + { "path": "../features/tsconfig.json" }, + { "path": "../../../src/plugins/data/tsconfig.json" }, + { "path": "../../../src/plugins/saved_objects/tsconfig.json" }, + { "path": "../../../src/plugins/home/tsconfig.json" }, + { "path": "../../../src/plugins/charts/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, + { "path": "../../../src/plugins/management/tsconfig.json" }, + ] +} diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts index a58927dfbd12..5c0b36874004 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.test.ts @@ -5,7 +5,8 @@ */ import { colourPalette, getSeriesAndDomain } from './data_formatting'; -import { NetworkItems } from './types'; +import { NetworkItems, MimeType } from './types'; +import { WaterfallDataEntry } from '../../waterfall/types'; describe('Palettes', () => { it('A colour palette comprising timing and mime type colours is correctly generated', () => { @@ -136,6 +137,31 @@ describe('getSeriesAndDomain', () => { }, ]; + const networkItemsWithUncommonMimeType: NetworkItems = [ + { + timestamp: '2021-01-05T19:22:28.928Z', + method: 'GET', + url: 'https://unpkg.com/director@1.2.8/build/director.js', + status: 200, + mimeType: 'application/x-javascript', + requestSentTime: 18098833.537, + requestStartTime: 18098837.233999997, + loadEndTime: 18098977.648000002, + timings: { + blocked: 84.54599999822676, + receive: 3.068000001803739, + queueing: 3.69700000010198, + proxy: -1, + total: 144.1110000014305, + wait: 52.56100000042352, + connect: -1, + send: 0.2390000008745119, + ssl: -1, + dns: -1, + }, + }, + ]; + it('formats timings', () => { const actual = getSeriesAndDomain(networkItems); expect(actual).toMatchInlineSnapshot(` @@ -456,4 +482,22 @@ describe('getSeriesAndDomain', () => { } `); }); + + it('handles formatting when mime type is not mapped to a specific mime type bucket', () => { + const actual = getSeriesAndDomain(networkItemsWithUncommonMimeType); + const { series } = actual; + /* verify that raw mime type appears in the tooltip config and that + * the colour is mapped to mime type other */ + const contentDownloadedingConfigItem = series.find((item: WaterfallDataEntry) => { + const { tooltipProps } = item.config; + if (tooltipProps && typeof tooltipProps.value === 'string') { + return ( + tooltipProps.value.includes('application/x-javascript') && + tooltipProps.colour === colourPalette[MimeType.Other] + ); + } + return false; + }); + expect(contentDownloadedingConfigItem).toBeDefined(); + }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts index 43fa93fa5f6f..5e59026fd65f 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/data_formatting.ts @@ -50,7 +50,7 @@ const getFriendlyTooltipValue = ({ let label = FriendlyTimingLabels[timing]; if (timing === Timings.Receive && mimeType) { const formattedMimeType: MimeType = MimeTypesMap[mimeType]; - label += ` (${FriendlyMimetypeLabels[formattedMimeType]})`; + label += ` (${FriendlyMimetypeLabels[formattedMimeType] || mimeType})`; } return `${label}: ${formatValueForDisplay(value)}ms`; }; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/types.ts b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/types.ts index 738929741dda..137c0767a83e 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/types.ts +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/types.ts @@ -111,6 +111,7 @@ export const FriendlyMimetypeLabels = { export const MimeTypesMap: Record = { 'text/html': MimeType.Html, 'application/javascript': MimeType.Script, + 'application/json': MimeType.Script, 'text/javascript': MimeType.Script, 'text/css': MimeType.Stylesheet, // Images @@ -130,6 +131,7 @@ export const MimeTypesMap: Record = { 'audio/x-pn-wav': MimeType.Media, 'audio/webm': MimeType.Media, 'video/webm': MimeType.Media, + 'video/mp4': MimeType.Media, 'audio/ogg': MimeType.Media, 'video/ogg': MimeType.Media, 'application/ogg': MimeType.Media, diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx index 3ebd82860446..21b1a067a4e9 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.test.tsx @@ -6,9 +6,18 @@ import React from 'react'; import { shallowWithIntl } from '@kbn/test/jest'; +import { fireEvent, waitFor } from '@testing-library/react'; import { FiltersExpressionsSelect } from './filters_expression_select'; +import { render } from '../../../../lib/helper/rtl_helpers'; +import { filterAriaLabels as aria } from './translations'; +import { filterLabels } from '../../filter_group/translations'; + +describe('FiltersExpressionSelect', () => { + const LOCATION_FIELD_NAME = 'observer.geo.name'; + const PORT_FIELD_NAME = 'url.port'; + const SCHEME_FIELD_NAME = 'monitor.type'; + const TAG_FIELD_NAME = 'tags'; -describe('filters expression select component', () => { it('is empty when no filters available', () => { const component = shallowWithIntl( { `); }); - it('contains provided new filter values', () => { - const component = shallowWithIntl( + it.each([ + [[LOCATION_FIELD_NAME], [aria.LOCATION], [aria.TAG, aria.PORT, aria.SCHEME]], + [ + [LOCATION_FIELD_NAME, TAG_FIELD_NAME], + [aria.LOCATION, aria.TAG], + [aria.PORT, aria.SCHEME], + ], + [ + [PORT_FIELD_NAME, SCHEME_FIELD_NAME], + [aria.PORT, aria.SCHEME], + [aria.LOCATION, aria.TAG], + ], + [[TAG_FIELD_NAME], [aria.TAG], [aria.LOCATION, aria.PORT, aria.SCHEME]], + ])('contains provided new filter values', (newFilters, expectedLabels, absentLabels) => { + const { getByLabelText, queryByLabelText } = render( { shouldUpdateUrl={false} /> ); - expect(component).toMatchInlineSnapshot(` - - - - - } - disabled={true} - fieldName="observer.geo.name" - forceOpen={false} - id="filter_location" - items={Array []} - loading={false} - onFilterFieldChange={[Function]} - selectedItems={Array []} - setForceOpen={[Function]} - title="Scheme" - /> - - - - - - - - - `); + expectedLabels.forEach((label) => expect(getByLabelText(label))); + absentLabels.forEach((label) => expect(queryByLabelText(label)).toBeNull()); }); - it('contains provided selected filter values', () => { - const component = shallowWithIntl( + it.each([ + ['Remove filter Location', LOCATION_FIELD_NAME], + ['Remove filter Scheme', SCHEME_FIELD_NAME], + ['Remove filter Port', PORT_FIELD_NAME], + ['Remove filter Tag', TAG_FIELD_NAME], + ])('fires remove filter handler', async (removeButtonLabel, expectedFieldName) => { + const onRemoveFilterMock = jest.fn(); + const setAlertParamsMock = jest.fn(); + const { getByLabelText } = render( ); - expect(component).toMatchInlineSnapshot(` - - - - - } - disabled={false} - fieldName="tags" - forceOpen={false} - id="filter_tags" - items={ - Array [ - "foo", - "bar", - ] - } - loading={false} - onFilterFieldChange={[Function]} - selectedItems={Array []} - setForceOpen={[Function]} - title="Tags" - /> - - - - - - - - - `); + + const removeButton = getByLabelText(removeButtonLabel); + fireEvent.click(removeButton); + expect(onRemoveFilterMock).toHaveBeenCalledTimes(1); + expect(onRemoveFilterMock).toHaveBeenCalledWith(expectedFieldName); + expect(setAlertParamsMock).toHaveBeenCalledTimes(1); + expect(setAlertParamsMock).toHaveBeenCalledWith('filters', { + [SCHEME_FIELD_NAME]: [], + [LOCATION_FIELD_NAME]: [], + [TAG_FIELD_NAME]: [], + [PORT_FIELD_NAME]: [], + }); }); + + const TEST_TAGS = ['foo', 'bar']; + const TEST_PORTS = [5601, 9200]; + const TEST_SCHEMES = ['http', 'tcp']; + const TEST_LOCATIONS = ['nyc', 'fairbanks']; + + it.each([ + [ + { + tags: TEST_TAGS, + ports: [5601, 9200], + schemes: ['http', 'tcp'], + locations: ['nyc', 'fairbanks'], + }, + [TAG_FIELD_NAME], + aria.TAG, + filterLabels.TAG, + TEST_TAGS, + ], + [ + { + tags: [], + ports: TEST_PORTS, + schemes: [], + locations: [], + }, + [PORT_FIELD_NAME], + aria.PORT, + filterLabels.PORT, + TEST_PORTS, + ], + [ + { + tags: [], + ports: [], + schemes: TEST_SCHEMES, + locations: [], + }, + [SCHEME_FIELD_NAME], + aria.SCHEME, + filterLabels.SCHEME, + TEST_SCHEMES, + ], + [ + { + tags: [], + ports: [], + schemes: [], + locations: TEST_LOCATIONS, + }, + [LOCATION_FIELD_NAME], + aria.LOCATION, + filterLabels.LOCATION, + TEST_LOCATIONS, + ], + ])( + 'applies accessible label to filter expressions, and contains selected filters', + /** + * @param filters the set of filters the test should supply as props + * @param newFilters the set of filter item types the component should render + * @param expectedFilterButtonAriaLabel the aria label for the popover button for the targeted filter + * @param filterLabel the name of the filter label expected in each item's aria-label + * @param expectedFilterItems the set of filter options the component should render + */ + async ( + filters, + newFilters, + expectedFilterButtonAriaLabel, + filterLabel, + expectedFilterItems + ) => { + const { getByLabelText } = render( + + ); + + const filterButton = getByLabelText(expectedFilterButtonAriaLabel); + + fireEvent.click(filterButton); + + await waitFor(() => { + expectedFilterItems.forEach((filterItem: string | number) => + expect(getByLabelText(`Filter by ${filterLabel} ${filterItem}.`)) + ); + }); + } + ); }); diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx index 64862a8b748d..63dcf31fd811 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/filters_expression_select.tsx @@ -8,7 +8,7 @@ import React, { useState } from 'react'; import { EuiButtonIcon, EuiExpression, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { FilterPopover } from '../../filter_group/filter_popover'; import { filterLabels } from '../../filter_group/translations'; -import { alertFilterLabels } from './translations'; +import { alertFilterLabels, filterAriaLabels } from './translations'; import { FilterExpressionsSelectProps } from './filters_expression_select_container'; import { OverviewFiltersState } from '../../../../state/reducers/overview_filters'; @@ -58,6 +58,7 @@ export const FiltersExpressionsSelect: React.FC = ({ const monitorFilters = [ { + 'aria-label': filterAriaLabels.PORT, onFilterFieldChange, loading: false, fieldName: 'url.port', @@ -71,6 +72,7 @@ export const FiltersExpressionsSelect: React.FC = ({ value: selectedPorts.length === 0 ? alertFilterLabels.ANY_PORT : selectedPorts?.join(','), }, { + 'aria-label': filterAriaLabels.TAG, onFilterFieldChange, loading: false, fieldName: 'tags', @@ -78,11 +80,12 @@ export const FiltersExpressionsSelect: React.FC = ({ disabled: tags?.length === 0, items: tags ?? [], selectedItems: selectedTags, - title: filterLabels.TAGS, + title: filterLabels.TAG, description: selectedTags.length === 0 ? alertFilterLabels.WITH : alertFilterLabels.WITH_TAG, value: selectedTags.length === 0 ? alertFilterLabels.ANY_TAG : selectedTags?.join(','), }, { + 'aria-label': filterAriaLabels.SCHEME, onFilterFieldChange, loading: false, fieldName: 'monitor.type', @@ -95,6 +98,7 @@ export const FiltersExpressionsSelect: React.FC = ({ value: selectedSchemes.length === 0 ? alertFilterLabels.ANY_TYPE : selectedSchemes?.join(','), }, { + 'aria-label': filterAriaLabels.LOCATION, onFilterFieldChange, loading: false, fieldName: 'observer.geo.name', @@ -102,7 +106,7 @@ export const FiltersExpressionsSelect: React.FC = ({ disabled: locations?.length === 0, items: locations ?? [], selectedItems: selectedLocations, - title: filterLabels.SCHEME, + title: filterLabels.LOCATION, description: selectedLocations.length === 0 ? alertFilterLabels.FROM : alertFilterLabels.FROM_LOCATION, value: @@ -132,7 +136,7 @@ export const FiltersExpressionsSelect: React.FC = ({ {...item} btnContent={ = ({
{ diff --git a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/translations.ts b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/translations.ts index 64c082dc5133..919095d411fa 100644 --- a/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/translations.ts +++ b/x-pack/plugins/uptime/public/components/overview/alerts/monitor_expressions/translations.ts @@ -54,6 +54,12 @@ export const alertFilterLabels = { ANY_LOCATION: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.anyLocation', { defaultMessage: 'any location', }), + + REMOVE_FILTER_LABEL: (title: string) => + i18n.translate('xpack.uptime.alerts.monitorExpression.label', { + defaultMessage: 'Remove filter {title}', + values: { title }, + }), }; export const statusExpLabels = { @@ -82,3 +88,18 @@ export const timeExpLabels = { } ), }; + +export const filterAriaLabels = { + PORT: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.port.label', { + defaultMessage: `Select port filters to apply to the alert's query.`, + }), + TAG: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.tag.label', { + defaultMessage: `Select tag filters to apply to the alert's query.`, + }), + SCHEME: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.scheme.label', { + defaultMessage: `Select protocol scheme filters to apply to the alert's query.`, + }), + LOCATION: i18n.translate('xpack.uptime.alerts.monitorStatus.filters.location.label', { + defaultMessage: `Select location filters to apply to the alert's query.`, + }), +}; diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/__snapshots__/filter_popover.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/filter_group/__snapshots__/filter_popover.test.tsx.snap deleted file mode 100644 index 47efe8946d0b..000000000000 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/__snapshots__/filter_popover.test.tsx.snap +++ /dev/null @@ -1,129 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`FilterPopover component does not show item list when loading 1`] = ` - - } - closePopover={[Function]} - data-test-subj="filter-popover_test" - display="inlineBlock" - hasArrow={true} - id="test" - isOpen={false} - ownFocus={true} - panelPaddingSize="m" - zIndex={10000} -> - - - - -`; - -exports[`FilterPopover component renders without errors for valid props 1`] = ` - - } - closePopover={[Function]} - data-test-subj="filter-popover_test" - display="inlineBlock" - hasArrow={true} - id="test" - isOpen={false} - ownFocus={true} - panelPaddingSize="m" - zIndex={10000} -> - - - - -`; - -exports[`FilterPopover component returns selected items on popover close 1`] = ` -
-
- Some text -
-
-
- -
-
-
-`; diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.test.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.test.tsx new file mode 100644 index 000000000000..0fc413011fa5 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.test.tsx @@ -0,0 +1,73 @@ +/* + * 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 { fireEvent, waitFor } from '@testing-library/react'; +import { render } from '../../../lib/helper/rtl_helpers'; +import { FilterGroupComponent } from './filter_group'; + +describe('FilterGroupComponent', () => { + const overviewFilters = { + locations: ['nyc', 'fairbanks'], + ports: [5601, 9200], + schemes: ['http', 'tcp'], + tags: ['prod', 'dev'], + }; + it.each([ + ['expands filter group for Location filter', 'Search for location'], + ['expands filter group for Port filter', 'Search for port'], + ['expands filter group for Scheme filter', 'Search for scheme'], + ['expands filter group for Tag filter', 'Search for tag'], + ])('handles loading', async (popoverButtonLabel, searchInputLabel) => { + const { getByLabelText } = render( + + ); + + const popoverButton = getByLabelText(popoverButtonLabel); + fireEvent.click(popoverButton); + await waitFor(() => { + const searchInput = getByLabelText(searchInputLabel); + expect(searchInput).toHaveAttribute('placeholder', 'Loading...'); + }); + }); + + it.each([ + [ + 'expands filter group for Location filter', + 'Search for location', + ['Filter by Location nyc.', 'Filter by Location fairbanks.'], + ], + [ + 'expands filter group for Port filter', + 'Search for port', + ['Filter by Port 5601.', 'Filter by Port 9200.'], + ], + [ + 'expands filter group for Scheme filter', + 'Search for scheme', + ['Filter by Scheme http.', 'Filter by Scheme tcp.'], + ], + [ + 'expands filter group for Tag filter', + 'Search for tag', + ['Filter by Tag prod.', 'Filter by Tag dev.'], + ], + ])( + 'displays filter items when clicked', + async (popoverButtonLabel, searchInputLabel, filterItemButtonLabels) => { + const { getByLabelText } = render( + + ); + + const popoverButton = getByLabelText(popoverButtonLabel); + fireEvent.click(popoverButton); + await waitFor(() => { + expect(getByLabelText(searchInputLabel)); + filterItemButtonLabels.forEach((itemLabel) => expect(getByLabelText(itemLabel))); + }); + } + ); +}); diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx index a5256ee1e262..a4a03ec5587a 100644 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx +++ b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_group.tsx @@ -15,7 +15,7 @@ import { useFilterUpdate } from '../../../hooks/use_filter_update'; import { MONITOR_ROUTE } from '../../../../common/constants'; import { useSelectedFilters } from '../../../hooks/use_selected_filters'; -interface PresentationalComponentProps { +interface Props { loading: boolean; overviewFilters: OverviewFilters; } @@ -28,10 +28,7 @@ function isDisabled(array?: T[]) { return array ? array.length === 0 : true; } -export const FilterGroupComponent: React.FC = ({ - overviewFilters, - loading, -}) => { +export const FilterGroupComponent: React.FC = ({ overviewFilters, loading }) => { const { locations, ports, schemes, tags } = overviewFilters; const [updatedFieldValues, setUpdatedFieldValues] = useState<{ @@ -90,7 +87,7 @@ export const FilterGroupComponent: React.FC = ({ disabled: isDisabled(tags), items: tags ?? [], selectedItems: selectedTags, - title: filterLabels.TAGS, + title: filterLabels.TAG, }, ] : []), diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.test.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.test.tsx index d08d56dc2e2a..dc45077901c1 100644 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.test.tsx @@ -5,23 +5,22 @@ */ import React from 'react'; -import { shallowWithIntl, mountWithIntl } from '@kbn/test/jest'; +import { fireEvent, waitFor, waitForElementToBeRemoved } from '@testing-library/react'; import { FilterPopoverProps, FilterPopover } from './filter_popover'; -import { UptimeFilterButton } from './uptime_filter_button'; -import { EuiFilterSelectItem } from '@elastic/eui'; +import { render } from '../../../lib/helper/rtl_helpers'; describe('FilterPopover component', () => { let props: FilterPopoverProps; beforeEach(() => { props = { - fieldName: 'foo', + fieldName: 'test-fieldName', id: 'test', loading: false, items: ['first', 'second', 'third', 'fourth'], onFilterFieldChange: jest.fn(), selectedItems: ['first', 'third'], - title: 'bar', + title: 'test-title', }; }); @@ -29,43 +28,68 @@ describe('FilterPopover component', () => { jest.clearAllMocks(); }); - it('renders without errors for valid props', () => { - const wrapper = shallowWithIntl(); - expect(wrapper).toMatchSnapshot(); - }); - it('expands on button click', () => { - const wrapper = mountWithIntl(); + const { getByRole, getByLabelText, getByText, queryByLabelText, queryByText } = render( + + ); - // ensure the popover isn't open - expect(wrapper.find('EuiPopoverTitle')).toHaveLength(0); + const screenReaderOnlyText = 'You are in a dialog. To close this dialog, hit escape.'; - expect(wrapper.find(UptimeFilterButton)).toHaveLength(1); - wrapper.find(UptimeFilterButton).simulate('click'); + expect(queryByText(screenReaderOnlyText)).toBeNull(); + expect(queryByLabelText('Filter by bar fourth.')).toBeNull(); - // check that the popover is now open - expect(wrapper.find('EuiPopoverTitle')).toHaveLength(1); + fireEvent.click(getByRole('button')); + + expect(getByText(screenReaderOnlyText)); + expect(getByLabelText('Filter by test-title fourth.')); }); - it('does not show item list when loading', () => { + it('does not show item list when loading, and displays placeholder', async () => { props.loading = true; - const wrapper = shallowWithIntl(); - expect(wrapper).toMatchSnapshot(); - }); + const { getByRole, queryByText, getByLabelText } = render(); - it('returns selected items on popover close', () => { - const wrapper = mountWithIntl( -
-
Some text
- -
- ); - expect(wrapper.find(UptimeFilterButton)).toHaveLength(1); - wrapper.find(UptimeFilterButton).simulate('click'); - expect(wrapper.find(EuiFilterSelectItem)).toHaveLength(props.items.length); - wrapper.find(EuiFilterSelectItem).at(1).simulate('click'); - wrapper.find('#foo').simulate('click'); - const rendered = wrapper.render(); - expect(rendered).toMatchSnapshot(); + fireEvent.click(getByRole('button')); + + await waitFor(() => { + const search = getByLabelText('Search for test-title'); + expect(search).toHaveAttribute('placeholder', 'Loading...'); + }); + + expect(queryByText('Filter by test-title second.')).toBeNull(); }); + + it.each([ + [[], ['third'], ['third']], + [['first', 'third'], ['first'], ['third']], + [['fourth'], ['first', 'second'], ['first', 'second', 'fourth']], + ])( + 'returns selected items on popover close', + async (selectedPropsItems, expectedSelections, itemsToClick) => { + if (itemsToClick.length < 1) { + throw new Error('This test assumes at least one item will be clicked'); + } + props.selectedItems = selectedPropsItems; + + const { getByLabelText, queryByLabelText } = render(); + + const uptimeFilterButton = getByLabelText(`expands filter group for ${props.title} filter`); + + fireEvent.click(uptimeFilterButton); + + const generateLabelText = (item: string) => `Filter by ${props.title} ${item}.`; + + itemsToClick.forEach((item) => { + const optionButtonLabelText = generateLabelText(item); + const optionButton = getByLabelText(optionButtonLabelText); + fireEvent.click(optionButton); + }); + + fireEvent.click(uptimeFilterButton); + + await waitForElementToBeRemoved(() => queryByLabelText(generateLabelText(itemsToClick[0]))); + + expect(props.onFilterFieldChange).toHaveBeenCalledTimes(1); + expect(props.onFilterFieldChange).toHaveBeenCalledWith(props.fieldName, expectedSelections); + } + ); }); diff --git a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx index e79c036d54e0..d5b6efbf9ded 100644 --- a/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx +++ b/x-pack/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx @@ -76,8 +76,11 @@ export const FilterPopover = ({ numFilters={items.length} numActiveFilters={isOpen ? tempSelectedItems.length : selectedItems.length} onClick={() => { + if (isOpen) { + // only update these values on close + onFilterFieldChange(fieldName, tempSelectedItems); + } setIsOpen(!isOpen); - onFilterFieldChange(fieldName, tempSelectedItems); }} title={title} /> @@ -124,6 +127,10 @@ export const FilterPopover = ({ {!loading && itemsToDisplay.map((item) => ( ( = ( params: { @@ -29,7 +29,7 @@ export type UMSavedObjectsQueryFn = ( ) => Promise | T; export interface UptimeCoreSetup { - router: IRouter; + router: UptimeRouter; } export interface UptimeCorePlugins { diff --git a/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts b/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts index ed831f4e1736..3a6516bd33d4 100644 --- a/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts +++ b/x-pack/plugins/uptime/server/lib/alerts/status_check.test.ts @@ -18,7 +18,6 @@ import { AlertInstanceState, AlertInstanceContext, } from '../../../../alerts/server'; -import { IRouter } from 'kibana/server'; import { UMServerLibs } from '../lib'; import { UptimeCorePlugins, UptimeCoreSetup } from '../adapters'; import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../common/constants'; @@ -26,6 +25,7 @@ import { alertsMock, AlertServicesMock } from '../../../../alerts/server/mocks'; import { GetMonitorStatusResult } from '../requests/get_monitor_status'; import { makePing } from '../../../common/runtime_types/ping'; import { GetMonitorAvailabilityResult } from '../requests/get_monitor_availability'; +import type { UptimeRouter } from '../../types'; /** * The alert takes some dependencies as parameters; these are things like @@ -35,7 +35,7 @@ import { GetMonitorAvailabilityResult } from '../requests/get_monitor_availabili * so we don't have to mock them all for each test. */ const bootstrapDependencies = (customRequests?: any) => { - const router: IRouter = {} as IRouter; + const router = {} as UptimeRouter; // these server/libs parameters don't have any functionality, which is fine // because we aren't testing them here const server: UptimeCoreSetup = { router }; diff --git a/x-pack/plugins/uptime/server/rest_api/types.ts b/x-pack/plugins/uptime/server/rest_api/types.ts index 4e627cebb345..3d3f2393d242 100644 --- a/x-pack/plugins/uptime/server/rest_api/types.ts +++ b/x-pack/plugins/uptime/server/rest_api/types.ts @@ -10,12 +10,12 @@ import { RouteConfig, RouteMethod, SavedObjectsClientContract, - RequestHandlerContext, KibanaRequest, KibanaResponseFactory, IKibanaResponse, } from 'kibana/server'; import { UMServerLibs, UptimeESClient } from '../lib/lib'; +import type { UptimeRequestHandlerContext } from '../types'; /** * Defines the basic properties employed by Uptime routes. @@ -38,7 +38,9 @@ export type UMRouteDefinition = UMServerRoute & * provided by the Kibana platform. Route objects must conform to this type in order * to successfully interact with the Kibana platform. */ -export type UMKibanaRoute = UMRouteDefinition>; +export type UMKibanaRoute = UMRouteDefinition< + RequestHandler +>; /** * This is an abstraction over the default Kibana route type. This allows us to use custom @@ -68,7 +70,7 @@ export type UMRouteHandler = ({ savedObjectsClient, }: { uptimeEsClient: UptimeESClient; - context: RequestHandlerContext; + context: UptimeRequestHandlerContext; request: KibanaRequest, Record, Record>; response: KibanaResponseFactory; savedObjectsClient: SavedObjectsClientContract; diff --git a/x-pack/plugins/uptime/server/types.ts b/x-pack/plugins/uptime/server/types.ts new file mode 100644 index 000000000000..7a107268b2cf --- /dev/null +++ b/x-pack/plugins/uptime/server/types.ts @@ -0,0 +1,21 @@ +/* + * 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 type { IRouter, RequestHandlerContext } from 'src/core/server'; +import type { AlertingApiRequestHandlerContext } from '../../alerts/server'; +import type { LicensingApiRequestHandlerContext } from '../../licensing/server'; +/** + * @internal + */ +export interface UptimeRequestHandlerContext extends RequestHandlerContext { + licensing: LicensingApiRequestHandlerContext; + alerting: AlertingApiRequestHandlerContext; +} + +/** + * @internal + */ +export type UptimeRouter = IRouter; diff --git a/x-pack/plugins/watcher/server/index.ts b/x-pack/plugins/watcher/server/index.ts index 356be781fb19..51eb7bfa543f 100644 --- a/x-pack/plugins/watcher/server/index.ts +++ b/x-pack/plugins/watcher/server/index.ts @@ -6,6 +6,4 @@ import { PluginInitializerContext } from 'kibana/server'; import { WatcherServerPlugin } from './plugin'; -export { WatcherContext } from './plugin'; - export const plugin = (ctx: PluginInitializerContext) => new WatcherServerPlugin(ctx); diff --git a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts b/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts index 1b2476fc78b4..0d162f300d37 100644 --- a/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts +++ b/x-pack/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.ts @@ -4,20 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - KibanaRequest, - KibanaResponseFactory, - RequestHandler, - RequestHandlerContext, -} from 'kibana/server'; -import { RouteDependencies } from '../../types'; +import type { KibanaRequest, KibanaResponseFactory, RequestHandler } from 'kibana/server'; +import type { RouteDependencies, WatcherRequestHandlerContext } from '../../types'; -export const licensePreRoutingFactory = ( +export const licensePreRoutingFactory = ( { getLicenseStatus }: RouteDependencies, - handler: RequestHandler + handler: RequestHandler ) => { return function licenseCheck( - ctx: RequestHandlerContext, + ctx: Context, request: KibanaRequest, response: KibanaResponseFactory ) { diff --git a/x-pack/plugins/watcher/server/plugin.ts b/x-pack/plugins/watcher/server/plugin.ts index 9ff46283a72a..8ce63ab00977 100644 --- a/x-pack/plugins/watcher/server/plugin.ts +++ b/x-pack/plugins/watcher/server/plugin.ts @@ -3,23 +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. */ - -declare module 'kibana/server' { - interface RequestHandlerContext { - watcher?: WatcherContext; - } -} - import { CoreSetup, ILegacyCustomClusterClient, - ILegacyScopedClusterClient, Logger, Plugin, PluginInitializerContext, } from 'kibana/server'; import { PLUGIN, INDEX_NAMES } from '../common/constants'; -import { Dependencies, LicenseStatus, RouteDependencies } from './types'; +import type { + Dependencies, + LicenseStatus, + RouteDependencies, + WatcherRequestHandlerContext, +} from './types'; import { registerSettingsRoutes } from './routes/api/settings'; import { registerIndicesRoutes } from './routes/api/indices'; @@ -30,10 +27,6 @@ import { registerListFieldsRoute } from './routes/api/register_list_fields_route import { registerLoadHistoryRoute } from './routes/api/register_load_history_route'; import { elasticsearchJsPlugin } from './lib/elasticsearch_js_plugin'; -export interface WatcherContext { - client: ILegacyScopedClusterClient; -} - async function getCustomEsClient(getStartServices: CoreSetup['getStartServices']) { const [core] = await getStartServices(); const esConfig = { plugins: [elasticsearchJsPlugin] }; @@ -53,7 +46,7 @@ export class WatcherServerPlugin implements Plugin { } async setup({ http, getStartServices }: CoreSetup, { licensing, features }: Dependencies) { - const router = http.createRouter(); + const router = http.createRouter(); const routeDependencies: RouteDependencies = { router, getLicenseStatus: () => this.licenseStatus, @@ -85,12 +78,15 @@ export class WatcherServerPlugin implements Plugin { ], }); - http.registerRouteHandlerContext('watcher', async (ctx, request) => { - this.watcherESClient = this.watcherESClient ?? (await getCustomEsClient(getStartServices)); - return { - client: this.watcherESClient.asScoped(request), - }; - }); + http.registerRouteHandlerContext( + 'watcher', + async (ctx, request) => { + this.watcherESClient = this.watcherESClient ?? (await getCustomEsClient(getStartServices)); + return { + client: this.watcherESClient.asScoped(request), + }; + } + ); registerListFieldsRoute(routeDependencies); registerLoadHistoryRoute(routeDependencies); diff --git a/x-pack/plugins/watcher/server/types.ts b/x-pack/plugins/watcher/server/types.ts index 5ef3aef7de1c..db31e94bf77e 100644 --- a/x-pack/plugins/watcher/server/types.ts +++ b/x-pack/plugins/watcher/server/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IRouter } from 'kibana/server'; +import type { ILegacyScopedClusterClient, IRouter, RequestHandlerContext } from 'src/core/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; import { LicensingPluginSetup } from '../../licensing/server'; @@ -21,7 +21,7 @@ export interface ServerShim { } export interface RouteDependencies { - router: IRouter; + router: WatcherRouter; getLicenseStatus: () => LicenseStatus; } @@ -29,3 +29,22 @@ export interface LicenseStatus { hasRequired: boolean; message?: string; } + +/** + * @internal + */ +export interface WatcherContext { + client: ILegacyScopedClusterClient; +} + +/** + * @internal + */ +export interface WatcherRequestHandlerContext extends RequestHandlerContext { + watcher: WatcherContext; +} + +/** + * @internal + */ +export type WatcherRouter = IRouter; diff --git a/x-pack/test/accessibility/apps/lens.ts b/x-pack/test/accessibility/apps/lens.ts index bfd79f070d28..a7cacd0ad1cb 100644 --- a/x-pack/test/accessibility/apps/lens.ts +++ b/x-pack/test/accessibility/apps/lens.ts @@ -12,7 +12,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const listingTable = getService('listingTable'); - describe('Lens', () => { + // FLAKY: https://github.com/elastic/kibana/issues/88926 + // FLAKY: https://github.com/elastic/kibana/issues/88927 + // FLAKY: https://github.com/elastic/kibana/issues/88929 + describe.skip('Lens', () => { const lensChartName = 'MyLensChart'; before(async () => { await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts b/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts index 7ec894540830..2731a3565d27 100644 --- a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts +++ b/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts @@ -122,7 +122,7 @@ export default function ({ getService }: FtrProviderContext) { .expect(200); expect(body.authenticationsSuccess!).to.eql(expectedResult.authSuccess); expect(body.authenticationsSuccessHistogram!).to.eql(expectedResult.authSuccessHistogram); - expect(body.authenticationsFailure!).to.eql(expectedResult.authSuccess); + expect(body.authenticationsFailure!).to.eql(expectedResult.authFailure); expect(body.authenticationsFailureHistogram!).to.eql(expectedResult.authFailureHistogram); }); @@ -185,14 +185,28 @@ export default function ({ getService }: FtrProviderContext) { y: 6, }, ], - authSuccess: null, + authSuccess: 0, authSuccessHistogram: null, authFailure: 0, authFailureHistogram: null, - uniqueSourceIps: null, - uniqueSourceIpsHistogram: null, - uniqueDestinationIps: null, - uniqueDestinationIpsHistogram: null, + uniqueSourceIps: 370, + uniqueSourceIpsHistogram: [ + { x: 1543276800000, y: 74 }, + { x: 1543278600000, y: 52 }, + { x: 1543280400000, y: 71 }, + { x: 1543282200000, y: 76 }, + { x: 1543284000000, y: 71 }, + { x: 1543285800000, y: 89 }, + ], + uniqueDestinationIps: 1, + uniqueDestinationIpsHistogram: [ + { x: 1543276800000, y: 0 }, + { x: 1543278600000, y: 0 }, + { x: 1543280400000, y: 0 }, + { x: 1543282200000, y: 0 }, + { x: 1543284000000, y: 0 }, + { x: 1543285800000, y: 1 }, + ], }; it('Make sure that we get KpiHosts data', async () => { @@ -227,14 +241,14 @@ export default function ({ getService }: FtrProviderContext) { to: TO, from: FROM, }, - defaultIndex: ['filebeat-*'], + defaultIndex: ['auditbeat-*'], docValueFields: [], inspect: false, }) .expect(200); expect(body.authenticationsSuccess!).to.eql(expectedResult.authSuccess); expect(body.authenticationsSuccessHistogram!).to.eql(expectedResult.authSuccessHistogram); - expect(body.authenticationsFailure!).to.eql(expectedResult.authSuccess); + expect(body.authenticationsFailure!).to.eql(expectedResult.authFailure); expect(body.authenticationsFailureHistogram!).to.eql(expectedResult.authFailureHistogram); }); @@ -249,7 +263,7 @@ export default function ({ getService }: FtrProviderContext) { to: TO, from: FROM, }, - defaultIndex: ['filebeat-*'], + defaultIndex: ['auditbeat-*'], docValueFields: [], inspect: false, }) diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts b/x-pack/test/api_integration/apis/security_solution/kpi_network.ts index b1802a012179..5db52ec4d1f3 100644 --- a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts +++ b/x-pack/test/api_integration/apis/security_solution/kpi_network.ts @@ -203,9 +203,9 @@ export default function ({ getService }: FtrProviderContext) { const expectedResult = { networkEvents: 665, uniqueFlowId: 124, - uniqueSourcePrivateIps: null, + uniqueSourcePrivateIps: 0, uniqueSourcePrivateIpsHistogram: null, - uniqueDestinationPrivateIps: null, + uniqueDestinationPrivateIps: 0, uniqueDestinationPrivateIpsHistogram: null, dnsQueries: 0, tlsHandshakes: 1, @@ -302,7 +302,7 @@ export default function ({ getService }: FtrProviderContext) { to: TO, from: FROM, }, - defaultIndex: ['filebeat-*'], + defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, }) diff --git a/x-pack/test/api_integration/apis/uptime/rest/__snapshots__/monitor_states_real_data.snap b/x-pack/test/api_integration/apis/uptime/rest/__snapshots__/monitor_states_real_data.snap index aa21c54da635..f8c068005b86 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/__snapshots__/monitor_states_real_data.snap +++ b/x-pack/test/api_integration/apis/uptime/rest/__snapshots__/monitor_states_real_data.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`monitor states endpoint will fetch monitor state data for the given down filters 1`] = ` +exports[`apis uptime uptime REST endpoints with real-world data monitor states endpoint will fetch monitor state data for the given down filters 1`] = ` Object { "nextPagePagination": "{\\"cursorDirection\\":\\"AFTER\\",\\"sortOrder\\":\\"ASC\\",\\"cursorKey\\":{\\"monitor_id\\":\\"0020-down\\"}}", "prevPagePagination": null, diff --git a/x-pack/test/apm_api_integration/basic/tests/services/__snapshots__/throughput.snap b/x-pack/test/apm_api_integration/basic/tests/services/__snapshots__/throughput.snap index 43a6ed6f0760..fe7f434aad2e 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/__snapshots__/throughput.snap +++ b/x-pack/test/apm_api_integration/basic/tests/services/__snapshots__/throughput.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Throughput when data is loaded returns the service throughput has the correct throughput 1`] = ` +exports[`APM specs (basic) Services Throughput when data is loaded returns the service throughput has the correct throughput 1`] = ` Array [ Object { "x": 1607435850000, diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap b/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap index a4104d4083a6..56e82d752dcc 100644 --- a/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap +++ b/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Top traces when data is loaded returns the correct buckets 1`] = ` +exports[`APM specs (basic) Traces Top traces when data is loaded returns the correct buckets 1`] = ` Array [ Object { "averageResponseTime": 1733, diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/breakdown.snap b/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/breakdown.snap index 0b83a910bc1a..25aa68d2a86b 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/breakdown.snap +++ b/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/breakdown.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Breakdown when data is loaded returns the transaction breakdown for a service 1`] = ` +exports[`APM specs (basic) Transactions Breakdown when data is loaded returns the transaction breakdown for a service 1`] = ` Object { "timeseries": Array [ Object { @@ -1019,7 +1019,7 @@ Object { } `; -exports[`Breakdown when data is loaded returns the transaction breakdown for a transaction group 9`] = ` +exports[`APM specs (basic) Transactions Breakdown when data is loaded returns the transaction breakdown for a transaction group 9`] = ` Array [ Object { "x": 1607435850000, diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/error_rate.snap b/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/error_rate.snap index 997c4da24f48..3b67a86ba84e 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/error_rate.snap +++ b/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/error_rate.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Error rate when data is loaded returns the transaction error rate has the correct error rate 1`] = ` +exports[`APM specs (basic) Transactions Error rate when data is loaded returns the transaction error rate has the correct error rate 1`] = ` Array [ Object { "x": 1607435850000, diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/top_transaction_groups.snap b/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/top_transaction_groups.snap index 417cca8fcaf7..473305f3e39a 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/top_transaction_groups.snap +++ b/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/top_transaction_groups.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Top transaction groups when data is loaded returns the correct buckets (when ignoring samples) 1`] = ` +exports[`APM specs (basic) Transactions Top transaction groups when data is loaded returns the correct buckets (when ignoring samples) 1`] = ` Array [ Object { "averageResponseTime": 2722.75, diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap b/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap index 4bf242d8f9b6..c8681866169a 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap +++ b/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UX page load dist when there is data returns page load distribution 1`] = ` +exports[`APM specs (trial) CSM UX page load dist when there is data returns page load distribution 1`] = ` Object { "maxDuration": 54.46, "minDuration": 0, @@ -456,7 +456,7 @@ Object { } `; -exports[`UX page load dist when there is data returns page load distribution with breakdown 1`] = ` +exports[`APM specs (trial) CSM UX page load dist when there is data returns page load distribution with breakdown 1`] = ` Array [ Object { "data": Array [ @@ -819,6 +819,6 @@ Array [ ] `; -exports[`UX page load dist when there is no data returns empty list 1`] = `Object {}`; +exports[`APM specs (trial) CSM UX page load dist when there is no data returns empty list 1`] = `Object {}`; -exports[`UX page load dist when there is no data returns empty list with breakdowns 1`] = `Object {}`; +exports[`APM specs (trial) CSM UX page load dist when there is no data returns empty list with breakdowns 1`] = `Object {}`; diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap b/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap index 38b009fc73d3..76e5180ba214 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap +++ b/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`CSM page views when there is data returns page views 1`] = ` +exports[`APM specs (trial) CSM CSM page views when there is data returns page views 1`] = ` Object { "items": Array [ Object { @@ -128,7 +128,7 @@ Object { } `; -exports[`CSM page views when there is data returns page views with breakdown 1`] = ` +exports[`APM specs (trial) CSM CSM page views when there is data returns page views with breakdown 1`] = ` Object { "items": Array [ Object { @@ -265,14 +265,14 @@ Object { } `; -exports[`CSM page views when there is no data returns empty list 1`] = ` +exports[`APM specs (trial) CSM CSM page views when there is no data returns empty list 1`] = ` Object { "items": Array [], "topItems": Array [], } `; -exports[`CSM page views when there is no data returns empty list with breakdowns 1`] = ` +exports[`APM specs (trial) CSM CSM page views when there is no data returns empty list with breakdowns 1`] = ` Object { "items": Array [], "topItems": Array [], diff --git a/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap b/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap index e4f87e3e49ff..7639822eaa6f 100644 --- a/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap +++ b/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Service Maps with a trial license /api/apm/service-map when there is data returns service map elements filtering by environment not defined 1`] = ` +exports[`APM specs (trial) Service Maps Service Maps with a trial license /api/apm/service-map when there is data returns service map elements filtering by environment not defined 1`] = ` Object { "elements": Array [ Object { @@ -514,7 +514,7 @@ Object { } `; -exports[`Service Maps with a trial license /api/apm/service-map when there is data returns the correct data 3`] = ` +exports[`APM specs (trial) Service Maps Service Maps with a trial license /api/apm/service-map when there is data returns the correct data 3`] = ` Array [ Object { "data": Object { @@ -1741,7 +1741,7 @@ Array [ ] `; -exports[`Service Maps with a trial license when there is data with anomalies with the default apm user returns the correct anomaly stats 3`] = ` +exports[`APM specs (trial) Service Maps Service Maps with a trial license when there is data with anomalies with the default apm user returns the correct anomaly stats 3`] = ` Object { "elements": Array [ Object { diff --git a/x-pack/test/apm_api_integration/trial/tests/transactions/__snapshots__/latency.snap b/x-pack/test/apm_api_integration/trial/tests/transactions/__snapshots__/latency.snap index 99d4026dcdb2..9475670387a0 100644 --- a/x-pack/test/apm_api_integration/trial/tests/transactions/__snapshots__/latency.snap +++ b/x-pack/test/apm_api_integration/trial/tests/transactions/__snapshots__/latency.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Latency when data is loaded and fetching transaction charts with uiFilters when not defined environments seleted should return the correct anomaly boundaries 1`] = ` +exports[`APM specs (trial) Transactions Latency when data is loaded and fetching transaction charts with uiFilters when not defined environments seleted should return the correct anomaly boundaries 1`] = ` Array [ Object { "x": 1607436000000, @@ -15,7 +15,7 @@ Array [ ] `; -exports[`Latency when data is loaded and fetching transaction charts with uiFilters with environment selected and empty kuery filter should return a non-empty anomaly series 1`] = ` +exports[`APM specs (trial) Transactions Latency when data is loaded and fetching transaction charts with uiFilters with environment selected and empty kuery filter should return a non-empty anomaly series 1`] = ` Array [ Object { "x": 1607436000000, @@ -30,7 +30,7 @@ Array [ ] `; -exports[`Latency when data is loaded and fetching transaction charts with uiFilters with environment selected in uiFilters should return a non-empty anomaly series 1`] = ` +exports[`APM specs (trial) Transactions Latency when data is loaded and fetching transaction charts with uiFilters with environment selected in uiFilters should return a non-empty anomaly series 1`] = ` Array [ Object { "x": 1607436000000, diff --git a/x-pack/test/fleet_api_integration/apis/agents/enroll.ts b/x-pack/test/fleet_api_integration/apis/agents/enroll.ts index c88106eb79cd..609b28417914 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/enroll.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/enroll.ts @@ -74,28 +74,6 @@ export default function (providerContext: FtrProviderContext) { .expect(401); }); - it('should not allow to enroll an agent with a shared id if it already exists ', async () => { - const { body: apiResponse } = await supertest - .post(`/api/fleet/agents/enroll`) - .set('kbn-xsrf', 'xxx') - .set( - 'authorization', - `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString('base64')}` - ) - .send({ - shared_id: 'agent2_filebeat', - type: 'PERMANENT', - metadata: { - local: { - elastic: { agent: { version: kibanaVersion } }, - }, - user_provided: {}, - }, - }) - .expect(400); - expect(apiResponse.message).to.match(/Impossible to enroll an already active agent/); - }); - it('should not allow to enroll an agent with a version > kibana', async () => { const { body: apiResponse } = await supertest .post(`/api/fleet/agents/enroll`) diff --git a/x-pack/test/fleet_api_integration/apis/agents/list.ts b/x-pack/test/fleet_api_integration/apis/agents/list.ts index 78a6dbb7d651..1b3d3e7d32cb 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/list.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/list.ts @@ -104,14 +104,14 @@ export default function ({ getService }: FtrProviderContext) { .expect(400); }); it('should accept a valid "kuery" value', async () => { - const filter = encodeURIComponent('fleet-agents.shared_id : "agent2_filebeat"'); + const filter = encodeURIComponent('fleet-agents.access_api_key_id : "api-key-2"'); const { body: apiResponse } = await supertest .get(`/api/fleet/agents?kuery=${filter}`) .expect(200); expect(apiResponse.total).to.eql(1); const agent = apiResponse.list[0]; - expect(agent.shared_id).to.eql('agent2_filebeat'); + expect(agent.access_api_key_id).to.eql('api-key-2'); }); }); } diff --git a/x-pack/test/fleet_api_integration/apis/fleet_setup.ts b/x-pack/test/fleet_api_integration/apis/fleet_setup.ts new file mode 100644 index 000000000000..8e9a01b28ea9 --- /dev/null +++ b/x-pack/test/fleet_api_integration/apis/fleet_setup.ts @@ -0,0 +1,124 @@ +/* + * 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 '../../api_integration/ftr_provider_context'; +import { skipIfNoDockerRegistry } from '../helpers'; + +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; + const supertest = getService('supertest'); + const es = getService('es'); + + describe('fleet_setup', () => { + skipIfNoDockerRegistry(providerContext); + beforeEach(async () => { + try { + await es.security.deleteUser({ + username: 'fleet_enroll', + }); + } catch (e) { + if (e.meta?.statusCode !== 404) { + throw e; + } + } + try { + await es.security.deleteRole({ + name: 'fleet_enroll', + }); + } catch (e) { + if (e.meta?.statusCode !== 404) { + throw e; + } + } + }); + + it('should not create a fleet_enroll role if one does not already exist', async () => { + const { body: apiResponse } = await supertest + .post(`/api/fleet/setup`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + + expect(apiResponse.isInitialized).to.be(true); + + try { + await es.security.getUser({ + username: 'fleet_enroll', + }); + } catch (e) { + expect(e.meta?.statusCode).to.eql(404); + } + }); + + it('should update the fleet_enroll role with new index permissions if one does already exist', async () => { + try { + await es.security.putRole({ + name: 'fleet_enroll', + body: { + cluster: ['monitor', 'manage_api_key'], + indices: [ + { + names: [ + 'logs-*', + 'metrics-*', + 'traces-*', + '.ds-logs-*', + '.ds-metrics-*', + '.ds-traces-*', + ], + privileges: ['write', 'create_index', 'indices:admin/auto_create'], + allow_restricted_indices: false, + }, + ], + applications: [], + run_as: [], + metadata: {}, + transient_metadata: { enabled: true }, + }, + }); + } catch (e) { + if (e.meta?.statusCode !== 404) { + throw e; + } + } + + const { body: apiResponse } = await supertest + .post(`/api/fleet/setup`) + .set('kbn-xsrf', 'xxxx') + .expect(200); + + expect(apiResponse.isInitialized).to.be(true); + + const { body: roleResponse } = await es.security.getRole({ + name: 'fleet_enroll', + }); + expect(roleResponse).to.have.key('fleet_enroll'); + expect(roleResponse.fleet_enroll).to.eql({ + cluster: ['monitor', 'manage_api_key'], + indices: [ + { + names: [ + 'logs-*', + 'metrics-*', + 'traces-*', + '.ds-logs-*', + '.ds-metrics-*', + '.ds-traces-*', + '.logs-endpoint.diagnostic.collection-*', + '.ds-.logs-endpoint.diagnostic.collection-*', + ], + privileges: ['write', 'create_index', 'indices:admin/auto_create'], + allow_restricted_indices: false, + }, + ], + applications: [], + run_as: [], + metadata: {}, + transient_metadata: { enabled: true }, + }); + }); + }); +} diff --git a/x-pack/test/fleet_api_integration/apis/index.js b/x-pack/test/fleet_api_integration/apis/index.js index 0d634f60e282..f47259965222 100644 --- a/x-pack/test/fleet_api_integration/apis/index.js +++ b/x-pack/test/fleet_api_integration/apis/index.js @@ -7,6 +7,8 @@ export default function ({ loadTestFile }) { describe('Fleet Endpoints', function () { this.tags('ciGroup10'); + // Fleet setup + loadTestFile(require.resolve('./fleet_setup')); // Agent setup loadTestFile(require.resolve('./agents_setup')); // Agents diff --git a/x-pack/test/functional/apps/maps/mvt_scaling.js b/x-pack/test/functional/apps/maps/mvt_scaling.js index b5c9ddcbd5e1..a7551aca78b5 100644 --- a/x-pack/test/functional/apps/maps/mvt_scaling.js +++ b/x-pack/test/functional/apps/maps/mvt_scaling.js @@ -29,7 +29,7 @@ export default function ({ getPageObjects, getService }) { //Source should be correct expect(mapboxStyle.sources[VECTOR_SOURCE_ID].tiles[0]).to.equal( - '/api/maps/mvt/getTile?x={x}&y={y}&z={z}&geometryFieldName=geometry&index=geo_shapes*&requestBody=(_source:(includes:!(geometry,prop1)),docvalue_fields:!(prop1),query:(bool:(filter:!((match_all:())),must:!(),must_not:!(),should:!())),script_fields:(),size:10000,stored_fields:!(geometry,prop1))&geoFieldType=geo_shape' + '/api/maps/mvt/getTile?x={x}&y={y}&z={z}&geometryFieldName=geometry&index=geo_shapes*&requestBody=(_source:(includes:!(geometry,prop1)),docvalue_fields:!(prop1),query:(bool:(filter:!((match_all:())),must:!(),must_not:!(),should:!())),runtime_mappings:(),script_fields:(),size:10000,stored_fields:!(geometry,prop1))&geoFieldType=geo_shape' ); //Should correctly load meta for style-rule (sigma is set to 1, opacity to 1) diff --git a/x-pack/test/functional/apps/maps/mvt_super_fine.js b/x-pack/test/functional/apps/maps/mvt_super_fine.js index 3de2f461bc85..aede736deb26 100644 --- a/x-pack/test/functional/apps/maps/mvt_super_fine.js +++ b/x-pack/test/functional/apps/maps/mvt_super_fine.js @@ -32,7 +32,7 @@ export default function ({ getPageObjects, getService }) { //Source should be correct expect(mapboxStyle.sources[MB_VECTOR_SOURCE_ID].tiles[0]).to.equal( - "/api/maps/mvt/getGridTile?x={x}&y={y}&z={z}&geometryFieldName=geo.coordinates&index=logstash-*&requestBody=(_source:(excludes:!()),aggs:(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:geo.coordinates)),max_of_bytes:(max:(field:bytes))),geotile_grid:(bounds:!n,field:geo.coordinates,precision:!n,shard_size:65535,size:65535))),fields:!((field:'@timestamp',format:date_time),(field:'relatedContent.article:modified_time',format:date_time),(field:'relatedContent.article:published_time',format:date_time),(field:utc_time,format:date_time)),query:(bool:(filter:!((match_all:()),(range:('@timestamp':(format:strict_date_optional_time,gte:'2015-09-20T00:00:00.000Z',lte:'2015-09-20T01:00:00.000Z')))),must:!(),must_not:!(),should:!())),script_fields:(hour_of_day:(script:(lang:painless,source:'doc[!'@timestamp!'].value.getHour()'))),size:0,stored_fields:!('*'))&requestType=grid&geoFieldType=geo_point" + "/api/maps/mvt/getGridTile?x={x}&y={y}&z={z}&geometryFieldName=geo.coordinates&index=logstash-*&requestBody=(_source:(excludes:!()),aggs:(gridSplit:(aggs:(gridCentroid:(geo_centroid:(field:geo.coordinates)),max_of_bytes:(max:(field:bytes))),geotile_grid:(bounds:!n,field:geo.coordinates,precision:!n,shard_size:65535,size:65535))),fields:!((field:'@timestamp',format:date_time),(field:'relatedContent.article:modified_time',format:date_time),(field:'relatedContent.article:published_time',format:date_time),(field:utc_time,format:date_time)),query:(bool:(filter:!((match_all:()),(range:('@timestamp':(format:strict_date_optional_time,gte:'2015-09-20T00:00:00.000Z',lte:'2015-09-20T01:00:00.000Z')))),must:!(),must_not:!(),should:!())),runtime_mappings:(),script_fields:(hour_of_day:(script:(lang:painless,source:'doc[!'@timestamp!'].value.getHour()'))),size:0,stored_fields:!('*'))&requestType=grid&geoFieldType=geo_point" ); //Should correctly load meta for style-rule (sigma is set to 1, opacity to 1) diff --git a/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts index fc0c339ca269..531eba54f931 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/file_data_visualizer.ts @@ -7,6 +7,7 @@ import path from 'path'; import { FtrProviderContext } from '../../../ftr_provider_context'; +import { ML_JOB_FIELD_TYPES } from '../../../../../plugins/ml/common/constants/field_types'; export default function ({ getService }: FtrProviderContext) { const ml = getService('ml'); @@ -17,11 +18,98 @@ export default function ({ getService }: FtrProviderContext) { filePath: path.join(__dirname, 'files_to_import', 'artificial_server_log'), indexName: 'user-import_1', createIndexPattern: false, + fieldTypeFilters: [ML_JOB_FIELD_TYPES.NUMBER, ML_JOB_FIELD_TYPES.DATE], + fieldNameFilters: ['clientip'], expected: { results: { title: 'artificial_server_log', numberOfFields: 4, }, + metricFields: [ + { + fieldName: 'bytes', + type: ML_JOB_FIELD_TYPES.NUMBER, + docCountFormatted: '19 (100%)', + statsMaxDecimalPlaces: 3, + topValuesCount: 8, + }, + { + fieldName: 'httpversion', + type: ML_JOB_FIELD_TYPES.NUMBER, + docCountFormatted: '19 (100%)', + statsMaxDecimalPlaces: 3, + topValuesCount: 1, + }, + { + fieldName: 'response', + type: ML_JOB_FIELD_TYPES.NUMBER, + docCountFormatted: '19 (100%)', + statsMaxDecimalPlaces: 3, + topValuesCount: 3, + }, + ], + nonMetricFields: [ + { + fieldName: 'timestamp', + type: ML_JOB_FIELD_TYPES.DATE, + docCountFormatted: '19 (100%)', + exampleCount: 10, + }, + { + fieldName: 'agent', + type: ML_JOB_FIELD_TYPES.KEYWORD, + exampleCount: 8, + docCountFormatted: '19 (100%)', + }, + { + fieldName: 'auth', + type: ML_JOB_FIELD_TYPES.KEYWORD, + exampleCount: 1, + docCountFormatted: '19 (100%)', + }, + { + fieldName: 'ident', + type: ML_JOB_FIELD_TYPES.KEYWORD, + exampleCount: 1, + docCountFormatted: '19 (100%)', + }, + { + fieldName: 'verb', + type: ML_JOB_FIELD_TYPES.KEYWORD, + exampleCount: 1, + docCountFormatted: '19 (100%)', + }, + { + fieldName: 'request', + type: ML_JOB_FIELD_TYPES.KEYWORD, + exampleCount: 2, + docCountFormatted: '19 (100%)', + }, + { + fieldName: 'referrer', + type: ML_JOB_FIELD_TYPES.KEYWORD, + exampleCount: 1, + docCountFormatted: '19 (100%)', + }, + { + fieldName: 'clientip', + type: ML_JOB_FIELD_TYPES.IP, + exampleCount: 7, + docCountFormatted: '19 (100%)', + }, + { + fieldName: 'message', + type: ML_JOB_FIELD_TYPES.TEXT, + exampleCount: 10, + docCountFormatted: '19 (100%)', + }, + ], + visibleMetricFieldsCount: 3, + totalMetricFieldsCount: 3, + populatedFieldsCount: 12, + totalFieldsCount: 12, + fieldTypeFiltersResultCount: 4, + fieldNameFiltersResultCount: 1, }, }, ]; @@ -63,8 +151,65 @@ export default function ({ getService }: FtrProviderContext) { await ml.dataVisualizerFileBased.assertFileContentPanelExists(); await ml.dataVisualizerFileBased.assertSummaryPanelExists(); await ml.dataVisualizerFileBased.assertFileStatsPanelExists(); - await ml.dataVisualizerFileBased.assertNumberOfFieldCards( - testData.expected.results.numberOfFields + + await ml.testExecution.logTestStep( + `displays elements in the data visualizer table correctly` + ); + await ml.dataVisualizerIndexBased.assertDataVisualizerTableExist(); + + await ml.dataVisualizerIndexBased.assertVisibleMetricFieldsCount( + testData.expected.visibleMetricFieldsCount + ); + await ml.dataVisualizerIndexBased.assertTotalMetricFieldsCount( + testData.expected.totalMetricFieldsCount + ); + await ml.dataVisualizerIndexBased.assertVisibleFieldsCount( + testData.expected.totalFieldsCount + ); + await ml.dataVisualizerIndexBased.assertTotalFieldsCount( + testData.expected.totalFieldsCount + ); + + await ml.testExecution.logTestStep( + 'displays details for metric fields and non-metric fields correctly' + ); + await ml.dataVisualizerTable.ensureNumRowsPerPage(25); + + for (const fieldRow of testData.expected.metricFields) { + await ml.dataVisualizerTable.assertNumberFieldContents( + fieldRow.fieldName, + fieldRow.docCountFormatted, + fieldRow.topValuesCount, + false + ); + } + for (const fieldRow of testData.expected.nonMetricFields!) { + await ml.dataVisualizerTable.assertNonMetricFieldContents( + fieldRow.type, + fieldRow.fieldName!, + fieldRow.docCountFormatted, + fieldRow.exampleCount + ); + } + + await ml.testExecution.logTestStep('sets and resets field type filter correctly'); + await ml.dataVisualizerTable.setFieldTypeFilter( + testData.fieldTypeFilters, + testData.expected.fieldTypeFiltersResultCount + ); + await ml.dataVisualizerTable.removeFieldTypeFilter( + testData.fieldTypeFilters, + testData.expected.totalFieldsCount + ); + + await ml.testExecution.logTestStep('sets and resets field name filter correctly'); + await ml.dataVisualizerTable.setFieldNameFilter( + testData.fieldNameFilters, + testData.expected.fieldNameFiltersResultCount + ); + await ml.dataVisualizerTable.removeFieldNameFilter( + testData.fieldNameFilters, + testData.expected.totalFieldsCount ); await ml.testExecution.logTestStep('loads the import settings page'); diff --git a/x-pack/test/functional/apps/ml/data_visualizer/files_to_import/artificial_server_log b/x-pack/test/functional/apps/ml/data_visualizer/files_to_import/artificial_server_log index 3571d3c9b5e4..d9be69996b86 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/files_to_import/artificial_server_log +++ b/x-pack/test/functional/apps/ml/data_visualizer/files_to_import/artificial_server_log @@ -1,19 +1,20 @@ -2018-01-06 16:56:14.295748 INFO host:'Server A' Incoming connection from ip 123.456.789.0 -2018-01-06 16:56:15.295748 INFO host:'Server A' Incoming connection from ip 123.456.789.1 -2018-01-06 16:56:16.295748 INFO host:'Server A' Incoming connection from ip 123.456.789.2 -2018-01-06 16:56:17.295748 INFO host:'Server A' Incoming connection from ip 123.456.789.3 -2018-01-06 16:56:18.295748 INFO host:'Server B' Incoming connection from ip 123.456.789.0 -2018-01-06 16:56:19.295748 INFO host:'Server B' Incoming connection from ip 123.456.789.2 -2018-01-06 16:56:20.295748 INFO host:'Server B' Incoming connection from ip 123.456.789.3 -2018-01-06 16:56:21.295748 INFO host:'Server B' Incoming connection from ip 123.456.789.4 -2018-01-06 16:56:22.295748 WARN host:'Server A' Disk watermark 80% -2018-01-06 17:16:23.295748 WARN host:'Server A' Disk watermark 90% -2018-01-06 17:36:10.295748 ERROR host:'Server A' Main process crashed -2018-01-06 17:36:14.295748 INFO host:'Server A' Connection from ip 123.456.789.0 closed -2018-01-06 17:36:15.295748 INFO host:'Server A' Connection from ip 123.456.789.1 closed -2018-01-06 17:36:16.295748 INFO host:'Server A' Connection from ip 123.456.789.2 closed -2018-01-06 17:36:17.295748 INFO host:'Server A' Connection from ip 123.456.789.3 closed -2018-01-06 17:46:11.295748 INFO host:'Server B' Some special characters °!"§$%&/()=?`'^²³{[]}\+*~#'-_.:,;µ|<>äöüß -2018-01-06 17:46:12.295748 INFO host:'Server B' Shutting down - - +93.180.71.3 - - [17/May/2015:08:05:32 +0000] "GET /downloads/product_1 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.21)" +93.180.71.3 - - [17/May/2015:08:05:23 +0000] "GET /downloads/product_1 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.21)" +80.91.33.133 - - [17/May/2015:08:05:24 +0000] "GET /downloads/product_1 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.17)" +217.168.17.5 - - [17/May/2015:08:05:34 +0000] "GET /downloads/product_1 HTTP/1.1" 200 490 "-" "Debian APT-HTTP/1.3 (0.8.10.3)" +217.168.17.5 - - [17/May/2015:08:05:09 +0000] "GET /downloads/product_2 HTTP/1.1" 200 490 "-" "Debian APT-HTTP/1.3 (0.8.10.3)" +93.180.71.3 - - [17/May/2015:08:05:57 +0000] "GET /downloads/product_1 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.21)" +217.168.17.5 - - [17/May/2015:08:05:02 +0000] "GET /downloads/product_2 HTTP/1.1" 404 337 "-" "Debian APT-HTTP/1.3 (0.8.10.3)" +217.168.17.5 - - [17/May/2015:08:05:42 +0000] "GET /downloads/product_1 HTTP/1.1" 404 332 "-" "Debian APT-HTTP/1.3 (0.8.10.3)" +80.91.33.133 - - [17/May/2015:08:05:01 +0000] "GET /downloads/product_1 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.17)" +93.180.71.3 - - [17/May/2015:08:05:27 +0000] "GET /downloads/product_1 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.21)" +217.168.17.5 - - [17/May/2015:08:05:12 +0000] "GET /downloads/product_2 HTTP/1.1" 200 3316 "-" "Some special characters °!"§$%&/()=?`'^²³{[]}\+*~#'-_.:,;µ|<>äöüß" +188.138.60.101 - - [17/May/2015:08:05:49 +0000] "GET /downloads/product_2 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.9.7.9)" +80.91.33.133 - - [17/May/2015:08:05:14 +0000] "GET /downloads/product_1 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.16)" +46.4.66.76 - - [17/May/2015:08:05:45 +0000] "GET /downloads/product_1 HTTP/1.1" 404 318 "-" "Debian APT-HTTP/1.3 (1.0.1ubuntu2)" +93.180.71.3 - - [17/May/2015:08:05:26 +0000] "GET /downloads/product_1 HTTP/1.1" 404 324 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.21)" +91.234.194.89 - - [17/May/2015:08:05:22 +0000] "GET /downloads/product_2 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.9.7.9)" +80.91.33.133 - - [17/May/2015:08:05:07 +0000] "GET /downloads/product_1 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.17)" +37.26.93.214 - - [17/May/2015:08:05:38 +0000] "GET /downloads/product_2 HTTP/1.1" 404 319 "-" "Go 1.1 package http" +188.138.60.101 - - [17/May/2015:08:05:25 +0000] "GET /downloads/product_2 HTTP/1.1" 304 0 "-" "Debian APT-HTTP/1.3 (0.9.7.9)" +93.180.71.3 - - [17/May/2015:08:05:11 +0000] "GET /downloads/product_1 HTTP/1.1" 404 340 "-" "Debian APT-HTTP/1.3 (0.8.16~exp12ubuntu10.21)" diff --git a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts index 5a8b9bfc114e..0833f84960ea 100644 --- a/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts +++ b/x-pack/test/functional/apps/ml/data_visualizer/index_data_visualizer.ts @@ -6,7 +6,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; import { ML_JOB_FIELD_TYPES } from '../../../../../plugins/ml/common/constants/field_types'; -import { FieldVisConfig } from '../../../../../plugins/ml/public/application/datavisualizer/index_based/common'; +import { FieldVisConfig } from '../../../../../plugins/ml/public/application/datavisualizer/stats_table/types'; interface MetricFieldVisConfig extends FieldVisConfig { statsMaxDecimalPlaces: number; diff --git a/x-pack/test/functional/es_archives/data/search_sessions/data.json.gz b/x-pack/test/functional/es_archives/data/search_sessions/data.json.gz new file mode 100644 index 000000000000..28260ee99e4d Binary files /dev/null and b/x-pack/test/functional/es_archives/data/search_sessions/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/data/search_sessions/mappings.json b/x-pack/test/functional/es_archives/data/search_sessions/mappings.json new file mode 100644 index 000000000000..24bbcbea2338 --- /dev/null +++ b/x-pack/test/functional/es_archives/data/search_sessions/mappings.json @@ -0,0 +1,2596 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana": { + } + }, + "index": ".kibana_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "action": "6e96ac5e648f57523879661ea72525b7", + "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "alert": "49eb3350984bd2a162914d3776e70cfb", + "api_key_pending_invalidation": "16f515278a295f6245149ad7c5ddedb7", + "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", + "apm-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "app_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_daily": "43b8830d5d0df85a6823d290885fc9fd", + "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_transactional": "3d1b76c39bfb2cc8296b024d73854724", + "background-session": "dfd06597e582fdbbbc09f1a3615e6ce0", + "canvas-element": "7390014e1091044523666d97247392fc", + "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "canvas-workpad-template": "ae2673f678281e2c055d764b153e9715", + "cases": "477f214ff61acc3af26a7b7818e380c1", + "cases-comments": "8a50736330e953bca91747723a319593", + "cases-configure": "387c5f3a3bda7e0ae0dd4e106f914a69", + "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", + "config": "c63748b75f39d0c54de12d12c1ccbc20", + "core-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", + "dashboard": "40554caf09725935e2c02e02563a2d07", + "endpoint:user-artifact": "4a11183eee21e6fbad864f7a30b39ad0", + "endpoint:user-artifact-manifest": "4b9c0e7cfaf86d82a7ee9ed68065e50d", + "enterprise_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "epm-packages": "0cbbb16506734d341a96aaed65ec6413", + "epm-packages-assets": "44621b2f6052ef966da47b7c3a00f33b", + "exception-list": "67f055ab8c10abd7b2ebfd969b836788", + "exception-list-agnostic": "67f055ab8c10abd7b2ebfd969b836788", + "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", + "fleet-agent-actions": "9511b565b1cc6441a42033db3d5de8e9", + "fleet-agent-events": "e20a508b6e805189356be381dbfac8db", + "fleet-agents": "cb661e8ede2b640c42c8e5ef99db0683", + "fleet-enrollment-api-keys": "a69ef7ae661dab31561d6c6f052ef2a7", + "graph-workspace": "27a94b2edcb0610c6aea54a7c56d7752", + "index-pattern": "45915a1ad866812242df474eb0479052", + "infrastructure-ui-source": "3d1b76c39bfb2cc8296b024d73854724", + "ingest-agent-policies": "8b0733cce189659593659dad8db426f0", + "ingest-outputs": "8854f34453a47e26f86a29f8f3b80b4e", + "ingest-package-policies": "c91ca97b1ff700f0fc64dc6b13d65a85", + "ingest_manager_settings": "02a03095f0e05b7a538fa801b88a217f", + "inventory-view": "3d1b76c39bfb2cc8296b024d73854724", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "lens": "52346cfec69ff7b47d5f0c12361a2797", + "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", + "map": "4a05b35c3a3a58fbc72dd0202dc3487f", + "maps-telemetry": "5ef305b18111b77789afefbd36b66171", + "metrics-explorer-view": "3d1b76c39bfb2cc8296b024d73854724", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "ml-job": "3bb64c31915acf93fc724af137a0891b", + "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", + "monitoring-telemetry": "2669d5ec15e82391cf58df4294ee9c68", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "originId": "2f4316de49999235636386fe51dc06c1", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "43012c7ebc4cb57054e0a490e4b43023", + "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "siem-detection-engine-rule-actions": "6569b288c169539db10cb262bf79de18", + "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", + "siem-ui-timeline": "d12c5474364d737d17252acf1dc4585c", + "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", + "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", + "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", + "spaces-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", + "tag": "83d55da58f6530f7055415717ec06474", + "telemetry": "36a616f7026dfa617d6655df850fe16d", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "upgrade-assistant-reindex-operation": "215107c281839ea9b3ad5f6419819763", + "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", + "uptime-dynamic-settings": "3d1b76c39bfb2cc8296b024d73854724", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "visualization": "f819cf6636b75c9e76ba733a0c6ef355", + "workplace_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724" + } + }, + "dynamic": "strict", + "properties": { + "action": { + "properties": { + "actionTypeId": { + "type": "keyword" + }, + "config": { + "enabled": false, + "type": "object" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "secrets": { + "type": "binary" + } + } + }, + "action_task_params": { + "properties": { + "actionId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "params": { + "enabled": false, + "type": "object" + } + } + }, + "alert": { + "properties": { + "actions": { + "properties": { + "actionRef": { + "type": "keyword" + }, + "actionTypeId": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + }, + "type": "nested" + }, + "alertTypeId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "apiKeyOwner": { + "type": "keyword" + }, + "consumer": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + }, + "createdBy": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "executionStatus": { + "properties": { + "error": { + "properties": { + "message": { + "type": "keyword" + }, + "reason": { + "type": "keyword" + } + } + }, + "lastExecutionDate": { + "type": "date" + }, + "status": { + "type": "keyword" + } + } + }, + "meta": { + "properties": { + "versionApiKeyLastmodified": { + "type": "keyword" + } + } + }, + "muteAll": { + "type": "boolean" + }, + "mutedInstanceIds": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "notifyWhen": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledTaskId": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "throttle": { + "type": "keyword" + }, + "updatedAt": { + "type": "date" + }, + "updatedBy": { + "type": "keyword" + } + } + }, + "api_key_pending_invalidation": { + "properties": { + "apiKeyId": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + } + } + }, + "apm-indices": { + "properties": { + "apm_oss": { + "properties": { + "errorIndices": { + "type": "keyword" + }, + "metricsIndices": { + "type": "keyword" + }, + "onboardingIndices": { + "type": "keyword" + }, + "sourcemapIndices": { + "type": "keyword" + }, + "spanIndices": { + "type": "keyword" + }, + "transactionIndices": { + "type": "keyword" + } + } + } + } + }, + "apm-telemetry": { + "dynamic": "false", + "type": "object" + }, + "app_search_telemetry": { + "dynamic": "false", + "type": "object" + }, + "application_usage_daily": { + "dynamic": "false", + "properties": { + "timestamp": { + "type": "date" + } + } + }, + "application_usage_totals": { + "dynamic": "false", + "type": "object" + }, + "application_usage_transactional": { + "dynamic": "false", + "type": "object" + }, + "search-session": { + "properties": { + "appId": { + "type": "keyword" + }, + "created": { + "type": "date" + }, + "expires": { + "type": "date" + }, + "idMapping": { + "enabled": false, + "type": "object" + }, + "initialState": { + "enabled": false, + "type": "object" + }, + "name": { + "type": "keyword" + }, + "restoreState": { + "enabled": false, + "type": "object" + }, + "sessionId": { + "type": "keyword" + }, + "status": { + "type": "keyword" + }, + "urlGeneratorId": { + "type": "keyword" + } + } + }, + "canvas-element": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "content": { + "type": "text" + }, + "help": { + "type": "text" + }, + "image": { + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad-template": { + "dynamic": "false", + "properties": { + "help": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "tags": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "template_key": { + "type": "keyword" + } + } + }, + "cases": { + "properties": { + "closed_at": { + "type": "date" + }, + "closed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "connector": { + "properties": { + "fields": { + "properties": { + "key": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "id": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "description": { + "type": "text" + }, + "external_service": { + "properties": { + "connector_id": { + "type": "keyword" + }, + "connector_name": { + "type": "keyword" + }, + "external_id": { + "type": "keyword" + }, + "external_title": { + "type": "text" + }, + "external_url": { + "type": "text" + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "status": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-comments": { + "properties": { + "alertId": { + "type": "keyword" + }, + "comment": { + "type": "text" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "index": { + "type": "keyword" + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-configure": { + "properties": { + "closure_type": { + "type": "keyword" + }, + "connector": { + "properties": { + "fields": { + "properties": { + "key": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "id": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-user-actions": { + "properties": { + "action": { + "type": "keyword" + }, + "action_at": { + "type": "date" + }, + "action_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "action_field": { + "type": "keyword" + }, + "new_value": { + "type": "text" + }, + "old_value": { + "type": "text" + } + } + }, + "config": { + "dynamic": "false", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "core-usage-stats": { + "dynamic": "false", + "type": "object" + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "optionsJSON": { + "index": false, + "type": "text" + }, + "panelsJSON": { + "index": false, + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "pause": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "section": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "value": { + "doc_values": false, + "index": false, + "type": "integer" + } + } + }, + "timeFrom": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "timeRestore": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "timeTo": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "endpoint:user-artifact": { + "properties": { + "body": { + "type": "binary" + }, + "compressionAlgorithm": { + "index": false, + "type": "keyword" + }, + "created": { + "index": false, + "type": "date" + }, + "decodedSha256": { + "index": false, + "type": "keyword" + }, + "decodedSize": { + "index": false, + "type": "long" + }, + "encodedSha256": { + "type": "keyword" + }, + "encodedSize": { + "index": false, + "type": "long" + }, + "encryptionAlgorithm": { + "index": false, + "type": "keyword" + }, + "identifier": { + "type": "keyword" + } + } + }, + "endpoint:user-artifact-manifest": { + "properties": { + "created": { + "index": false, + "type": "date" + }, + "ids": { + "index": false, + "type": "keyword" + }, + "schemaVersion": { + "type": "keyword" + }, + "semanticVersion": { + "index": false, + "type": "keyword" + } + } + }, + "enterprise_search_telemetry": { + "dynamic": "false", + "type": "object" + }, + "epm-packages": { + "properties": { + "es_index_patterns": { + "enabled": false, + "type": "object" + }, + "install_source": { + "type": "keyword" + }, + "install_started_at": { + "type": "date" + }, + "install_status": { + "type": "keyword" + }, + "install_version": { + "type": "keyword" + }, + "installed_es": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "installed_kibana": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "internal": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "package_assets": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "removable": { + "type": "boolean" + }, + "version": { + "type": "keyword" + } + } + }, + "epm-packages-assets": { + "properties": { + "asset_path": { + "type": "keyword" + }, + "data_base64": { + "type": "binary" + }, + "data_utf8": { + "index": false, + "type": "text" + }, + "install_source": { + "type": "keyword" + }, + "media_type": { + "type": "keyword" + }, + "package_name": { + "type": "keyword" + }, + "package_version": { + "type": "keyword" + } + } + }, + "exception-list": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "os_types": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "exception-list-agnostic": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "os_types": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "file-upload-telemetry": { + "properties": { + "filesUploadedTotalCount": { + "type": "long" + } + } + }, + "fleet-agent-actions": { + "properties": { + "ack_data": { + "type": "text" + }, + "agent_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "data": { + "type": "binary" + }, + "policy_id": { + "type": "keyword" + }, + "policy_revision": { + "type": "integer" + }, + "sent_at": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agent-events": { + "properties": { + "action_id": { + "type": "keyword" + }, + "agent_id": { + "type": "keyword" + }, + "data": { + "type": "text" + }, + "message": { + "type": "text" + }, + "payload": { + "type": "text" + }, + "policy_id": { + "type": "keyword" + }, + "stream_id": { + "type": "keyword" + }, + "subtype": { + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agents": { + "properties": { + "access_api_key_id": { + "type": "keyword" + }, + "active": { + "type": "boolean" + }, + "current_error_events": { + "index": false, + "type": "text" + }, + "default_api_key": { + "type": "binary" + }, + "default_api_key_id": { + "type": "keyword" + }, + "enrolled_at": { + "type": "date" + }, + "last_checkin": { + "type": "date" + }, + "last_checkin_status": { + "type": "keyword" + }, + "last_updated": { + "type": "date" + }, + "local_metadata": { + "type": "flattened" + }, + "packages": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "policy_revision": { + "type": "integer" + }, + "shared_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "unenrolled_at": { + "type": "date" + }, + "unenrollment_started_at": { + "type": "date" + }, + "updated_at": { + "type": "date" + }, + "upgrade_started_at": { + "type": "date" + }, + "upgraded_at": { + "type": "date" + }, + "user_provided_metadata": { + "type": "flattened" + }, + "version": { + "type": "keyword" + } + } + }, + "fleet-enrollment-api-keys": { + "properties": { + "active": { + "type": "boolean" + }, + "api_key": { + "type": "binary" + }, + "api_key_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "expire_at": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "legacyIndexPatternRef": { + "index": false, + "type": "text" + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "index-pattern": { + "dynamic": "false", + "properties": { + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "infrastructure-ui-source": { + "dynamic": "false", + "type": "object" + }, + "ingest-agent-policies": { + "properties": { + "description": { + "type": "text" + }, + "is_default": { + "type": "boolean" + }, + "monitoring_enabled": { + "index": false, + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "package_policies": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "status": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest-outputs": { + "properties": { + "ca_sha256": { + "index": false, + "type": "keyword" + }, + "config": { + "type": "flattened" + }, + "config_yaml": { + "type": "text" + }, + "fleet_enroll_password": { + "type": "binary" + }, + "fleet_enroll_username": { + "type": "binary" + }, + "hosts": { + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "ingest-package-policies": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "enabled": { + "type": "boolean" + }, + "inputs": { + "enabled": false, + "properties": { + "compiled_input": { + "type": "flattened" + }, + "config": { + "type": "flattened" + }, + "enabled": { + "type": "boolean" + }, + "streams": { + "properties": { + "compiled_stream": { + "type": "flattened" + }, + "config": { + "type": "flattened" + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + }, + "type": "nested" + }, + "type": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + }, + "type": "nested" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "output_id": { + "type": "keyword" + }, + "package": { + "properties": { + "name": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "policy_id": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest_manager_settings": { + "properties": { + "agent_auto_upgrade": { + "type": "keyword" + }, + "has_seen_add_data_notice": { + "index": false, + "type": "boolean" + }, + "kibana_ca_sha256": { + "type": "keyword" + }, + "kibana_urls": { + "type": "keyword" + }, + "package_auto_upgrade": { + "type": "keyword" + } + } + }, + "inventory-view": { + "dynamic": "false", + "type": "object" + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "lens": { + "properties": { + "description": { + "type": "text" + }, + "expression": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "state": { + "type": "flattened" + }, + "title": { + "type": "text" + }, + "visualizationType": { + "type": "keyword" + } + } + }, + "lens-ui-telemetry": { + "properties": { + "count": { + "type": "integer" + }, + "date": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "map": { + "properties": { + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "maps-telemetry": { + "enabled": false, + "type": "object" + }, + "metrics-explorer-view": { + "dynamic": "false", + "type": "object" + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "config": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "index-pattern": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "search": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "space": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "ml-job": { + "properties": { + "datafeed_id": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "job_id": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "ml-telemetry": { + "properties": { + "file_data_visualizer": { + "properties": { + "index_creation_count": { + "type": "long" + } + } + } + } + }, + "monitoring-telemetry": { + "properties": { + "reportedClusterUuids": { + "type": "keyword" + } + } + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "originId": { + "type": "keyword" + }, + "query": { + "properties": { + "description": { + "type": "text" + }, + "filters": { + "enabled": false, + "type": "object" + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "index": false, + "type": "keyword" + } + } + }, + "timefilter": { + "enabled": false, + "type": "object" + }, + "title": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "properties": { + "columns": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "sort": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "search-telemetry": { + "dynamic": "false", + "type": "object" + }, + "siem-detection-engine-rule-actions": { + "properties": { + "actions": { + "properties": { + "action_type_id": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + } + }, + "alertThrottle": { + "type": "keyword" + }, + "ruleAlertId": { + "type": "keyword" + }, + "ruleThrottle": { + "type": "keyword" + } + } + }, + "siem-detection-engine-rule-status": { + "properties": { + "alertId": { + "type": "keyword" + }, + "bulkCreateTimeDurations": { + "type": "float" + }, + "gap": { + "type": "text" + }, + "lastFailureAt": { + "type": "date" + }, + "lastFailureMessage": { + "type": "text" + }, + "lastLookBackDate": { + "type": "date" + }, + "lastSuccessAt": { + "type": "date" + }, + "lastSuccessMessage": { + "type": "text" + }, + "searchAfterTimeDurations": { + "type": "float" + }, + "status": { + "type": "keyword" + }, + "statusDate": { + "type": "date" + } + } + }, + "siem-ui-timeline": { + "properties": { + "columns": { + "properties": { + "aggregatable": { + "type": "boolean" + }, + "category": { + "type": "keyword" + }, + "columnHeaderType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "example": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "indexes": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "placeholder": { + "type": "text" + }, + "searchable": { + "type": "boolean" + }, + "type": { + "type": "keyword" + } + } + }, + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "dataProviders": { + "properties": { + "and": { + "properties": { + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "type": { + "type": "text" + } + } + }, + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "type": { + "type": "text" + } + } + }, + "dateRange": { + "properties": { + "end": { + "type": "date" + }, + "start": { + "type": "date" + } + } + }, + "description": { + "type": "text" + }, + "eventType": { + "type": "keyword" + }, + "excludedRowRendererIds": { + "type": "text" + }, + "favorite": { + "properties": { + "favoriteDate": { + "type": "date" + }, + "fullName": { + "type": "text" + }, + "keySearch": { + "type": "text" + }, + "userName": { + "type": "text" + } + } + }, + "filters": { + "properties": { + "exists": { + "type": "text" + }, + "match_all": { + "type": "text" + }, + "meta": { + "properties": { + "alias": { + "type": "text" + }, + "controlledBy": { + "type": "text" + }, + "disabled": { + "type": "boolean" + }, + "field": { + "type": "text" + }, + "formattedValue": { + "type": "text" + }, + "index": { + "type": "keyword" + }, + "key": { + "type": "keyword" + }, + "negate": { + "type": "boolean" + }, + "params": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "text" + } + } + }, + "missing": { + "type": "text" + }, + "query": { + "type": "text" + }, + "range": { + "type": "text" + }, + "script": { + "type": "text" + } + } + }, + "indexNames": { + "type": "text" + }, + "kqlMode": { + "type": "keyword" + }, + "kqlQuery": { + "properties": { + "filterQuery": { + "properties": { + "kuery": { + "properties": { + "expression": { + "type": "text" + }, + "kind": { + "type": "keyword" + } + } + }, + "serializedQuery": { + "type": "text" + } + } + } + } + }, + "savedQueryId": { + "type": "keyword" + }, + "sort": { + "properties": { + "columnId": { + "type": "keyword" + }, + "sortDirection": { + "type": "keyword" + } + } + }, + "status": { + "type": "keyword" + }, + "templateTimelineId": { + "type": "text" + }, + "templateTimelineVersion": { + "type": "integer" + }, + "timelineType": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-note": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "note": { + "type": "text" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-pinned-event": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "space": { + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "imageUrl": { + "index": false, + "type": "text" + }, + "initials": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "spaces-usage-stats": { + "dynamic": "false", + "type": "object" + }, + "tag": { + "properties": { + "color": { + "type": "text" + }, + "description": { + "type": "text" + }, + "name": { + "type": "text" + } + } + }, + "telemetry": { + "properties": { + "allowChangingOptInStatus": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "lastReported": { + "type": "date" + }, + "lastVersionChecked": { + "type": "keyword" + }, + "reportFailureCount": { + "type": "integer" + }, + "reportFailureVersion": { + "type": "keyword" + }, + "sendUsageFrom": { + "type": "keyword" + }, + "userHasSeenNotice": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "tsvb-validation-telemetry": { + "properties": { + "failedRequests": { + "type": "long" + } + } + }, + "type": { + "type": "keyword" + }, + "ui-counter": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "upgrade-assistant-reindex-operation": { + "properties": { + "errorMessage": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "indexName": { + "type": "keyword" + }, + "lastCompletedStep": { + "type": "long" + }, + "locked": { + "type": "date" + }, + "newIndexName": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "reindexOptions": { + "properties": { + "openAndClose": { + "type": "boolean" + }, + "queueSettings": { + "properties": { + "queuedAt": { + "type": "long" + }, + "startedAt": { + "type": "long" + } + } + } + } + }, + "reindexTaskId": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "reindexTaskPercComplete": { + "type": "float" + }, + "runningReindexCount": { + "type": "integer" + }, + "status": { + "type": "integer" + } + } + }, + "upgrade-assistant-telemetry": { + "properties": { + "features": { + "properties": { + "deprecation_logging": { + "properties": { + "enabled": { + "null_value": true, + "type": "boolean" + } + } + } + } + }, + "ui_open": { + "properties": { + "cluster": { + "null_value": 0, + "type": "long" + }, + "indices": { + "null_value": 0, + "type": "long" + }, + "overview": { + "null_value": 0, + "type": "long" + } + } + }, + "ui_reindex": { + "properties": { + "close": { + "null_value": 0, + "type": "long" + }, + "open": { + "null_value": 0, + "type": "long" + }, + "start": { + "null_value": 0, + "type": "long" + }, + "stop": { + "null_value": 0, + "type": "long" + } + } + } + } + }, + "uptime-dynamic-settings": { + "dynamic": "false", + "type": "object" + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "savedSearchRefName": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "index": false, + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "index": false, + "type": "text" + } + } + }, + "workplace_search_telemetry": { + "dynamic": "false", + "type": "object" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} diff --git a/x-pack/test/functional/es_archives/fleet/agents/data.json b/x-pack/test/functional/es_archives/fleet/agents/data.json index f204e44b31bc..ca957e5ae2fe 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/data.json +++ b/x-pack/test/functional/es_archives/fleet/agents/data.json @@ -6,9 +6,8 @@ "source": { "type": "fleet-agents", "fleet-agents": { - "access_api_key_id": "api-key-2", + "access_api_key_id": "api-key-1", "active": true, - "shared_id": "agent1_filebeat", "policy_id": "policy1", "type": "PERMANENT", "local_metadata": {}, @@ -31,7 +30,6 @@ "fleet-agents": { "access_api_key_id": "api-key-2", "active": true, - "shared_id": "agent2_filebeat", "policy_id": "policy1", "type": "PERMANENT", "local_metadata": {}, @@ -54,7 +52,6 @@ "fleet-agents": { "access_api_key_id": "api-key-3", "active": true, - "shared_id": "agent3_metricbeat", "policy_id": "policy1", "type": "PERMANENT", "local_metadata": {}, @@ -77,7 +74,6 @@ "fleet-agents": { "access_api_key_id": "api-key-4", "active": true, - "shared_id": "agent4_metricbeat", "policy_id": "policy1", "type": "PERMANENT", "local_metadata": {}, diff --git a/x-pack/test/functional/es_archives/visualize/default/data.json b/x-pack/test/functional/es_archives/visualize/default/data.json index 5b5ee355c708..fe29bad0fa38 100644 --- a/x-pack/test/functional/es_archives/visualize/default/data.json +++ b/x-pack/test/functional/es_archives/visualize/default/data.json @@ -1,20 +1,22 @@ { "type": "doc", "value": { - "index": ".kibana", - "type": "doc", "id": "space:default", + "index": ".kibana_1", "source": { + "migrationVersion": { + "space": "6.6.0" + }, + "references": [ + ], "space": { - "name": "Default", + "_reserved": true, "description": "This is the default space!", - "disabledFeatures": [], - "_reserved": true + "disabledFeatures": [ + ], + "name": "Default" }, - "type": "space", - "migrationVersion": { - "space": "6.6.0" - } + "type": "space" } } } @@ -97,24 +99,25 @@ } } + { "type": "doc", "value": { - "index" : ".kibana", - "id" : "index-pattern:metricbeat-*", - "type": "_doc", - "source" : { - "index-pattern" : { - "fieldFormatMap" : "{\"aerospike.namespace.device.available.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.device.free.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.device.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.device.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.free.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.memory.used.data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.index.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.sindex.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"aws.rds.disk_usage.bin_log.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.free_local_storage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.free_storage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.freeable_memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.latency.commit\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.ddl\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.dml\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.insert\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.read\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.select\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.update\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.write\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.replica_lag.sec\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.swap_usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.volume_used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_daily_storage.bucket.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.downloaded.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.latency.first_byte.ms\":{\"id\":\"duration\",\"params\":{}},\"aws.s3_request.latency.total_request.ms\":{\"id\":\"duration\",\"params\":{}},\"aws.s3_request.requests.select_returned.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.requests.select_scanned.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.uploaded.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.sqs.oldest_message_age.sec\":{\"id\":\"duration\",\"params\":{}},\"aws.sqs.sent_message_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.degraded.ratio\":{\"id\":\"percent\",\"params\":{}},\"ceph.cluster_status.misplace.ratio\":{\"id\":\"percent\",\"params\":{}},\"ceph.cluster_status.pg.avail_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.data_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.total_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.used_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.traffic.read_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.traffic.write_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.log.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.misc.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.sst.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.total.byte\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.used.byte\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.used.pct\":{\"id\":\"percent\",\"params\":{}},\"ceph.pool_disk.stats.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.pool_disk.stats.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"client.bytes\":{\"id\":\"bytes\",\"params\":{}},\"client.nat.port\":{\"id\":\"string\",\"params\":{}},\"client.port\":{\"id\":\"string\",\"params\":{}},\"coredns.stats.dns.request.duration.ns.sum\":{\"id\":\"duration\",\"params\":{}},\"couchbase.bucket.data.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.disk.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.quota.ram.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.quota.use.pct\":{\"id\":\"percent\",\"params\":{}},\"couchbase.cluster.hdd.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.quota.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.used.by_data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.total.per_node.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.total.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.used.per_node.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.used.by_data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.couch.docs.data_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.couch.docs.disk_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.mcd_memory.allocated.bytes\":{\"id\":\"bytes\",\"params\":{}},\"destination.bytes\":{\"id\":\"bytes\",\"params\":{}},\"destination.nat.port\":{\"id\":\"string\",\"params\":{}},\"destination.port\":{\"id\":\"string\",\"params\":{}},\"docker.cpu.core.*.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.kernel.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.system.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.user.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.diskio.summary.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.commit.peak\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.commit.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.limit\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.private_working_set.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.rss.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.memory.rss.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.usage.max\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.usage.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.memory.usage.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.inbound.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.outbound.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.primaries.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.primaries.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.total.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.total.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.total.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.total.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.heap.init.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.heap.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.nonheap.init.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.nonheap.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.indices.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.disk.mvcc_db_total_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.memory.go_memstats_alloc.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.network.client_grpc_received.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.network.client_grpc_sent.bytes\":{\"id\":\"bytes\",\"params\":{}},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\",\"params\":{}},\"event.severity\":{\"id\":\"string\",\"params\":{}},\"golang.heap.allocations.active\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.allocated\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.idle\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.total\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.gc.next_gc_limit\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.obtained\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.released\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.stack\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.total\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.info.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"haproxy.info.memory.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.info.ssl.frontend.session_reuse.pct\":{\"id\":\"percent\",\"params\":{}},\"haproxy.stat.compressor.bypassed.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.response.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.throttle.pct\":{\"id\":\"percent\",\"params\":{}},\"http.request.body.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.request.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.body.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.status_code\":{\"id\":\"string\",\"params\":{}},\"kibana.stats.process.memory.heap.size_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kibana.stats.process.memory.heap.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kibana.stats.process.memory.heap.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.cpu.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.cpu.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.logs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.logs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.logs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.request.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.memory.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.allocatable.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.network.rx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.network.tx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.cpu.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.cpu.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.working_set.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.network.rx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.network.tx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.avg_obj_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.data_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.extent_free_list.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.file_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.index_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.storage_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.replstatus.headroom.max\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.headroom.min\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.lag.max\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.lag.min\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.oplog.size.allocated\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.replstatus.oplog.size.used\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.extra_info.heap_usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.dirty.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.maximum.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.max_file_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mysql.status.bytes.received\":{\"id\":\"bytes\",\"params\":{}},\"mysql.status.bytes.sent\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.cpu\":{\"id\":\"percent\",\"params\":{}},\"nats.stats.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.mem.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.uptime\":{\"id\":\"duration\",\"params\":{}},\"nats.subscriptions.cache.hit_rate\":{\"id\":\"percent\",\"params\":{}},\"network.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"process.pgid\":{\"id\":\"string\",\"params\":{}},\"process.pid\":{\"id\":\"string\",\"params\":{}},\"process.ppid\":{\"id\":\"string\",\"params\":{}},\"process.thread.id\":{\"id\":\"string\",\"params\":{}},\"rabbitmq.connection.frame_max\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.disk.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.disk.free.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.gc.reclaimed.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.io.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.io.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.mem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.queue.consumers.utilisation.pct\":{\"id\":\"percent\",\"params\":{}},\"rabbitmq.queue.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.active\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.allocated\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.fragmentation.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.resident\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.fragmentation.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.max.value\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.dataset\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.lua\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.peak\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.rss\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.value\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.buffer.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.copy_on_write.last_size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.rewrite.buffer.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.rewrite.current_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.aof.rewrite.last_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.aof.size.base\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.size.current\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.rdb.bgsave.current_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.rdb.bgsave.last_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.rdb.copy_on_write.last_size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.replication.backlog.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.replication.master.last_io_seconds_ago\":{\"id\":\"duration\",\"params\":{}},\"redis.info.replication.master.sync.last_io_seconds_ago\":{\"id\":\"duration\",\"params\":{}},\"redis.info.replication.master.sync.left_bytes\":{\"id\":\"bytes\",\"params\":{}},\"server.bytes\":{\"id\":\"bytes\",\"params\":{}},\"server.nat.port\":{\"id\":\"string\",\"params\":{}},\"server.port\":{\"id\":\"string\",\"params\":{}},\"source.bytes\":{\"id\":\"bytes\",\"params\":{}},\"source.nat.port\":{\"id\":\"string\",\"params\":{}},\"source.port\":{\"id\":\"string\",\"params\":{}},\"system.core.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.iowait.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.irq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.nice.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.softirq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.steal.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.system.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.user.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.idle.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.iowait.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.iowait.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.irq.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.irq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.nice.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.nice.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.softirq.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.softirq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.steal.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.steal.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.system.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.system.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.total.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.user.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.user.pct\":{\"id\":\"percent\",\"params\":{}},\"system.diskio.iostat.read.per_sec.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.iostat.write.per_sec.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.entropy.pct\":{\"id\":\"percent\",\"params\":{}},\"system.filesystem.available\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.free\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.total\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.fsstat.total_size.free\":{\"id\":\"bytes\",\"params\":{}},\"system.fsstat.total_size.total\":{\"id\":\"bytes\",\"params\":{}},\"system.fsstat.total_size.used\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.default_size\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.free\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.reserved\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.surplus\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.total\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.swap.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.total\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.total\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.blkio.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.active_anon.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.active_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.cache.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.hierarchical_memory_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.hierarchical_memsw_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.inactive_anon.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.inactive_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.mapped_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.rss_huge.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.swap.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.unevictable.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.memory.rss.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.memory.share\":{\"id\":\"bytes\",\"params\":{}},\"system.process.memory.size\":{\"id\":\"bytes\",\"params\":{}},\"system.socket.summary.tcp.memory\":{\"id\":\"bytes\",\"params\":{}},\"system.socket.summary.udp.memory\":{\"id\":\"bytes\",\"params\":{}},\"system.uptime.duration.ms\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"milliseconds\"}},\"url.port\":{\"id\":\"string\",\"params\":{}},\"vsphere.datastore.capacity.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.used.pct\":{\"id\":\"percent\",\"params\":{}},\"vsphere.host.memory.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.host.memory.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.host.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.free.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.total.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.used.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.used.host.bytes\":{\"id\":\"bytes\",\"params\":{}},\"windows.service.uptime.ms\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"milliseconds\"}}}", - "fields" : "[{\"name\":\"@timestamp\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"aerospike.namespace.client.delete.error\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.delete.not_found\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.delete.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.delete.timeout\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.read.error\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.read.not_found\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.read.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.read.timeout\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.write.error\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.write.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.write.timeout\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.device.available.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.device.free.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.device.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.device.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.hwm_breached\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.memory.free.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.memory.used.data.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.memory.used.index.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.memory.used.sindex.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.memory.used.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.node.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.objects.master\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.objects.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.stop_writes\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.ephemeral_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.bytes_per_request\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.bytes_per_sec\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.connections.async.closing\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.connections.async.keep_alive\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.connections.async.writing\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.connections.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.cpu.children_system\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.cpu.children_user\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.cpu.load\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.cpu.system\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.cpu.user\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.load.1\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.load.15\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.load.5\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.requests_per_sec\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.closing_connection\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.dns_lookup\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.gracefully_finishing\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.idle_cleanup\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.keepalive\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.logging\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.open_slot\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.reading_request\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.sending_reply\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.starting_up\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.waiting_for_connection\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.total_accesses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.total_kbytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.uptime.server_uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.uptime.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.workers.busy\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.workers.idle\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.cloudwatch.namespace\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.cpu.credit_balance\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.cpu.credit_usage\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.cpu.surplus_credit_balance\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.cpu.surplus_credits_charged\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.cpu.total.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.read.bytes_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.read.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.read.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.write.bytes_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.write.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.write.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.core.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.image.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.monitoring.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.private.dns_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.private.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.public.dns_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.public.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.state.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.state.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.threads_per_core\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.in.bytes_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.in.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.in.packets_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.out.bytes_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.out.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.out.packets_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.status.check_failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.status.check_failed_instance\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.status.check_failed_system\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.cpu.credit_balance\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.cpu.credit_usage\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.cpu.total.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.database_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.db_instance.arn\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.db_instance.class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.db_instance.identifier\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.db_instance.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.deadlocks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.disk_queue_depth\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.disk_usage.bin_log.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.disk_usage.replication_slot.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.disk_usage.transaction_logs.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.failed_sql_server_agent_jobs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.free_local_storage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.free_storage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.freeable_memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.commit\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.ddl\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.dml\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.insert\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.read\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.select\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.update\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.write\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.login_failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.maximum_used_transaction_ids\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.oldest_replication_slot_lag.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.queries\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.read_io.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.replica_lag.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.swap_usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.commit\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.ddl\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.delete\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.dml\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.insert\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.network\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.network_receive\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.network_transmit\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.read\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.select\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.update\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.write\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.transaction_logs_generation\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.transactions.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.transactions.blocked\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.volume_used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.write_io.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_daily_storage.bucket.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_daily_storage.bucket.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_daily_storage.number_of_objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.bucket.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.downloaded.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.errors.4xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.errors.5xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.latency.first_byte.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.latency.total_request.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.delete\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.get\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.head\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.list\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.post\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.put\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.select\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.select_returned.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.select_scanned.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.uploaded.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.empty_receives\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.delayed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.not_visible\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.visible\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.oldest_message_age.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.queue.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.sent_message_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.state.management.enabled\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.state.module.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.state.output.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.state.queue.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.acked\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.batches\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.duplicates\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.toomany\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.read.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.write.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.runtime.goroutines\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.uptime.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_disk.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_disk.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_disk.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_health.overall_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_health.timechecks.epoch\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_health.timechecks.round.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_health.timechecks.round.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.degraded.objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.degraded.ratio\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.degraded.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.misplace.objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.misplace.ratio\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.misplace.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.epoch\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.full\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.nearfull\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.num_in_osds\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.num_osds\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.num_remapped_pgs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.num_up_osds\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg.avail_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg.data_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg.total_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg.used_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg_state.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg_state.state_name\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg_state.version\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.traffic.read_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.traffic.read_op_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.traffic.write_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.traffic.write_op_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.version\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.available.kb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.available.pct\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.health\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.last_updated\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.store_stats.last_updated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.store_stats.log.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.store_stats.misc.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.store_stats.sst.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.store_stats.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.total.kb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.used.kb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.device_class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.pg_num\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.total.byte\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.used.byte\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.used.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.children\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.crush_weight\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.depth\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.device_class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.exists\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.father\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.primary_affinity\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.reweight\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.type_id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.stats.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.stats.objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.stats.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.stats.used.kb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.account.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.availability_zone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.image.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.instance.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.instance.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.machine.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.project.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.provider\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.region\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.autopilot.healthy\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.alloc.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.garbage_collector.pause.current.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.garbage_collector.pause.total.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.garbage_collector.runs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.goroutines\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.heap_objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.malloc_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.sys.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.image.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.image.tag\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.runtime\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.cache.hits.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.cache.misses.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.do.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.duration.ns.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.duration.ns.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.type.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.response.rcode.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.response.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.response.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.panic.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.proto\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.rcode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.server\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.zone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.data.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.disk.fetches\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.disk.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.item_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.memory.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.quota.ram.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.quota.use.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.hdd.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.hdd.quota.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.hdd.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.hdd.used.by_data.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.hdd.used.value.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.max_bucket_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.quota.index_memory.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.quota.memory.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.quota.total.per_node.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.quota.total.value.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.quota.used.per_node.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.quota.used.value.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.used.by_data.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.used.value.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.cmd_get\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.docs.data_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.docs.disk_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.spatial.data_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.spatial.disk_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.views.data_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.views.disk_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.cpu_utilization_rate.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.current_items.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.current_items.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.ep_bg_fetched\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.get_hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.mcd_memory.allocated.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.mcd_memory.reserved.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.memory.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.memory.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.memory.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.swap.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.swap.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.uptime.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.vb_replica_curr_items\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.auth_cache_hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.auth_cache_misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.database_reads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.database_writes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.open_databases\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.open_os_files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.request_time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd.bulk_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd.clients_requesting_changes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd.temporary_view_reads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd.view_reads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.COPY\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.DELETE\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.GET\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.HEAD\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.POST\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.PUT\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.200\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.201\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.202\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.301\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.304\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.400\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.401\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.403\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.404\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.405\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.409\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.412\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.500\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.data\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.ttl\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.header_flags\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.op_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.registered_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.resolved_ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.response_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.command\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.ip_addresses\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.size.root_fs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.size.rw\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.tags\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.kernel.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.kernel.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.system.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.system.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.total.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.user.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.user.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.read.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.read.rate\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.reads\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.summary.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.summary.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.summary.rate\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.total\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.write.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.write.rate\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.writes\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.action\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.actor.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.from\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.event.end_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.event.exit_code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.event.output\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.event.start_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.failingstreak\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.id.current\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.id.parent\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.size.regular\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.size.virtual\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.tags\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.containers.paused\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.containers.running\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.containers.stopped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.containers.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.images\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.commit.peak\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.commit.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.fail.count\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.private_working_set.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.rss.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.rss.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.usage.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.usage.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.usage.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.in.dropped\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.in.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.in.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.inbound.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.inbound.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.inbound.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.inbound.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.interface\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.out.dropped\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.out.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.out.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.outbound.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.outbound.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.outbound.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.outbound.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ecs.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.follower.global_checkpoint\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.follower.index\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.follower.operations_written\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.follower.shard.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.follower.time_since_last_read.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.leader.index\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.leader.max_seq_no\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.pending_task.insert_order\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.pending_task.priority\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.pending_task.source\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.pending_task.time_in_queue.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.state.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.indices.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.indices.fielddata.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.indices.shards.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.indices.shards.primaries\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.nodes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.nodes.data\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.nodes.master\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.primary\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.source.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.source.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.source.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.stage\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.target.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.target.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.target.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.primaries.docs.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.primaries.docs.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.primaries.segments.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.primaries.segments.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.primaries.store.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.total.docs.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.total.docs.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.total.segments.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.total.segments.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.total.store.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.total.docs.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.total.docs.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.total.segments.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.total.segments.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.total.store.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ml.job.data_counts.invalid_date_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ml.job.data_counts.processed_record_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ml.job.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ml.job.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.jvm.memory.heap.init.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.jvm.memory.heap.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.jvm.memory.nonheap.init.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.jvm.memory.nonheap.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.jvm.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.process.mlockall\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.fs.summary.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.fs.summary.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.fs.summary.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.indices.docs.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.indices.docs.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.indices.segments.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.indices.segments.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.indices.store.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.gc.collectors.old.collection.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.gc.collectors.old.collection.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.gc.collectors.young.collection.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.gc.collectors.young.collection.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.old.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.old.peak.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.old.peak_max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.old.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.survivor.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak_max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.survivor.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.young.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.young.peak.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.young.peak_max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.young.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.shard.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.shard.primary\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.shard.relocating_node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.shard.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.cluster_manager.active_clusters\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.cluster_manager.cluster_added\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.cluster_manager.cluster_modified\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.cluster_manager.cluster_removed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.cluster_manager.warming_clusters\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.filesystem.flushed_by_timer\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.filesystem.reopen_failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.filesystem.write_buffered\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.filesystem.write_completed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.filesystem.write_total_buffered\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.header_overflow\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.headers_cb_no_stream\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.rx_messaging_error\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.rx_reset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.too_many_header_frames\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.trailers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.tx_reset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.listener_added\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.listener_create_failure\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.listener_create_success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.listener_modified\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.listener_removed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.total_listeners_active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.total_listeners_draining\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.total_listeners_warming\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.admin_overrides_active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.load_error\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.load_success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.num_keys\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.override_dir_exists\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.override_dir_not_exists\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.days_until_first_cert_expiring\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.hot_restart_epoch\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.live\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.memory_allocated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.memory_heap_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.parent_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.total_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.version\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.watchdog_mega_miss\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.watchdog_miss\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.stats.overflow\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"error.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.api_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.disk.backend_commit_duration.ns.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.disk.backend_commit_duration.ns.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.disk.mvcc_db_total_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.disk.wal_fsync_duration.ns.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.disk.wal_fsync_duration.ns.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.counts.followers.counts.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.counts.followers.counts.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.latency.follower.latency.standardDeviation\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.latency.followers.latency.average\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.latency.followers.latency.current\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.latency.followers.latency.maximum\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.latency.followers.latency.minimum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.leader\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.memory.go_memstats_alloc.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.network.client_grpc_received.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.network.client_grpc_sent.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.leaderinfo.leader\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.leaderinfo.starttime\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.leaderinfo.uptime\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.recv.appendrequest.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.recv.bandwidthrate\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.recv.pkgrate\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.send.appendrequest.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.send.bandwidthrate\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.send.pkgrate\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.starttime\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.grpc_handled.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.grpc_started.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.has_leader\",\"type\":\"number\",\"esTypes\":[\"byte\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.leader_changes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.proposals_committed.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.proposals_failed.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.proposals_pending.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.compareanddelete.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.compareanddelete.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.compareandswap.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.compareandswap.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.create.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.create.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.delete.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.delete.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.expire.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.gets.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.gets.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.sets.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.sets.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.update.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.update.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.watchers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.action\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.category\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.dataset\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.duration\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.end\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.kind\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.module\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.outcome\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.provider\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.risk_score\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.risk_score_norm\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.sequence\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.severity\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.timezone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.accessed\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.ctime\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.device\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.directory\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.extension\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.gid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.group\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha512\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.inode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.mode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.mtime\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.owner\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.target_path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.uid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.expvar.cmdline\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.allocated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.frees\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.idle\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.mallocs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.cmdline\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.cpu_fraction\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.next_gc_limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.pause.avg.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.pause.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.pause.max.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.pause.sum.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.total_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.total_pause.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.obtained\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.released\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.stack\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"graphite.server.example\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.compress.bps.in\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.compress.bps.out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.compress.bps.rate_limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.hard_max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.rate.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.ssl.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.ssl.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.ssl.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.idle.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.memory.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.pipes.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.pipes.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.pipes.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.process_num\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.processes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.requests.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.requests.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.run_queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.session.rate.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.session.rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.session.rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.sockets.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.backend.key_rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.backend.key_rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.cache_misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.cached_lookups\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.frontend.key_rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.frontend.key_rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.frontend.session_reuse.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.rate.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.tasks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ulimit_n\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.uptime.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.zlib_mem_usage.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.zlib_mem_usage.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.agent.last\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.down\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.duration\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.health.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.health.last\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.client.aborted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.component_type\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.compressor.bypassed.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.compressor.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.compressor.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.compressor.response.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.connection.retried\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.connection.time.avg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.connection.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.downtime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.last_change\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.proxy.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.proxy.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.queue.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.queue.time.avg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.connection.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.denied\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.queued.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.queued.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.redispatched\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.denied\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.1xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.2xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.3xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.4xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.5xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.other\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.time.avg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.selected.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.server.aborted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.server.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.server.backup\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.server.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.service_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.rate.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.throttle.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.tracked.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.weight\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.sha512\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.architecture\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.containerized\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.build\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.codename\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.body.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.body.content\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.method\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.referrer\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.body.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.body.content\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.phrase\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.status_code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.agent.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.agent.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.secured\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.server.product\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.server.vendor\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.server.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.url\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.broker.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.broker.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.broker.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.broker.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.client.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.client.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.client.member_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.error.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.meta\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.partition\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.topic\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.broker.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.broker.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.offset.newest\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.offset.oldest\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.error.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.insync_replica\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.is_leader\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.isr\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.leader\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.replica\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.topic.error.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.topic.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.topic_broker_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.topic_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.topic.error.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.topic.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.concurrent_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.host.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.index\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.process.event_loop_delay.ms\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.process.memory.heap.size_limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.process.memory.heap.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.process.memory.heap.uptime.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.process.memory.heap.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.request.disconnects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.request.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.response_time.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.response_time.max.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.snapshot\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.status.metrics.concurrent_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.status.metrics.requests.disconnects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.status.metrics.requests.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.status.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.status.status.overall.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.audit.event.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.audit.rejected.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.client.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.etcd.object.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.request.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.request.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.request.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.request.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.response.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.response.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.process.cpu.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.process.fds.open.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.process.memory.resident.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.process.memory.virtual.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.process.started.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.client\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.component\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.content_type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.current.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.dry_run\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.group\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.handler\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.kind\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.latency.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.latency.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.longrunning.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.method\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.resource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.scope\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.subresource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.verb\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.limit.cores\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.limit.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.request.cores\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.request.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.usage.core.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.usage.limit.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.usage.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.usage.node.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.image\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.inodes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.inodes.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.inodes.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.majorpagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.pagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.request.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.usage.limit.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.usage.node.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.workingset.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.rootfs.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.rootfs.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.rootfs.inodes.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.rootfs.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.status.phase\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.status.ready\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.status.reason\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.status.restarts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.client.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.handler\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.request.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.request.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.request.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.request.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.response.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.response.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.leader.is_master\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.method\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.node.collector.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.node.collector.eviction.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.node.collector.health.pct\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.node.collector.unhealthy.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.process.cpu.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.process.fds.open.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.process.memory.resident.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.process.memory.virtual.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.process.started.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.workqueue.adds.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.workqueue.depth.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.workqueue.longestrunning.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.workqueue.retries.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.workqueue.unfinished.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.zone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.active.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.concurrency\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.created.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.deadline.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.is_suspended\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.last_schedule.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.next_schedule.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.schedule\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.paused\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.replicas.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.replicas.desired\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.replicas.unavailable\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.replicas.updated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.involved_object.api_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.involved_object.kind\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.involved_object.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.involved_object.resource_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.involved_object.uid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.message\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.namespace\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.resource_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.self_link\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.timestamp.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.uid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.reason\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.timestamp.first_occurrence\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.timestamp.last_occurrence\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.namespace\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.cpu.allocatable.cores\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.cpu.capacity.cores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.cpu.usage.core.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.cpu.usage.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.inodes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.inodes.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.inodes.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.allocatable.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.majorpagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.pagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.workingset.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.network.rx.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.network.rx.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.network.tx.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.network.tx.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.pod.allocatable.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.pod.capacity.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.runtime.imagefs.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.runtime.imagefs.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.runtime.imagefs.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.status.ready\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.status.unschedulable\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.cpu.usage.limit.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.cpu.usage.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.cpu.usage.node.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.host_ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.major_page_faults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.page_faults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.usage.limit.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.usage.node.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.working_set.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.network.rx.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.network.rx.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.network.tx.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.network.tx.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.status.phase\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.status.ready\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.status.scheduled\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.uid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.client.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.handler\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.request.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.request.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.request.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.request.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.response.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.response.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.method\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.process.cpu.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.process.fds.open.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.process.memory.resident.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.process.memory.virtual.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.process.started.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.sync.networkprogramming.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.sync.networkprogramming.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.sync.rules.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.sync.rules.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.replicas.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.replicas.desired\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.replicas.labeled\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.replicas.observed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.replicas.ready\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.client.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.handler\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.request.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.request.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.request.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.request.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.response.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.response.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.leader.is_master\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.method\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.operation\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.process.cpu.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.process.fds.open.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.process.memory.resident.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.process.memory.virtual.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.process.started.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.result\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.duration.seconds.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.duration.seconds.sum\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.e2e.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.e2e.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.pod.attempts.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.pod.preemption.victims.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.created\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.generation.desired\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.generation.observed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.replicas.desired\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.replicas.observed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.container\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.cpu.usage.core.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.cpu.usage.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.memory.majorpagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.memory.pagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.memory.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.memory.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.memory.workingset.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.inodes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.inodes.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.inodes.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kvm.dommemstat.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kvm.dommemstat.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kvm.dommemstat.stat.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kvm.dommemstat.stat.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.level\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.logger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"logstash.node.jvm.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"logstash.node.stats.events.filtered\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"logstash.node.stats.events.in\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"logstash.node.stats.events.out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.bytes.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.bytes.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.cmd.get\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.cmd.set\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.connections.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.connections.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.evictions\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.get.hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.get.misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.items.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.items.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.threads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.uptime.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.written.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"metricset.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"metricset.period\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.collection\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.commands.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.commands.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.db\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.getmore.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.getmore.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.insert.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.insert.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.lock.read.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.lock.read.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.lock.write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.lock.write.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.queries.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.queries.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.remove.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.remove.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.total.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.total.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.update.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.update.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.avg_obj_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.collections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.data_file_version.major\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.data_file_version.minor\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.data_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.db\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.extent_free_list.num\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.extent_free_list.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.file_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.index_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.indexes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.ns_size_mb.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.num_extents\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.storage_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.aggregate.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.aggregate.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.build_info.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.build_info.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.coll_stats.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.coll_stats.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.connection_pool_stats.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.connection_pool_stats.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.count.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.count.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.db_stats.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.db_stats.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.distinct.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.distinct.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.find.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.find.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_cmd_line_opts.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_cmd_line_opts.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_last_error.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_last_error.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_log.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_log.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_more.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_more.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_parameter.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_parameter.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.host_info.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.host_info.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.insert.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.insert.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.is_master.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.is_master.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.is_self.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.is_self.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.last_collections.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.last_collections.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.last_commands.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.last_commands.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.list_databased.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.list_databased.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.list_indexes.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.list_indexes.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.ping.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.ping.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.profile.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.profile.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_get_rbid.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_get_rbid.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_get_status.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_get_status.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_heartbeat.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_heartbeat.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_update_position.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_update_position.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.server_status.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.server_status.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.update.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.update.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.whatsmyuri.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.whatsmyuri.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.cursor.open.no_timeout\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.cursor.open.pinned\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.cursor.open.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.cursor.timed_out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.document.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.document.inserted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.document.returned\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.document.updated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.get_last_error.write_timeouts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.get_last_error.write_wait.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.get_last_error.write_wait.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.operation.scan_and_order\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.operation.write_conflicts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.query_executor.scanned_documents\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.query_executor.scanned_indexes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.apply.attempts_to_become_secondary\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.apply.batches.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.apply.batches.time.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.apply.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.buffer.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.buffer.max_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.buffer.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.cancels\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.event_created\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.event_wait\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.dbwork\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.exclusive\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.netcmd\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.work\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.work_at\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.waits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.event_waiters\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.network_interface\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.in_progress.dbwork\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.in_progress.exclusive\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.in_progress.network\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.ready\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.sleepers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.shutting_down\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.unsignaled_events\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.initial_sync.completed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.initial_sync.failed_attempts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.initial_sync.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.network.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.network.getmores.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.network.getmores.time.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.network.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.network.reders_created\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.preload.docs.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.preload.docs.time.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.preload.indexes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.preload.indexes.time.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.storage.free_list.search.bucket_exhausted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.storage.free_list.search.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.storage.free_list.search.scanned\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.ttl.deleted_documents\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.ttl.passes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.headroom.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.headroom.min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.lag.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.lag.min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.arbiter.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.arbiter.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.down.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.down.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.primary.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.primary.optime\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.recovering.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.recovering.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.rollback.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.rollback.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.secondary.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.secondary.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.secondary.optimes\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.startup2.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.startup2.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.unhealthy.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.unhealthy.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.unknown.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.unknown.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.oplog.first.timestamp\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.oplog.last.timestamp\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.oplog.size.allocated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.oplog.size.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.oplog.window\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.optimes.applied\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.optimes.durable\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.optimes.last_committed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.server_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.set_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.asserts.msg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.asserts.regular\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.asserts.rollovers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.asserts.user\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.asserts.warning\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.background_flushing.average.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.background_flushing.flushes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.background_flushing.last.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.background_flushing.last_finished\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.background_flushing.total.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.connections.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.connections.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.connections.total_created\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.extra_info.heap_usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.extra_info.page_faults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.active_clients.readers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.active_clients.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.active_clients.writers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.current_queue.readers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.current_queue.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.current_queue.writers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.total_time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.commits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.commits_in_write_lock\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.compression\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.early_commits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.journaled.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.commits.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.commits_in_write_lock.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.dt.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.prep_log_buffer.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.remap_private_view.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.write_to_data_files.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.write_to_journal.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.write_to_data_files.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.local_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.acquire.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.acquire.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.acquire.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.acquire.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.deadlock.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.deadlock.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.deadlock.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.deadlock.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.us.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.us.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.us.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.us.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.acquire.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.acquire.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.acquire.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.acquire.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.deadlock.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.deadlock.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.deadlock.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.deadlock.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.us.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.us.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.us.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.us.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.acquire.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.acquire.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.acquire.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.acquire.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.deadlock.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.deadlock.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.deadlock.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.deadlock.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.us.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.us.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.us.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.us.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.acquire.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.acquire.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.acquire.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.acquire.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.deadlock.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.deadlock.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.deadlock.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.deadlock.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.us.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.us.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.us.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.us.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.acquire.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.acquire.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.acquire.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.acquire.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.deadlock.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.deadlock.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.deadlock.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.deadlock.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.us.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.us.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.us.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.us.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.memory.bits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.memory.mapped.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.memory.mapped_with_journal.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.memory.resident.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.memory.virtual.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.network.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.network.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.network.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.command\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.delete\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.getmore\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.insert\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.query\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.update\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.commands.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.commands.latency\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.reads.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.reads.latency\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.writes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.writes.latency\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.command\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.delete\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.getmore\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.insert\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.query\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.update\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.process\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.storage_engine.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.uptime.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.dirty.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.maximum.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.pages.evicted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.pages.read\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.pages.write\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.read.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.read.out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.read.total_tickets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.write.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.write.out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.write.total_tickets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.flushes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.max_file_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.scans\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.syncs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.writes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.write_backs_queued\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.database.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.database.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.active_temp_tables\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.batch_requests_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.buffer.cache_hit.pct\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.buffer.checkpoint_pages_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.buffer.database_pages\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.buffer.page_life_expectancy.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.buffer.target_pages\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.compilations_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.connections_reset_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.lock_waits_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.logins_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.logouts_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.page_splits_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.recompilations_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.transactions\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.user_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.space_usage.since_last_backup.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.space_usage.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.space_usage.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.space_usage.used.pct\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.stats.active_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.stats.backup_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.stats.recovery_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.stats.since_last_checkpoint.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.stats.total_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"munin.plugin.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.apply.oooe\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.apply.oool\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.apply.window\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cert.deps_distance\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cert.index_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cert.interval\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cluster.conf_id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cluster.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cluster.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.commit.oooe\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.commit.window\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.connected\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.evs.evict\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.evs.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.flow_ctl.paused\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.flow_ctl.paused_ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.flow_ctl.recv\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.flow_ctl.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.last_committed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.bf_aborts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.cert_failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.commits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.recv.queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.recv.queue_avg\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.recv.queue_max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.recv.queue_min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.replays\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.send.queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.send.queue_avg\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.send.queue_max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.send.queue_min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.ready\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.received.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.received.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.data_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.keys\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.keys_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.other_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.aborted.clients\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.aborted.connects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.binlog.cache.disk_use\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.binlog.cache.use\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.bytes.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.bytes.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.command.delete\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.command.insert\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.command.select\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.command.update\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.created.tmp.disk_tables\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.created.tmp.files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.created.tmp.tables\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.delayed.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.delayed.insert_threads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.delayed.writes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.flush_commands\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.commit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.delete\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.external_lock\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.mrr_init\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.prepare\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.first\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.key\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.last\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.next\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.prev\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.rnd\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.rnd_next\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.rollback\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.savepoint\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.savepoint_rollback\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.update\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.write\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.bytes.data\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.bytes.dirty\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.dump_status\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.load_status\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.data\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.dirty\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.flushed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.latched\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.misc\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pool.reads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pool.resize_status\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pool.wait_free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.read.ahead\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.read.ahead_evicted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.read.ahead_rnd\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.read.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.write_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.max_used_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.open.files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.open.streams\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.open.tables\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.opened_tables\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.queries\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.questions\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.threads.cached\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.threads.connected\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.threads.created\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.threads.running\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.connections.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.routes.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.server.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.server.time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.cores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.cpu\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.http.req_stats.uri.connz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.http.req_stats.uri.root\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.http.req_stats.uri.routez\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.http.req_stats.uri.subsz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.http.req_stats.uri.varz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.in.messages\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.mem.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.out.messages\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.remotes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.slow_consumers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.total_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.cache.fanout.avg\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.cache.fanout.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.cache.hit_rate\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.cache.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.inserts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.matches\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.removes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.application\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.community_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.direction\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.forwarded_ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.iana_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.protocol\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.transport\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.accepts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.handled\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.reading\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.waiting\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.writing\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.serial_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.vendor\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.buffer_pool\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cache.buffer.hit.pct\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cache.get.consistent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cache.get.db_blocks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cache.physical_reads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.avg\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.cache_hit.pct\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.max\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.opened.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.opened.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.parse.real\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.parse.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.session.cache_hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.total\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.io_reloads\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.lock_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.machine\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.pin_requests\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.username\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.online_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.size.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.size.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.space.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.space.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.space.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"organization.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.connections.accepted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.connections.listen_queue_len\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.connections.max_listen_queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.connections.queued\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.process_manager\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.processes.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.processes.idle\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.processes.max_active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.processes.max_children_reached\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.processes.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.slow_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.start_since\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.last_request_cpu\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.last_request_memory\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.request_duration\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.script\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.start_since\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.application_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.backend_start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.client.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.client.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.client.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.database.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.database.oid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.query\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.query_start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.state_change\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.transaction_start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.user.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.waiting\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.allocated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.backend\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.backend_fsync\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.checkpoints\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.clean\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.clean_full\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.checkpoints.requested\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.checkpoints.scheduled\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.checkpoints.times.sync.ms\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.checkpoints.times.write.ms\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.stats_reset\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.blocks.hit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.blocks.read\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.blocks.time.read.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.blocks.time.write.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.conflicts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.deadlocks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.number_of_backends\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.oid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.rows.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.rows.fetched\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.rows.inserted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.rows.returned\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.rows.updated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.stats_reset\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.temporary.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.temporary.files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.transactions.commit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.transactions.rollback\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.database.oid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.calls\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.local.dirtied\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.local.hit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.local.read\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.local.written\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.shared.dirtied\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.shared.hit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.shared.read\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.shared.written\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.temp.read\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.temp.written\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.rows\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.time.max.ms\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.time.mean.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.time.min.ms\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.time.stddev.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.time.total.ms\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.user.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.args\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.executable\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha512\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pgid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.ppid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.title\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.working_directory\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.channel_max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.channels\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.client_provided.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.frame_max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.octet_count.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.octet_count.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.packet_count.pending\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.packet_count.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.packet_count.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.peer.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.peer.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.auto_delete\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.durable\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.internal\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.messages.publish_in.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.messages.publish_in.details.rate\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.messages.publish_out.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.messages.publish_out.details.rate\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.disk.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.disk.free.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.fd.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.fd.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.gc.num.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.gc.reclaimed.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.file_handle.open_attempt.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.file_handle.open_attempt.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.read.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.read.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.reopen.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.seek.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.seek.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.sync.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.sync.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.write.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.mem.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.mem.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.mnesia.disk.tx.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.mnesia.ram.tx.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.msg.store_read.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.msg.store_write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.proc.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.proc.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.processors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.queue.index.journal_write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.queue.index.read.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.queue.index.write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.run.queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.socket.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.socket.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.arguments.max_priority\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.auto_delete\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.consumers.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.consumers.utilisation.pct\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.disk.reads.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.disk.writes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.durable\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.exclusive\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.persistent.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.ready.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.ready.details.rate\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.total.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.total.details.rate\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.unacknowledged.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.unacknowledged.details.rate\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.vhost\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.biggest_input_buf\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.blocked\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.connected\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.longest_output_list\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.max_input_buffer\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.max_output_buffer\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.cluster.enabled\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.cpu.used.sys\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.cpu.used.sys_children\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.cpu.used.user\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.cpu.used.user_children\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.active_defrag.is_running\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.allocated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.fragmentation.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.fragmentation.ratio\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.resident\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.rss.ratio\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.fragmentation.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.fragmentation.ratio\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.max.policy\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.max.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.used.dataset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.used.lua\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.used.peak\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.used.rss\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.used.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.bgrewrite.last_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.buffer.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.copy_on_write.last_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.enabled\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.fsync.delayed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.fsync.pending\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.rewrite.buffer.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.rewrite.current_time.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.rewrite.in_progress\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.rewrite.last_time.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.rewrite.scheduled\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.size.base\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.size.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.write.last_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.loading\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.bgsave.current_time.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.bgsave.in_progress\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.bgsave.last_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.bgsave.last_time.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.copy_on_write.last_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.last_save.changes_since\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.last_save.time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.backlog.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.backlog.first_byte_offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.backlog.histlen\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.backlog.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.connected_slaves\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.last_io_seconds_ago\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.link_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.second_offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.sync.in_progress\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.sync.last_io_seconds_ago\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.sync.left_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master_offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.role\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.slave.is_readonly\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.slave.offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.slave.priority\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.arch_bits\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.build_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.config_file\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.gcc_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.git_dirty\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.git_sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.hz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.lru_clock\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.mode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.multiplexing_api\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.run_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.tcp_port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.slowlog.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.active_defrag.hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.active_defrag.key_hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.active_defrag.key_misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.active_defrag.misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.commands_processed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.connections.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.connections.rejected\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.instantaneous.input_kbps\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.instantaneous.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.instantaneous.output_kbps\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.keys.evicted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.keys.expired\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.keyspace.hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.keyspace.misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.latest_fork_usec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.migrate_cached_sockets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.net.input.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.net.output.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.pubsub.channels\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.pubsub.patterns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.slave_expires_tracked_keys\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.sync.full\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.sync.partial.err\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.sync.partial.ok\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.key.expire.ttl\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.key.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.key.length\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.key.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.key.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.keyspace.avg_ttl\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.keyspace.expires\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.keyspace.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.keyspace.keys\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"related.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.ephemeral_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.idle.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.idle.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.iowait.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.iowait.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.irq.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.irq.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.nice.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.nice.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.softirq.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.softirq.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.steal.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.steal.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.system.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.system.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.user.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.user.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.cores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.idle.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.idle.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.idle.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.iowait.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.iowait.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.iowait.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.irq.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.irq.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.irq.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.nice.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.nice.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.nice.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.softirq.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.softirq.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.softirq.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.steal.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.steal.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.steal.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.system.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.system.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.system.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.total.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.total.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.user.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.user.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.user.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.io.time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.await\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.busy\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.queue.avg_size\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.read.await\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.read.per_sec.bytes\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.read.request.merges_per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.read.request.per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.request.avg_size\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.service_time\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.write.await\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.write.per_sec.bytes\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.write.request.merges_per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.write.request.per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.read.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.read.time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.serial_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.write.time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.entropy.available_bits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.entropy.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.device_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.free_files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.mount_point\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.used.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.fsstat.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.fsstat.total_files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.fsstat.total_size.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.fsstat.total_size.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.fsstat.total_size.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.1\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.15\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.5\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.cores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.norm.1\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.norm.15\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.norm.5\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.actual.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.actual.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.actual.used.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.default_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.reserved\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.surplus\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.used.pct\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.swap.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.swap.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.swap.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.swap.used.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.used.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.in.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.in.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.in.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.out.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.out.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.out.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.blkio.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.blkio.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.blkio.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.blkio.total.ios\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.cfs.period.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.cfs.quota.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.cfs.shares\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.rt.period.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.rt.runtime.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.stats.periods\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.stats.throttled.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.stats.throttled.periods\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpuacct.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpuacct.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpuacct.stats.system.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpuacct.stats.user.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpuacct.total.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem.usage.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem_tcp.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem_tcp.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem_tcp.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem_tcp.usage.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.mem.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.mem.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.mem.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.mem.usage.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.memsw.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.memsw.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.memsw.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.memsw.usage.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.active_anon.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.active_file.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.cache.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.hierarchical_memory_limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.hierarchical_memsw_limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.inactive_anon.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.inactive_file.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.major_page_faults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.mapped_file.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.page_faults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.pages_in\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.pages_out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.rss_huge.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.swap.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.unevictable.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cmdline\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.system.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.total.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.total.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.total.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.total.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.user.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.fd.limit.hard\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.fd.limit.soft\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.fd.open\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.memory.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.memory.rss.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.memory.share\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.memory.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.dead\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.idle\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.running\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.sleeping\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.stopped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.unknown\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.zombie\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.blocks.synced\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.blocks.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.disks.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.disks.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.disks.spare\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.disks.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.level\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.sync_action\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.local.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.local.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.process.cmdline\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.remote.etld_plus_one\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.remote.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.remote.host_error\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.remote.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.remote.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.all.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.all.listening\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.close_wait\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.established\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.listening\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.orphan\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.time_wait\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.memory\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.udp.all.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.udp.memory\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.uptime.duration.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"timeseries.instance\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tracing.trace.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tracing.transaction.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"traefik.health.response.avg_time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"traefik.health.response.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"traefik.health.uptime.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.fragment\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.password\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.query\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.scheme\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.username\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.device.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.read_errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.requests.offloaded\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.requests.routed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.requests.static\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.requests.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.worker_pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.write_errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.total.exceptions\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.total.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.total.read_errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.total.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.total.write_errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.accepting\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.avg_rt\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.delta_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.exceptions\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.harakiri_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.respawn_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.rss\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.running_time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.signal_queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.signals\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.tx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.vsz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.capacity.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.capacity.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.capacity.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.capacity.used.pct\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.fstype\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.cpu.free.mhz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.cpu.total.mhz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.cpu.used.mhz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.memory.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.memory.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.memory.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.network_names\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.cpu.used.mhz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.memory.free.guest.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.memory.total.guest.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.memory.used.guest.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.memory.used.host.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.network_names\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.os\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.display_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.exit_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.path_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.start_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.start_type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.uptime.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.connection.interest_ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.connection.queued\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.connection.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.connection.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.approximate_data_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.ephemerals_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.followers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.latency.avg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.latency.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.latency.min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.max_file_descriptor_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.num_alive_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.open_file_descriptor_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.outstanding_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.packets.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.packets.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.pending_syncs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.server_state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.synced_followers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.watch_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.znode_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.epoch\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.latency.avg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.latency.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.latency.min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.mode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.node_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.outstanding\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.version_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.zxid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", - "timeFieldName" : "@timestamp", - "title" : "metricbeat-*" - }, - "type" : "index-pattern", - "migrationVersion" : { - "index-pattern" : "7.6.0" - }, - "updated_at" : "2020-01-22T15:34:59.061Z" + "id": "index-pattern:metricbeat-*", + "index": ".kibana_1", + "source": { + "index-pattern": { + "fieldFormatMap": "{\"aerospike.namespace.device.available.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.device.free.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.device.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.device.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.free.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.memory.used.data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.index.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.sindex.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"aws.rds.disk_usage.bin_log.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.free_local_storage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.free_storage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.freeable_memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.latency.commit\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.ddl\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.dml\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.insert\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.read\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.select\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.update\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.write\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.replica_lag.sec\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.swap_usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.volume_used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_daily_storage.bucket.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.downloaded.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.latency.first_byte.ms\":{\"id\":\"duration\",\"params\":{}},\"aws.s3_request.latency.total_request.ms\":{\"id\":\"duration\",\"params\":{}},\"aws.s3_request.requests.select_returned.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.requests.select_scanned.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.uploaded.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.sqs.oldest_message_age.sec\":{\"id\":\"duration\",\"params\":{}},\"aws.sqs.sent_message_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.degraded.ratio\":{\"id\":\"percent\",\"params\":{}},\"ceph.cluster_status.misplace.ratio\":{\"id\":\"percent\",\"params\":{}},\"ceph.cluster_status.pg.avail_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.data_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.total_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.used_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.traffic.read_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.traffic.write_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.log.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.misc.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.sst.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.total.byte\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.used.byte\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.used.pct\":{\"id\":\"percent\",\"params\":{}},\"ceph.pool_disk.stats.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.pool_disk.stats.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"client.bytes\":{\"id\":\"bytes\",\"params\":{}},\"client.nat.port\":{\"id\":\"string\",\"params\":{}},\"client.port\":{\"id\":\"string\",\"params\":{}},\"coredns.stats.dns.request.duration.ns.sum\":{\"id\":\"duration\",\"params\":{}},\"couchbase.bucket.data.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.disk.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.quota.ram.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.quota.use.pct\":{\"id\":\"percent\",\"params\":{}},\"couchbase.cluster.hdd.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.quota.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.used.by_data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.total.per_node.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.total.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.used.per_node.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.used.by_data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.couch.docs.data_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.couch.docs.disk_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.mcd_memory.allocated.bytes\":{\"id\":\"bytes\",\"params\":{}},\"destination.bytes\":{\"id\":\"bytes\",\"params\":{}},\"destination.nat.port\":{\"id\":\"string\",\"params\":{}},\"destination.port\":{\"id\":\"string\",\"params\":{}},\"docker.cpu.core.*.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.kernel.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.system.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.user.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.diskio.summary.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.commit.peak\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.commit.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.limit\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.private_working_set.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.rss.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.memory.rss.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.usage.max\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.usage.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.memory.usage.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.inbound.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.outbound.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.primaries.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.primaries.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.total.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.total.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.total.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.total.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.heap.init.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.heap.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.nonheap.init.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.nonheap.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.indices.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.disk.mvcc_db_total_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.memory.go_memstats_alloc.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.network.client_grpc_received.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.network.client_grpc_sent.bytes\":{\"id\":\"bytes\",\"params\":{}},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\",\"params\":{}},\"event.severity\":{\"id\":\"string\",\"params\":{}},\"golang.heap.allocations.active\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.allocated\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.idle\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.total\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.gc.next_gc_limit\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.obtained\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.released\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.stack\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.total\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.info.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"haproxy.info.memory.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.info.ssl.frontend.session_reuse.pct\":{\"id\":\"percent\",\"params\":{}},\"haproxy.stat.compressor.bypassed.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.response.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.throttle.pct\":{\"id\":\"percent\",\"params\":{}},\"http.request.body.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.request.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.body.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.status_code\":{\"id\":\"string\",\"params\":{}},\"kibana.stats.process.memory.heap.size_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kibana.stats.process.memory.heap.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kibana.stats.process.memory.heap.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.cpu.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.cpu.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.logs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.logs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.logs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.request.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.memory.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.allocatable.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.network.rx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.network.tx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.cpu.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.cpu.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.working_set.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.network.rx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.network.tx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.avg_obj_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.data_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.extent_free_list.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.file_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.index_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.storage_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.replstatus.headroom.max\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.headroom.min\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.lag.max\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.lag.min\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.oplog.size.allocated\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.replstatus.oplog.size.used\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.extra_info.heap_usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.dirty.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.maximum.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.max_file_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mysql.status.bytes.received\":{\"id\":\"bytes\",\"params\":{}},\"mysql.status.bytes.sent\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.cpu\":{\"id\":\"percent\",\"params\":{}},\"nats.stats.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.mem.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.uptime\":{\"id\":\"duration\",\"params\":{}},\"nats.subscriptions.cache.hit_rate\":{\"id\":\"percent\",\"params\":{}},\"network.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"process.pgid\":{\"id\":\"string\",\"params\":{}},\"process.pid\":{\"id\":\"string\",\"params\":{}},\"process.ppid\":{\"id\":\"string\",\"params\":{}},\"process.thread.id\":{\"id\":\"string\",\"params\":{}},\"rabbitmq.connection.frame_max\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.disk.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.disk.free.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.gc.reclaimed.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.io.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.io.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.mem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.queue.consumers.utilisation.pct\":{\"id\":\"percent\",\"params\":{}},\"rabbitmq.queue.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.active\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.allocated\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.fragmentation.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.resident\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.fragmentation.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.max.value\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.dataset\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.lua\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.peak\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.rss\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.value\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.buffer.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.copy_on_write.last_size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.rewrite.buffer.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.rewrite.current_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.aof.rewrite.last_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.aof.size.base\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.size.current\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.rdb.bgsave.current_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.rdb.bgsave.last_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.rdb.copy_on_write.last_size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.replication.backlog.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.replication.master.last_io_seconds_ago\":{\"id\":\"duration\",\"params\":{}},\"redis.info.replication.master.sync.last_io_seconds_ago\":{\"id\":\"duration\",\"params\":{}},\"redis.info.replication.master.sync.left_bytes\":{\"id\":\"bytes\",\"params\":{}},\"server.bytes\":{\"id\":\"bytes\",\"params\":{}},\"server.nat.port\":{\"id\":\"string\",\"params\":{}},\"server.port\":{\"id\":\"string\",\"params\":{}},\"source.bytes\":{\"id\":\"bytes\",\"params\":{}},\"source.nat.port\":{\"id\":\"string\",\"params\":{}},\"source.port\":{\"id\":\"string\",\"params\":{}},\"system.core.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.iowait.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.irq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.nice.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.softirq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.steal.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.system.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.user.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.idle.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.iowait.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.iowait.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.irq.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.irq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.nice.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.nice.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.softirq.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.softirq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.steal.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.steal.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.system.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.system.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.total.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.user.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.user.pct\":{\"id\":\"percent\",\"params\":{}},\"system.diskio.iostat.read.per_sec.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.iostat.write.per_sec.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.entropy.pct\":{\"id\":\"percent\",\"params\":{}},\"system.filesystem.available\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.free\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.total\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.fsstat.total_size.free\":{\"id\":\"bytes\",\"params\":{}},\"system.fsstat.total_size.total\":{\"id\":\"bytes\",\"params\":{}},\"system.fsstat.total_size.used\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.default_size\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.free\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.reserved\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.surplus\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.total\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.swap.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.total\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.total\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.blkio.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.active_anon.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.active_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.cache.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.hierarchical_memory_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.hierarchical_memsw_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.inactive_anon.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.inactive_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.mapped_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.rss_huge.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.swap.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.unevictable.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.memory.rss.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.memory.share\":{\"id\":\"bytes\",\"params\":{}},\"system.process.memory.size\":{\"id\":\"bytes\",\"params\":{}},\"system.socket.summary.tcp.memory\":{\"id\":\"bytes\",\"params\":{}},\"system.socket.summary.udp.memory\":{\"id\":\"bytes\",\"params\":{}},\"system.uptime.duration.ms\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"milliseconds\"}},\"url.port\":{\"id\":\"string\",\"params\":{}},\"vsphere.datastore.capacity.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.used.pct\":{\"id\":\"percent\",\"params\":{}},\"vsphere.host.memory.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.host.memory.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.host.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.free.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.total.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.used.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.used.host.bytes\":{\"id\":\"bytes\",\"params\":{}},\"windows.service.uptime.ms\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"milliseconds\"}}}", + "timeFieldName": "@timestamp", + "title": "metricbeat-*" + }, + "migrationVersion": { + "index-pattern": "7.11.0" + }, + "references": [ + ], + "type": "index-pattern", + "updated_at": "2020-01-22T15:34:59.061Z" } } } @@ -122,22 +125,19 @@ { "type": "doc", "value": { - "type": "_doc", - "index" : ".kibana", - "type": "doc", - "id" : "custom-space:index-pattern:metricbeat-*", - "source" : { - "index-pattern" : { - "fieldFormatMap" : "{\"aerospike.namespace.device.available.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.device.free.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.device.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.device.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.free.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.memory.used.data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.index.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.sindex.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"aws.rds.disk_usage.bin_log.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.free_local_storage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.free_storage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.freeable_memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.latency.commit\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.ddl\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.dml\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.insert\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.read\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.select\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.update\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.write\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.replica_lag.sec\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.swap_usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.volume_used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_daily_storage.bucket.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.downloaded.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.latency.first_byte.ms\":{\"id\":\"duration\",\"params\":{}},\"aws.s3_request.latency.total_request.ms\":{\"id\":\"duration\",\"params\":{}},\"aws.s3_request.requests.select_returned.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.requests.select_scanned.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.uploaded.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.sqs.oldest_message_age.sec\":{\"id\":\"duration\",\"params\":{}},\"aws.sqs.sent_message_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.degraded.ratio\":{\"id\":\"percent\",\"params\":{}},\"ceph.cluster_status.misplace.ratio\":{\"id\":\"percent\",\"params\":{}},\"ceph.cluster_status.pg.avail_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.data_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.total_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.used_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.traffic.read_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.traffic.write_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.log.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.misc.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.sst.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.total.byte\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.used.byte\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.used.pct\":{\"id\":\"percent\",\"params\":{}},\"ceph.pool_disk.stats.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.pool_disk.stats.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"client.bytes\":{\"id\":\"bytes\",\"params\":{}},\"client.nat.port\":{\"id\":\"string\",\"params\":{}},\"client.port\":{\"id\":\"string\",\"params\":{}},\"coredns.stats.dns.request.duration.ns.sum\":{\"id\":\"duration\",\"params\":{}},\"couchbase.bucket.data.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.disk.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.quota.ram.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.quota.use.pct\":{\"id\":\"percent\",\"params\":{}},\"couchbase.cluster.hdd.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.quota.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.used.by_data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.total.per_node.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.total.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.used.per_node.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.used.by_data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.couch.docs.data_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.couch.docs.disk_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.mcd_memory.allocated.bytes\":{\"id\":\"bytes\",\"params\":{}},\"destination.bytes\":{\"id\":\"bytes\",\"params\":{}},\"destination.nat.port\":{\"id\":\"string\",\"params\":{}},\"destination.port\":{\"id\":\"string\",\"params\":{}},\"docker.cpu.core.*.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.kernel.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.system.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.user.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.diskio.summary.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.commit.peak\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.commit.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.limit\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.private_working_set.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.rss.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.memory.rss.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.usage.max\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.usage.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.memory.usage.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.inbound.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.outbound.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.primaries.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.primaries.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.total.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.total.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.total.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.total.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.heap.init.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.heap.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.nonheap.init.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.nonheap.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.indices.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.disk.mvcc_db_total_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.memory.go_memstats_alloc.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.network.client_grpc_received.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.network.client_grpc_sent.bytes\":{\"id\":\"bytes\",\"params\":{}},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\",\"params\":{}},\"event.severity\":{\"id\":\"string\",\"params\":{}},\"golang.heap.allocations.active\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.allocated\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.idle\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.total\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.gc.next_gc_limit\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.obtained\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.released\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.stack\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.total\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.info.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"haproxy.info.memory.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.info.ssl.frontend.session_reuse.pct\":{\"id\":\"percent\",\"params\":{}},\"haproxy.stat.compressor.bypassed.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.response.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.throttle.pct\":{\"id\":\"percent\",\"params\":{}},\"http.request.body.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.request.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.body.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.status_code\":{\"id\":\"string\",\"params\":{}},\"kibana.stats.process.memory.heap.size_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kibana.stats.process.memory.heap.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kibana.stats.process.memory.heap.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.cpu.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.cpu.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.logs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.logs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.logs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.request.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.memory.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.allocatable.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.network.rx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.network.tx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.cpu.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.cpu.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.working_set.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.network.rx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.network.tx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.avg_obj_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.data_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.extent_free_list.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.file_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.index_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.storage_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.replstatus.headroom.max\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.headroom.min\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.lag.max\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.lag.min\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.oplog.size.allocated\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.replstatus.oplog.size.used\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.extra_info.heap_usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.dirty.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.maximum.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.max_file_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mysql.status.bytes.received\":{\"id\":\"bytes\",\"params\":{}},\"mysql.status.bytes.sent\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.cpu\":{\"id\":\"percent\",\"params\":{}},\"nats.stats.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.mem.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.uptime\":{\"id\":\"duration\",\"params\":{}},\"nats.subscriptions.cache.hit_rate\":{\"id\":\"percent\",\"params\":{}},\"network.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"process.pgid\":{\"id\":\"string\",\"params\":{}},\"process.pid\":{\"id\":\"string\",\"params\":{}},\"process.ppid\":{\"id\":\"string\",\"params\":{}},\"process.thread.id\":{\"id\":\"string\",\"params\":{}},\"rabbitmq.connection.frame_max\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.disk.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.disk.free.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.gc.reclaimed.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.io.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.io.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.mem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.queue.consumers.utilisation.pct\":{\"id\":\"percent\",\"params\":{}},\"rabbitmq.queue.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.active\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.allocated\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.fragmentation.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.resident\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.fragmentation.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.max.value\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.dataset\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.lua\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.peak\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.rss\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.value\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.buffer.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.copy_on_write.last_size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.rewrite.buffer.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.rewrite.current_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.aof.rewrite.last_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.aof.size.base\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.size.current\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.rdb.bgsave.current_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.rdb.bgsave.last_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.rdb.copy_on_write.last_size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.replication.backlog.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.replication.master.last_io_seconds_ago\":{\"id\":\"duration\",\"params\":{}},\"redis.info.replication.master.sync.last_io_seconds_ago\":{\"id\":\"duration\",\"params\":{}},\"redis.info.replication.master.sync.left_bytes\":{\"id\":\"bytes\",\"params\":{}},\"server.bytes\":{\"id\":\"bytes\",\"params\":{}},\"server.nat.port\":{\"id\":\"string\",\"params\":{}},\"server.port\":{\"id\":\"string\",\"params\":{}},\"source.bytes\":{\"id\":\"bytes\",\"params\":{}},\"source.nat.port\":{\"id\":\"string\",\"params\":{}},\"source.port\":{\"id\":\"string\",\"params\":{}},\"system.core.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.iowait.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.irq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.nice.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.softirq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.steal.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.system.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.user.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.idle.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.iowait.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.iowait.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.irq.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.irq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.nice.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.nice.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.softirq.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.softirq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.steal.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.steal.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.system.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.system.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.total.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.user.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.user.pct\":{\"id\":\"percent\",\"params\":{}},\"system.diskio.iostat.read.per_sec.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.iostat.write.per_sec.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.entropy.pct\":{\"id\":\"percent\",\"params\":{}},\"system.filesystem.available\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.free\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.total\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.fsstat.total_size.free\":{\"id\":\"bytes\",\"params\":{}},\"system.fsstat.total_size.total\":{\"id\":\"bytes\",\"params\":{}},\"system.fsstat.total_size.used\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.default_size\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.free\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.reserved\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.surplus\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.total\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.swap.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.total\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.total\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.blkio.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.active_anon.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.active_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.cache.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.hierarchical_memory_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.hierarchical_memsw_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.inactive_anon.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.inactive_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.mapped_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.rss_huge.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.swap.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.unevictable.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.memory.rss.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.memory.share\":{\"id\":\"bytes\",\"params\":{}},\"system.process.memory.size\":{\"id\":\"bytes\",\"params\":{}},\"system.socket.summary.tcp.memory\":{\"id\":\"bytes\",\"params\":{}},\"system.socket.summary.udp.memory\":{\"id\":\"bytes\",\"params\":{}},\"system.uptime.duration.ms\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"milliseconds\"}},\"url.port\":{\"id\":\"string\",\"params\":{}},\"vsphere.datastore.capacity.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.used.pct\":{\"id\":\"percent\",\"params\":{}},\"vsphere.host.memory.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.host.memory.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.host.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.free.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.total.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.used.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.used.host.bytes\":{\"id\":\"bytes\",\"params\":{}},\"windows.service.uptime.ms\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"milliseconds\"}}}", - "fields" : "[{\"name\":\"@timestamp\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"aerospike.namespace.client.delete.error\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.delete.not_found\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.delete.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.delete.timeout\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.read.error\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.read.not_found\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.read.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.read.timeout\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.write.error\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.write.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.client.write.timeout\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.device.available.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.device.free.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.device.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.device.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.hwm_breached\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.memory.free.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.memory.used.data.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.memory.used.index.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.memory.used.sindex.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.memory.used.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.node.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.objects.master\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.objects.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aerospike.namespace.stop_writes\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.ephemeral_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"agent.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.bytes_per_request\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.bytes_per_sec\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.connections.async.closing\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.connections.async.keep_alive\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.connections.async.writing\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.connections.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.cpu.children_system\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.cpu.children_user\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.cpu.load\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.cpu.system\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.cpu.user\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.load.1\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.load.15\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.load.5\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.requests_per_sec\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.closing_connection\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.dns_lookup\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.gracefully_finishing\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.idle_cleanup\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.keepalive\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.logging\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.open_slot\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.reading_request\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.sending_reply\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.starting_up\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.scoreboard.waiting_for_connection\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.total_accesses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.total_kbytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.uptime.server_uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.uptime.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.workers.busy\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"apache.status.workers.idle\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.cloudwatch.namespace\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.cpu.credit_balance\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.cpu.credit_usage\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.cpu.surplus_credit_balance\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.cpu.surplus_credits_charged\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.cpu.total.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.read.bytes_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.read.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.read.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.write.bytes_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.write.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.diskio.write.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.core.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.image.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.monitoring.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.private.dns_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.private.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.public.dns_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.public.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.state.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.state.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.instance.threads_per_core\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.in.bytes_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.in.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.in.packets_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.out.bytes_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.out.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.network.out.packets_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.status.check_failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.status.check_failed_instance\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.ec2.status.check_failed_system\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.cpu.credit_balance\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.cpu.credit_usage\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.cpu.total.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.database_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.db_instance.arn\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.db_instance.class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.db_instance.identifier\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.db_instance.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.deadlocks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.disk_queue_depth\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.disk_usage.bin_log.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.disk_usage.replication_slot.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.disk_usage.transaction_logs.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.failed_sql_server_agent_jobs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.free_local_storage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.free_storage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.freeable_memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.commit\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.ddl\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.dml\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.insert\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.read\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.select\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.update\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.latency.write\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.login_failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.maximum_used_transaction_ids\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.oldest_replication_slot_lag.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.queries\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.read_io.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.replica_lag.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.swap_usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.commit\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.ddl\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.delete\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.dml\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.insert\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.network\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.network_receive\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.network_transmit\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.read\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.select\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.update\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.throughput.write\",\"type\":\"number\",\"esTypes\":[\"float\",\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.transaction_logs_generation\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.transactions.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.transactions.blocked\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.volume_used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.rds.write_io.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_daily_storage.bucket.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_daily_storage.bucket.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_daily_storage.number_of_objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.bucket.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.downloaded.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.errors.4xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.errors.5xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.latency.first_byte.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.latency.total_request.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.delete\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.get\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.head\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.list\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.post\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.put\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.select\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.select_returned.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.select_scanned.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.requests.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.s3_request.uploaded.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.empty_receives\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.delayed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.not_visible\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.messages.visible\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.oldest_message_age.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.queue.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"aws.sqs.sent_message_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.state.management.enabled\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.state.module.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.state.output.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.state.queue.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.acked\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.batches\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.duplicates\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.toomany\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.events.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.read.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.libbeat.output.write.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.runtime.goroutines\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.stats.uptime.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"beat.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_disk.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_disk.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_disk.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_health.overall_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_health.timechecks.epoch\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_health.timechecks.round.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_health.timechecks.round.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.degraded.objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.degraded.ratio\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.degraded.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.misplace.objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.misplace.ratio\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.misplace.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.epoch\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.full\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.nearfull\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.num_in_osds\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.num_osds\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.num_remapped_pgs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.osd.num_up_osds\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg.avail_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg.data_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg.total_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg.used_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg_state.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg_state.state_name\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.pg_state.version\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.traffic.read_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.traffic.read_op_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.traffic.write_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.traffic.write_op_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.cluster_status.version\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.available.kb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.available.pct\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.health\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.last_updated\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.store_stats.last_updated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.store_stats.log.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.store_stats.misc.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.store_stats.sst.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.store_stats.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.total.kb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.monitor_health.used.kb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.device_class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.pg_num\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.total.byte\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.used.byte\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_df.used.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.children\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.crush_weight\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.depth\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.device_class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.exists\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.father\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.primary_affinity\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.reweight\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.osd_tree.type_id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.stats.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.stats.objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.stats.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ceph.pool_disk.stats.used.kb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"client.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.account.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.availability_zone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.image.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.instance.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.instance.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.machine.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.project.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.provider\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cloud.region\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.autopilot.healthy\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.alloc.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.garbage_collector.pause.current.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.garbage_collector.pause.total.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.garbage_collector.runs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.goroutines\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.heap_objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.malloc_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"consul.agent.runtime.sys.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.image.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.image.tag\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"container.runtime\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.cache.hits.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.cache.misses.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.do.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.duration.ns.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.duration.ns.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.request.type.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.response.rcode.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.response.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.dns.response.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.panic.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.proto\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.rcode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.server\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"coredns.stats.zone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.data.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.disk.fetches\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.disk.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.item_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.memory.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.quota.ram.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.quota.use.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.bucket.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.hdd.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.hdd.quota.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.hdd.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.hdd.used.by_data.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.hdd.used.value.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.max_bucket_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.quota.index_memory.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.quota.memory.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.quota.total.per_node.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.quota.total.value.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.quota.used.per_node.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.quota.used.value.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.used.by_data.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.cluster.ram.used.value.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.cmd_get\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.docs.data_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.docs.disk_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.spatial.data_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.spatial.disk_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.views.data_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.couch.views.disk_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.cpu_utilization_rate.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.current_items.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.current_items.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.ep_bg_fetched\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.get_hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.mcd_memory.allocated.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.mcd_memory.reserved.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.memory.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.memory.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.memory.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.swap.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.swap.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.uptime.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchbase.node.vb_replica_curr_items\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.auth_cache_hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.auth_cache_misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.database_reads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.database_writes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.open_databases\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.open_os_files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.couchdb.request_time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd.bulk_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd.clients_requesting_changes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd.temporary_view_reads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd.view_reads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.COPY\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.DELETE\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.GET\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.HEAD\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.POST\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_request_methods.PUT\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.200\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.201\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.202\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.301\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.304\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.400\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.401\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.403\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.404\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.405\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.409\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.412\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"couchdb.server.httpd_status_codes.500\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"destination.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.data\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.ttl\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.answers.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.header_flags\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.op_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.class\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.registered_domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.question.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.resolved_ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.response_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dns.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.command\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.ip_addresses\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.size.root_fs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.size.rw\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.container.tags\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.kernel.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.kernel.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.system.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.system.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.total.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.user.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.cpu.user.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.read.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.read.rate\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.reads\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.summary.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.summary.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.summary.rate\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.total\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.write.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.write.rate\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.diskio.writes\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.action\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.actor.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.from\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.event.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.event.end_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.event.exit_code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.event.output\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.event.start_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.failingstreak\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.healthcheck.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.id.current\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.id.parent\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.size.regular\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.size.virtual\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.image.tags\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.containers.paused\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.containers.running\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.containers.stopped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.containers.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.info.images\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.commit.peak\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.commit.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.fail.count\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.private_working_set.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.rss.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.rss.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.usage.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.usage.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.memory.usage.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.in.dropped\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.in.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.in.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.inbound.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.inbound.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.inbound.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.inbound.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.interface\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.out.dropped\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.out.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.out.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.outbound.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.outbound.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.outbound.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docker.network.outbound.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ecs.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.follower.global_checkpoint\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.follower.index\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.follower.operations_written\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.follower.shard.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.follower.time_since_last_read.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.leader.index\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ccr.leader.max_seq_no\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.pending_task.insert_order\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.pending_task.priority\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.pending_task.source\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.pending_task.time_in_queue.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.state.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.indices.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.indices.fielddata.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.indices.shards.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.indices.shards.primaries\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.nodes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.nodes.data\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.nodes.master\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.cluster.stats.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.primary\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.source.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.source.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.source.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.stage\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.target.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.target.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.target.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.recovery.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.primaries.docs.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.primaries.docs.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.primaries.segments.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.primaries.segments.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.primaries.store.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.total.docs.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.total.docs.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.total.segments.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.total.segments.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.summary.total.store.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.total.docs.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.total.docs.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.total.segments.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.total.segments.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.index.total.store.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ml.job.data_counts.invalid_date_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ml.job.data_counts.processed_record_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ml.job.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.ml.job.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.jvm.memory.heap.init.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.jvm.memory.heap.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.jvm.memory.nonheap.init.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.jvm.memory.nonheap.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.jvm.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.process.mlockall\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.fs.summary.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.fs.summary.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.fs.summary.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.indices.docs.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.indices.docs.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.indices.segments.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.indices.segments.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.indices.store.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.gc.collectors.old.collection.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.gc.collectors.old.collection.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.gc.collectors.young.collection.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.gc.collectors.young.collection.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.old.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.old.peak.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.old.peak_max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.old.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.survivor.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak_max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.survivor.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.young.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.young.peak.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.young.peak_max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.stats.jvm.mem.pools.young.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.node.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.shard.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.shard.primary\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.shard.relocating_node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"elasticsearch.shard.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.cluster_manager.active_clusters\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.cluster_manager.cluster_added\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.cluster_manager.cluster_modified\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.cluster_manager.cluster_removed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.cluster_manager.warming_clusters\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.filesystem.flushed_by_timer\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.filesystem.reopen_failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.filesystem.write_buffered\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.filesystem.write_completed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.filesystem.write_total_buffered\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.header_overflow\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.headers_cb_no_stream\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.rx_messaging_error\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.rx_reset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.too_many_header_frames\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.trailers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.http2.tx_reset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.listener_added\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.listener_create_failure\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.listener_create_success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.listener_modified\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.listener_removed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.total_listeners_active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.total_listeners_draining\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.listener_manager.total_listeners_warming\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.admin_overrides_active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.load_error\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.load_success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.num_keys\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.override_dir_exists\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.runtime.override_dir_not_exists\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.days_until_first_cert_expiring\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.hot_restart_epoch\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.live\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.memory_allocated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.memory_heap_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.parent_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.total_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.version\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.watchdog_mega_miss\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.server.watchdog_miss\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"envoyproxy.server.stats.overflow\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"error.message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"error.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.api_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.disk.backend_commit_duration.ns.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.disk.backend_commit_duration.ns.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.disk.mvcc_db_total_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.disk.wal_fsync_duration.ns.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.disk.wal_fsync_duration.ns.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.counts.followers.counts.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.counts.followers.counts.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.latency.follower.latency.standardDeviation\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.latency.followers.latency.average\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.latency.followers.latency.current\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.latency.followers.latency.maximum\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.followers.latency.followers.latency.minimum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.leader.leader\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.memory.go_memstats_alloc.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.network.client_grpc_received.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.network.client_grpc_sent.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.leaderinfo.leader\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.leaderinfo.starttime\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.leaderinfo.uptime\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.recv.appendrequest.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.recv.bandwidthrate\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.recv.pkgrate\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.send.appendrequest.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.send.bandwidthrate\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.send.pkgrate\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.starttime\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.self.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.grpc_handled.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.grpc_started.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.has_leader\",\"type\":\"number\",\"esTypes\":[\"byte\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.leader_changes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.proposals_committed.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.proposals_failed.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.server.proposals_pending.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.compareanddelete.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.compareanddelete.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.compareandswap.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.compareandswap.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.create.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.create.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.delete.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.delete.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.expire.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.gets.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.gets.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.sets.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.sets.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.update.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.update.success\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"etcd.store.watchers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.action\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.category\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.dataset\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.duration\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.end\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.kind\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.module\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.outcome\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.provider\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.risk_score\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.risk_score_norm\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.sequence\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.severity\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.timezone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"event.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.accessed\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.ctime\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.device\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.directory\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.extension\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.gid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.group\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.hash.sha512\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.inode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.mode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.mtime\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.owner\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.target_path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"file.uid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.expvar.cmdline\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.allocated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.frees\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.idle\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.mallocs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.allocations.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.cmdline\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.cpu_fraction\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.next_gc_limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.pause.avg.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.pause.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.pause.max.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.pause.sum.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.total_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.gc.total_pause.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.obtained\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.released\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.stack\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"golang.heap.system.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"graphite.server.example\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.compress.bps.in\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.compress.bps.out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.compress.bps.rate_limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.hard_max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.rate.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.ssl.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.ssl.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.ssl.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.connection.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.idle.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.memory.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.pipes.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.pipes.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.pipes.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.process_num\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.processes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.requests.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.requests.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.run_queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.session.rate.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.session.rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.session.rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.sockets.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.backend.key_rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.backend.key_rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.cache_misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.cached_lookups\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.frontend.key_rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.frontend.key_rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.frontend.session_reuse.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.rate.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ssl.rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.tasks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.ulimit_n\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.uptime.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.zlib_mem_usage.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.info.zlib_mem_usage.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.agent.last\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.down\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.duration\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.health.fail\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.health.last\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.check.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.client.aborted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.component_type\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.compressor.bypassed.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.compressor.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.compressor.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.compressor.response.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.connection.retried\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.connection.time.avg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.connection.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.downtime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.last_change\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.proxy.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.proxy.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.queue.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.queue.time.avg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.connection.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.denied\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.queued.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.queued.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.redispatched\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.request.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.denied\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.1xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.2xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.3xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.4xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.5xx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.http.other\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.response.time.avg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.selected.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.server.aborted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.server.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.server.backup\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.server.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.service_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.rate.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.rate.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.session.rate.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.throttle.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.tracked.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"haproxy.stat.weight\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hash.sha512\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.architecture\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.containerized\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.build\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.codename\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.body.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.body.content\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.method\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.request.referrer\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.body.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.body.content\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.phrase\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.response.status_code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"http.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.agent.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.agent.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.secured\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.server.product\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.server.vendor\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.server.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"jolokia.url\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.broker.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.broker.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.broker.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.broker.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.client.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.client.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.client.member_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.error.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.meta\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.partition\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.consumergroup.topic\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.broker.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.broker.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.offset.newest\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.offset.oldest\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.error.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.insync_replica\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.is_leader\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.isr\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.leader\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.partition.replica\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.topic.error.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.topic.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.topic_broker_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.partition.topic_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.topic.error.code\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kafka.topic.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.concurrent_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.host.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.index\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.process.event_loop_delay.ms\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.process.memory.heap.size_limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.process.memory.heap.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.process.memory.heap.uptime.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.process.memory.heap.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.request.disconnects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.request.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.response_time.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.response_time.max.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.snapshot\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.stats.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.status.metrics.concurrent_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.status.metrics.requests.disconnects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.status.metrics.requests.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.status.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kibana.status.status.overall.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.audit.event.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.audit.rejected.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.client.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.etcd.object.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.request.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.request.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.request.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.request.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.response.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.http.response.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.process.cpu.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.process.fds.open.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.process.memory.resident.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.process.memory.virtual.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.process.started.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.client\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.component\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.content_type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.current.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.dry_run\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.group\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.handler\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.kind\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.latency.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.latency.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.longrunning.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.method\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.resource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.scope\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.subresource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.verb\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.apiserver.request.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.limit.cores\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.limit.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.request.cores\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.request.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.usage.core.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.usage.limit.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.usage.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.cpu.usage.node.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.image\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.inodes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.inodes.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.inodes.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.logs.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.majorpagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.pagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.request.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.usage.limit.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.usage.node.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.memory.workingset.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.rootfs.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.rootfs.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.rootfs.inodes.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.rootfs.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.status.phase\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.status.ready\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.status.reason\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.container.status.restarts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.client.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.handler\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.request.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.request.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.request.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.request.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.response.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.http.response.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.leader.is_master\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.method\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.node.collector.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.node.collector.eviction.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.node.collector.health.pct\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.node.collector.unhealthy.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.process.cpu.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.process.fds.open.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.process.memory.resident.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.process.memory.virtual.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.process.started.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.workqueue.adds.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.workqueue.depth.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.workqueue.longestrunning.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.workqueue.retries.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.workqueue.unfinished.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.controllermanager.zone\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.active.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.concurrency\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.created.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.deadline.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.is_suspended\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.last_schedule.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.next_schedule.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.cronjob.schedule\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.paused\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.replicas.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.replicas.desired\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.replicas.unavailable\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.deployment.replicas.updated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.involved_object.api_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.involved_object.kind\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.involved_object.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.involved_object.resource_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.involved_object.uid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.message\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.namespace\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.resource_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.self_link\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.timestamp.created\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.metadata.uid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.reason\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.timestamp.first_occurrence\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.timestamp.last_occurrence\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.event.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.namespace\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.cpu.allocatable.cores\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.cpu.capacity.cores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.cpu.usage.core.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.cpu.usage.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.inodes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.inodes.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.inodes.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.fs.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.allocatable.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.majorpagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.pagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.memory.workingset.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.network.rx.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.network.rx.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.network.tx.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.network.tx.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.pod.allocatable.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.pod.capacity.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.runtime.imagefs.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.runtime.imagefs.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.runtime.imagefs.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.status.ready\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.node.status.unschedulable\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.cpu.usage.limit.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.cpu.usage.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.cpu.usage.node.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.host_ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.major_page_faults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.page_faults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.usage.limit.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.usage.node.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.memory.working_set.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.network.rx.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.network.rx.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.network.tx.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.network.tx.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.status.phase\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.status.ready\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.status.scheduled\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.pod.uid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.client.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.handler\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.request.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.request.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.request.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.request.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.response.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.http.response.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.method\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.process.cpu.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.process.fds.open.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.process.memory.resident.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.process.memory.virtual.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.process.started.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.sync.networkprogramming.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.sync.networkprogramming.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.sync.rules.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.proxy.sync.rules.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.replicas.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.replicas.desired\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.replicas.labeled\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.replicas.observed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.replicaset.replicas.ready\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.client.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.handler\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.request.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.request.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.request.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.request.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.request.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.response.size.bytes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.http.response.size.bytes.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.leader.is_master\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.method\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.operation\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.process.cpu.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.process.fds.open.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.process.memory.resident.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.process.memory.virtual.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.process.started.sec\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.result\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.duration.seconds.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.duration.seconds.sum\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.e2e.duration.us.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.e2e.duration.us.sum\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.pod.attempts.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.scheduler.scheduling.pod.preemption.victims.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.created\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.generation.desired\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.generation.observed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.replicas.desired\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.statefulset.replicas.observed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.container\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.cpu.usage.core.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.cpu.usage.nanocores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.memory.majorpagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.memory.pagefaults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.memory.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.memory.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.memory.workingset.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.system.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.available.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.capacity.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.inodes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.inodes.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.inodes.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.fs.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kubernetes.volume.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kvm.dommemstat.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kvm.dommemstat.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kvm.dommemstat.stat.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"kvm.dommemstat.stat.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.level\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.logger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"log.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"logstash.node.jvm.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"logstash.node.stats.events.filtered\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"logstash.node.stats.events.in\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"logstash.node.stats.events.out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.bytes.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.bytes.limit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.cmd.get\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.cmd.set\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.connections.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.connections.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.evictions\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.get.hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.get.misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.items.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.items.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.threads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.uptime.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memcached.stats.written.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"metricset.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"metricset.period\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.collection\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.commands.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.commands.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.db\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.getmore.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.getmore.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.insert.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.insert.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.lock.read.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.lock.read.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.lock.write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.lock.write.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.queries.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.queries.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.remove.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.remove.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.total.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.total.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.update.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.collstats.update.time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.avg_obj_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.collections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.data_file_version.major\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.data_file_version.minor\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.data_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.db\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.extent_free_list.num\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.extent_free_list.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.file_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.index_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.indexes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.ns_size_mb.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.num_extents\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.objects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.dbstats.storage_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.aggregate.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.aggregate.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.build_info.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.build_info.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.coll_stats.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.coll_stats.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.connection_pool_stats.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.connection_pool_stats.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.count.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.count.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.db_stats.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.db_stats.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.distinct.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.distinct.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.find.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.find.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_cmd_line_opts.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_cmd_line_opts.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_last_error.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_last_error.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_log.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_log.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_more.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_more.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_parameter.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.get_parameter.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.host_info.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.host_info.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.insert.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.insert.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.is_master.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.is_master.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.is_self.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.is_self.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.last_collections.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.last_collections.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.last_commands.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.last_commands.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.list_databased.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.list_databased.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.list_indexes.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.list_indexes.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.ping.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.ping.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.profile.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.profile.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_get_rbid.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_get_rbid.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_get_status.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_get_status.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_heartbeat.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_heartbeat.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_update_position.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.replset_update_position.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.server_status.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.server_status.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.update.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.update.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.whatsmyuri.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.commands.whatsmyuri.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.cursor.open.no_timeout\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.cursor.open.pinned\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.cursor.open.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.cursor.timed_out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.document.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.document.inserted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.document.returned\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.document.updated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.get_last_error.write_timeouts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.get_last_error.write_wait.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.get_last_error.write_wait.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.operation.scan_and_order\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.operation.write_conflicts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.query_executor.scanned_documents\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.query_executor.scanned_indexes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.apply.attempts_to_become_secondary\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.apply.batches.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.apply.batches.time.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.apply.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.buffer.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.buffer.max_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.buffer.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.cancels\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.event_created\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.event_wait\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.dbwork\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.exclusive\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.netcmd\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.work\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.scheduled.work_at\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.counters.waits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.event_waiters\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.network_interface\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.in_progress.dbwork\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.in_progress.exclusive\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.in_progress.network\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.ready\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.queues.sleepers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.shutting_down\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.executor.unsignaled_events\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.initial_sync.completed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.initial_sync.failed_attempts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.initial_sync.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.network.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.network.getmores.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.network.getmores.time.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.network.ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.network.reders_created\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.preload.docs.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.preload.docs.time.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.preload.indexes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.replication.preload.indexes.time.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.storage.free_list.search.bucket_exhausted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.storage.free_list.search.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.storage.free_list.search.scanned\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.ttl.deleted_documents\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.metrics.ttl.passes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.headroom.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.headroom.min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.lag.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.lag.min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.arbiter.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.arbiter.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.down.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.down.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.primary.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.primary.optime\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.recovering.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.recovering.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.rollback.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.rollback.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.secondary.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.secondary.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.secondary.optimes\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.startup2.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.startup2.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.unhealthy.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.unhealthy.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.unknown.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.members.unknown.hosts\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.oplog.first.timestamp\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.oplog.last.timestamp\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.oplog.size.allocated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.oplog.size.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.oplog.window\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.optimes.applied\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.optimes.durable\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.optimes.last_committed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.server_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.replstatus.set_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.asserts.msg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.asserts.regular\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.asserts.rollovers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.asserts.user\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.asserts.warning\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.background_flushing.average.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.background_flushing.flushes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.background_flushing.last.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.background_flushing.last_finished\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.background_flushing.total.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.connections.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.connections.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.connections.total_created\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.extra_info.heap_usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.extra_info.page_faults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.active_clients.readers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.active_clients.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.active_clients.writers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.current_queue.readers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.current_queue.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.current_queue.writers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.global_lock.total_time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.commits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.commits_in_write_lock\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.compression\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.early_commits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.journaled.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.commits.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.commits_in_write_lock.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.dt.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.prep_log_buffer.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.remap_private_view.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.write_to_data_files.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.times.write_to_journal.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.journaling.write_to_data_files.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.local_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.acquire.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.acquire.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.acquire.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.acquire.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.deadlock.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.deadlock.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.deadlock.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.deadlock.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.us.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.us.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.us.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.collection.wait.us.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.acquire.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.acquire.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.acquire.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.acquire.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.deadlock.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.deadlock.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.deadlock.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.deadlock.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.us.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.us.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.us.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.database.wait.us.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.acquire.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.acquire.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.acquire.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.acquire.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.deadlock.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.deadlock.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.deadlock.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.deadlock.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.us.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.us.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.us.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.global.wait.us.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.acquire.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.acquire.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.acquire.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.acquire.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.deadlock.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.deadlock.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.deadlock.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.deadlock.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.us.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.us.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.us.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.meta_data.wait.us.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.acquire.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.acquire.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.acquire.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.acquire.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.deadlock.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.deadlock.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.deadlock.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.deadlock.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.count.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.count.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.count.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.count.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.us.R\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.us.W\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.us.r\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.locks.oplog.wait.us.w\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.memory.bits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.memory.mapped.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.memory.mapped_with_journal.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.memory.resident.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.memory.virtual.mb\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.network.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.network.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.network.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.command\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.delete\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.getmore\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.insert\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.query\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.counters.update\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.commands.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.commands.latency\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.reads.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.reads.latency\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.writes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.latencies.writes.latency\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.command\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.delete\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.getmore\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.insert\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.query\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.ops.replicated.update\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.process\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.storage_engine.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.uptime.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.dirty.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.maximum.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.pages.evicted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.pages.read\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.pages.write\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.cache.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.read.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.read.out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.read.total_tickets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.write.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.write.out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.concurrent_transactions.write.total_tickets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.flushes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.max_file_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.scans\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.syncs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.wired_tiger.log.writes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mongodb.status.write_backs_queued\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.database.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.database.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.active_temp_tables\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.batch_requests_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.buffer.cache_hit.pct\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.buffer.checkpoint_pages_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.buffer.database_pages\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.buffer.page_life_expectancy.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.buffer.target_pages\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.compilations_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.connections_reset_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.lock_waits_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.logins_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.logouts_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.page_splits_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.recompilations_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.transactions\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.performance.user_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.space_usage.since_last_backup.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.space_usage.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.space_usage.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.space_usage.used.pct\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.stats.active_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.stats.backup_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.stats.recovery_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.stats.since_last_checkpoint.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mssql.transaction_log.stats.total_size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"munin.plugin.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.apply.oooe\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.apply.oool\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.apply.window\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cert.deps_distance\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cert.index_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cert.interval\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cluster.conf_id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cluster.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.cluster.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.commit.oooe\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.commit.window\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.connected\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.evs.evict\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.evs.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.flow_ctl.paused\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.flow_ctl.paused_ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.flow_ctl.recv\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.flow_ctl.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.last_committed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.bf_aborts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.cert_failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.commits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.recv.queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.recv.queue_avg\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.recv.queue_max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.recv.queue_min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.replays\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.send.queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.send.queue_avg\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.send.queue_max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.send.queue_min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.local.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.ready\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.received.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.received.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.data_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.keys\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.keys_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.galera_status.repl.other_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.aborted.clients\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.aborted.connects\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.binlog.cache.disk_use\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.binlog.cache.use\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.bytes.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.bytes.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.command.delete\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.command.insert\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.command.select\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.command.update\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.created.tmp.disk_tables\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.created.tmp.files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.created.tmp.tables\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.delayed.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.delayed.insert_threads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.delayed.writes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.flush_commands\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.commit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.delete\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.external_lock\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.mrr_init\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.prepare\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.first\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.key\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.last\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.next\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.prev\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.rnd\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.read.rnd_next\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.rollback\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.savepoint\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.savepoint_rollback\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.update\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.handler.write\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.bytes.data\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.bytes.dirty\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.dump_status\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.load_status\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.data\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.dirty\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.flushed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.latched\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.misc\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pages.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pool.reads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pool.resize_status\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.pool.wait_free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.read.ahead\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.read.ahead_evicted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.read.ahead_rnd\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.read.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.innodb.buffer_pool.write_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.max_used_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.open.files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.open.streams\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.open.tables\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.opened_tables\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.queries\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.questions\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.threads.cached\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.threads.connected\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.threads.created\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mysql.status.threads.running\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.connections.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.routes.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.server.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.server.time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.cores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.cpu\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.http.req_stats.uri.connz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.http.req_stats.uri.root\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.http.req_stats.uri.routez\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.http.req_stats.uri.subsz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.http.req_stats.uri.varz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.in.messages\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.mem.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.out.messages\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.remotes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.slow_consumers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.total_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.stats.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.cache.fanout.avg\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.cache.fanout.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.cache.hit_rate\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.cache.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.inserts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.matches\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.removes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nats.subscriptions.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.application\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.community_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.direction\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.forwarded_ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.iana_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.protocol\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.transport\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"network.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.accepts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.handled\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.reading\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.waiting\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nginx.stubstatus.writing\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.serial_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.vendor\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"observer.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.buffer_pool\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cache.buffer.hit.pct\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cache.get.consistent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cache.get.db_blocks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cache.physical_reads\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.avg\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.cache_hit.pct\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.max\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.opened.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.opened.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.parse.real\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.parse.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.session.cache_hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.cursors.total\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.io_reloads\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.lock_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.machine\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.pin_requests\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.performance.username\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.online_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.size.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.size.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.size.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.data_file.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.space.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.space.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"oracle.tablespace.space.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"organization.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.connections.accepted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.connections.listen_queue_len\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.connections.max_listen_queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.connections.queued\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.process_manager\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.processes.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.processes.idle\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.processes.max_active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.processes.max_children_reached\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.processes.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.slow_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.start_since\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.pool.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.last_request_cpu\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.last_request_memory\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.request_duration\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.script\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.start_since\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"php_fpm.process.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.application_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.backend_start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.client.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.client.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.client.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.database.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.database.oid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.query\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.query_start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.state_change\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.transaction_start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.user.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.activity.waiting\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.allocated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.backend\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.backend_fsync\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.checkpoints\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.clean\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.buffers.clean_full\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.checkpoints.requested\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.checkpoints.scheduled\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.checkpoints.times.sync.ms\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.checkpoints.times.write.ms\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.bgwriter.stats_reset\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.blocks.hit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.blocks.read\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.blocks.time.read.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.blocks.time.write.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.conflicts\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.deadlocks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.number_of_backends\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.oid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.rows.deleted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.rows.fetched\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.rows.inserted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.rows.returned\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.rows.updated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.stats_reset\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.temporary.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.temporary.files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.transactions.commit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.database.transactions.rollback\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.database.oid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.calls\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.local.dirtied\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.local.hit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.local.read\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.local.written\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.shared.dirtied\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.shared.hit\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.shared.read\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.shared.written\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.temp.read\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.memory.temp.written\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.rows\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.time.max.ms\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.time.mean.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.time.min.ms\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.time.stddev.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.query.time.total.ms\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"postgresql.statement.user.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.args\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.executable\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.md5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha256\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.hash.sha512\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pgid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.ppid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.start\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.thread.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.title\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"process.working_directory\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.channel_max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.channels\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.client_provided.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.frame_max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.octet_count.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.octet_count.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.packet_count.pending\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.packet_count.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.packet_count.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.peer.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.peer.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.connection.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.auto_delete\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.durable\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.internal\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.messages.publish_in.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.messages.publish_in.details.rate\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.messages.publish_out.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.messages.publish_out.details.rate\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.exchange.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.disk.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.disk.free.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.fd.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.fd.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.gc.num.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.gc.reclaimed.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.file_handle.open_attempt.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.file_handle.open_attempt.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.read.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.read.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.reopen.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.seek.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.seek.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.sync.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.sync.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.write.avg.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.io.write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.mem.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.mem.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.mnesia.disk.tx.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.mnesia.ram.tx.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.msg.store_read.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.msg.store_write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.proc.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.proc.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.processors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.queue.index.journal_write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.queue.index.read.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.queue.index.write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.run.queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.socket.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.socket.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.node.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.arguments.max_priority\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.auto_delete\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.consumers.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.consumers.utilisation.pct\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.disk.reads.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.disk.writes.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.durable\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.exclusive\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.memory.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.persistent.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.ready.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.ready.details.rate\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.total.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.total.details.rate\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.unacknowledged.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.messages.unacknowledged.details.rate\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.queue.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rabbitmq.vhost\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.biggest_input_buf\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.blocked\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.connected\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.longest_output_list\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.max_input_buffer\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.clients.max_output_buffer\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.cluster.enabled\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.cpu.used.sys\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.cpu.used.sys_children\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.cpu.used.user\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.cpu.used.user_children\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.active_defrag.is_running\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.allocated\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.fragmentation.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.fragmentation.ratio\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.resident\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.allocator_stats.rss.ratio\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.fragmentation.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.fragmentation.ratio\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.max.policy\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.max.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.used.dataset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.used.lua\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.used.peak\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.used.rss\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.memory.used.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.bgrewrite.last_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.buffer.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.copy_on_write.last_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.enabled\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.fsync.delayed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.fsync.pending\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.rewrite.buffer.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.rewrite.current_time.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.rewrite.in_progress\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.rewrite.last_time.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.rewrite.scheduled\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.size.base\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.size.current\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.aof.write.last_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.loading\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.bgsave.current_time.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.bgsave.in_progress\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.bgsave.last_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.bgsave.last_time.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.copy_on_write.last_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.last_save.changes_since\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.persistence.rdb.last_save.time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.backlog.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.backlog.first_byte_offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.backlog.histlen\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.backlog.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.connected_slaves\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.last_io_seconds_ago\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.link_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.second_offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.sync.in_progress\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.sync.last_io_seconds_ago\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master.sync.left_bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.master_offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.role\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.slave.is_readonly\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.slave.offset\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.replication.slave.priority\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.arch_bits\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.build_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.config_file\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.gcc_version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.git_dirty\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.git_sha1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.hz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.lru_clock\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.mode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.multiplexing_api\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.run_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.tcp_port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.server.uptime\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.slowlog.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.active_defrag.hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.active_defrag.key_hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.active_defrag.key_misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.active_defrag.misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.commands_processed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.connections.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.connections.rejected\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.instantaneous.input_kbps\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.instantaneous.ops_per_sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.instantaneous.output_kbps\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.keys.evicted\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.keys.expired\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.keyspace.hits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.keyspace.misses\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.latest_fork_usec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.migrate_cached_sockets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.net.input.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.net.output.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.pubsub.channels\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.pubsub.patterns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.slave_expires_tracked_keys\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.sync.full\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.sync.partial.err\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.info.stats.sync.partial.ok\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.key.expire.ttl\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.key.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.key.length\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.key.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.key.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.keyspace.avg_ttl\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.keyspace.expires\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.keyspace.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"redis.keyspace.keys\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"related.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"server.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.ephemeral_id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"service.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.address\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.as.number\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.as.organization.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.city_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.continent_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.country_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.country_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.region_iso_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.geo.region_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.mac\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.nat.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.nat.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"source.user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.idle.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.idle.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.iowait.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.iowait.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.irq.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.irq.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.nice.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.nice.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.softirq.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.softirq.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.steal.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.steal.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.system.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.system.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.user.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.core.user.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.cores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.idle.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.idle.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.idle.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.iowait.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.iowait.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.iowait.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.irq.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.irq.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.irq.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.nice.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.nice.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.nice.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.softirq.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.softirq.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.softirq.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.steal.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.steal.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.steal.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.system.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.system.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.system.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.total.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.total.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":3,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.user.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.user.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.cpu.user.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.io.time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.await\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.busy\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.queue.avg_size\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.read.await\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.read.per_sec.bytes\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.read.request.merges_per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.read.request.per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.request.avg_size\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.service_time\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.write.await\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.write.per_sec.bytes\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.write.request.merges_per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.iostat.write.request.per_sec\",\"type\":\"number\",\"esTypes\":[\"float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.read.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.read.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.read.time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.serial_number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.write.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.write.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.diskio.write.time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.entropy.available_bits\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.entropy.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.available\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.device_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.free_files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.mount_point\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.filesystem.used.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.fsstat.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.fsstat.total_files\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.fsstat.total_size.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.fsstat.total_size.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.fsstat.total_size.used\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.1\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.15\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.5\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.cores\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.norm.1\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.norm.15\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.load.norm.5\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.actual.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.actual.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.actual.used.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.default_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.reserved\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.surplus\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.hugepages.used.pct\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.swap.free\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.swap.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.swap.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.swap.used.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.memory.used.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.in.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.in.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.in.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.in.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.out.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.out.dropped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.out.errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.network.out.packets\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.blkio.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.blkio.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.blkio.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.blkio.total.ios\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.cfs.period.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.cfs.quota.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.cfs.shares\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.rt.period.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.rt.runtime.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.stats.periods\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.stats.throttled.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpu.stats.throttled.periods\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpuacct.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpuacct.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpuacct.stats.system.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpuacct.stats.user.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.cpuacct.total.ns\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem.usage.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem_tcp.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem_tcp.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem_tcp.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.kmem_tcp.usage.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.mem.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.mem.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.mem.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.mem.usage.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.memsw.failures\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.memsw.limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.memsw.usage.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.memsw.usage.max.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.active_anon.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.active_file.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.cache.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.hierarchical_memory_limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.hierarchical_memsw_limit.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.inactive_anon.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.inactive_file.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.major_page_faults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.mapped_file.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.page_faults\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.pages_in\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.pages_out\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.rss_huge.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.swap.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.memory.stats.unevictable.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cgroup.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cmdline\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.start_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.system.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.total.norm.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.total.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.total.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.total.value\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.cpu.user.ticks\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.fd.limit.hard\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.fd.limit.soft\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.fd.open\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.memory.rss.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.memory.rss.pct\",\"type\":\"number\",\"esTypes\":[\"scaled_float\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.memory.share\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.memory.size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.dead\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.idle\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.running\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.sleeping\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.stopped\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.unknown\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.process.summary.zombie\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.blocks.synced\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.blocks.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.disks.active\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.disks.failed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.disks.spare\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.disks.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.level\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.raid.sync_action\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.local.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.local.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.process.cmdline\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.remote.etld_plus_one\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.remote.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.remote.host_error\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.remote.ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.remote.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.all.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.all.listening\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.close_wait\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.established\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.listening\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.orphan\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.all.time_wait\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.tcp.memory\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.udp.all.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.socket.summary.udp.memory\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"system.uptime.duration.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tags\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"timeseries.instance\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tracing.trace.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tracing.transaction.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"traefik.health.response.avg_time.us\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"traefik.health.response.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"traefik.health.uptime.sec\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.fragment\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.password\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.path\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.port\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.query\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.scheme\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url.username\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.domain\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.email\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.full_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.group.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.hash\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.device.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.original\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.family\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.full\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.kernel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.platform\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.os.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"user_agent.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.read_errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.requests.offloaded\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.requests.routed\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.requests.static\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.requests.total\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.worker_pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.core.write_errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.total.exceptions\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.total.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.total.read_errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.total.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.total.write_errors\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.accepting\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.avg_rt\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.delta_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.exceptions\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.harakiri_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.id\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.respawn_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.rss\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.running_time\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.signal_queue\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.signals\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.tx\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"uwsgi.status.worker.vsz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.capacity.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.capacity.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.capacity.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.capacity.used.pct\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.fstype\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.datastore.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.cpu.free.mhz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.cpu.total.mhz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.cpu.used.mhz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.memory.free.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.memory.total.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.memory.used.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.host.network_names\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.cpu.used.mhz\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.host\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.memory.free.guest.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.memory.total.guest.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.memory.used.guest.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.memory.used.host.bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.network_names\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"vsphere.virtualmachine.os\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.display_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.exit_code\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.path_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.pid\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.start_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.start_type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"windows.service.uptime.ms\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.connection.interest_ops\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.connection.queued\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.connection.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.connection.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.approximate_data_size\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.ephemerals_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.followers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.hostname\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.latency.avg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.latency.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.latency.min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.max_file_descriptor_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.num_alive_connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.open_file_descriptor_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.outstanding_requests\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.packets.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.packets.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.pending_syncs\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.server_state\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.synced_followers\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.version\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.watch_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.mntr.znode_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.connections\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.epoch\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.latency.avg\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.latency.max\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.latency.min\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.mode\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.node_count\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.outstanding\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.received\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.sent\",\"type\":\"number\",\"esTypes\":[\"long\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.version_date\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"zookeeper.server.zxid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", - "timeFieldName" : "@timestamp", - "title" : "metricbeat-*" - }, - "type" : "index-pattern", - "migrationVersion" : { - "index-pattern" : "7.6.0" - }, - "updated_at" : "2020-01-22T15:34:59.061Z" + "id": "custom-space:index-pattern:metricbeat-*", + "index": ".kibana_1", + "source": { + "index-pattern": { + "fieldFormatMap": "{\"aerospike.namespace.device.available.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.device.free.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.device.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.device.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.free.pct\":{\"id\":\"percent\",\"params\":{}},\"aerospike.namespace.memory.used.data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.index.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.sindex.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aerospike.namespace.memory.used.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.ec2.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"aws.rds.disk_usage.bin_log.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.free_local_storage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.free_storage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.freeable_memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.latency.commit\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.ddl\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.dml\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.insert\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.read\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.select\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.update\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.latency.write\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.replica_lag.sec\":{\"id\":\"duration\",\"params\":{}},\"aws.rds.swap_usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.rds.volume_used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_daily_storage.bucket.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.downloaded.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.latency.first_byte.ms\":{\"id\":\"duration\",\"params\":{}},\"aws.s3_request.latency.total_request.ms\":{\"id\":\"duration\",\"params\":{}},\"aws.s3_request.requests.select_returned.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.requests.select_scanned.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.s3_request.uploaded.bytes\":{\"id\":\"bytes\",\"params\":{}},\"aws.sqs.oldest_message_age.sec\":{\"id\":\"duration\",\"params\":{}},\"aws.sqs.sent_message_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_disk.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.degraded.ratio\":{\"id\":\"percent\",\"params\":{}},\"ceph.cluster_status.misplace.ratio\":{\"id\":\"percent\",\"params\":{}},\"ceph.cluster_status.pg.avail_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.data_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.total_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.pg.used_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.traffic.read_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.cluster_status.traffic.write_bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.log.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.misc.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.sst.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.monitor_health.store_stats.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.total.byte\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.used.byte\":{\"id\":\"bytes\",\"params\":{}},\"ceph.osd_df.used.pct\":{\"id\":\"percent\",\"params\":{}},\"ceph.pool_disk.stats.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"ceph.pool_disk.stats.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"client.bytes\":{\"id\":\"bytes\",\"params\":{}},\"client.nat.port\":{\"id\":\"string\",\"params\":{}},\"client.port\":{\"id\":\"string\",\"params\":{}},\"coredns.stats.dns.request.duration.ns.sum\":{\"id\":\"duration\",\"params\":{}},\"couchbase.bucket.data.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.disk.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.quota.ram.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.bucket.quota.use.pct\":{\"id\":\"percent\",\"params\":{}},\"couchbase.cluster.hdd.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.quota.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.used.by_data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.hdd.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.total.per_node.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.total.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.used.per_node.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.quota.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.used.by_data.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.cluster.ram.used.value.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.couch.docs.data_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.couch.docs.disk_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"couchbase.node.mcd_memory.allocated.bytes\":{\"id\":\"bytes\",\"params\":{}},\"destination.bytes\":{\"id\":\"bytes\",\"params\":{}},\"destination.nat.port\":{\"id\":\"string\",\"params\":{}},\"destination.port\":{\"id\":\"string\",\"params\":{}},\"docker.cpu.core.*.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.kernel.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.system.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.cpu.user.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.diskio.summary.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.commit.peak\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.commit.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.limit\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.private_working_set.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.rss.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.memory.rss.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.usage.max\":{\"id\":\"bytes\",\"params\":{}},\"docker.memory.usage.pct\":{\"id\":\"percent\",\"params\":{}},\"docker.memory.usage.total\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.inbound.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"docker.network.outbound.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.primaries.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.primaries.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.total.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.summary.total.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.total.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.index.total.store.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.heap.init.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.heap.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.nonheap.init.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.jvm.memory.nonheap.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.fs.summary.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.indices.segments.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.old.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.survivor.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.peak.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.peak_max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"elasticsearch.node.stats.jvm.mem.pools.young.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.disk.mvcc_db_total_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.memory.go_memstats_alloc.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.network.client_grpc_received.bytes\":{\"id\":\"bytes\",\"params\":{}},\"etcd.network.client_grpc_sent.bytes\":{\"id\":\"bytes\",\"params\":{}},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\",\"params\":{}},\"event.severity\":{\"id\":\"string\",\"params\":{}},\"golang.heap.allocations.active\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.allocated\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.idle\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.allocations.total\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.gc.next_gc_limit\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.obtained\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.released\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.stack\":{\"id\":\"bytes\",\"params\":{}},\"golang.heap.system.total\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.info.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"haproxy.info.memory.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.info.ssl.frontend.session_reuse.pct\":{\"id\":\"percent\",\"params\":{}},\"haproxy.stat.compressor.bypassed.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.compressor.response.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"haproxy.stat.throttle.pct\":{\"id\":\"percent\",\"params\":{}},\"http.request.body.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.request.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.body.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.bytes\":{\"id\":\"bytes\",\"params\":{}},\"http.response.status_code\":{\"id\":\"string\",\"params\":{}},\"kibana.stats.process.memory.heap.size_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kibana.stats.process.memory.heap.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kibana.stats.process.memory.heap.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.apiserver.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.cpu.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.cpu.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.logs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.logs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.logs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.request.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.memory.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.memory.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.container.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.container.rootfs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.controllermanager.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.fs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.allocatable.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.network.rx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.network.tx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.node.runtime.imagefs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.cpu.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.cpu.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.memory.usage.limit.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.usage.node.pct\":{\"id\":\"percent\",\"params\":{}},\"kubernetes.pod.memory.working_set.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.network.rx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.pod.network.tx.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.proxy.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.http.request.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.http.response.size.bytes.sum\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.process.memory.resident.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.scheduler.process.memory.virtual.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.system.memory.workingset.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.available.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.capacity.bytes\":{\"id\":\"bytes\",\"params\":{}},\"kubernetes.volume.fs.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.avg_obj_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.data_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.extent_free_list.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.file_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.index_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.dbstats.storage_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.replstatus.headroom.max\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.headroom.min\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.lag.max\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.lag.min\":{\"id\":\"duration\",\"params\":{}},\"mongodb.replstatus.oplog.size.allocated\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.replstatus.oplog.size.used\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.extra_info.heap_usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.dirty.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.maximum.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.cache.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.max_file_size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mongodb.status.wired_tiger.log.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"mysql.status.bytes.received\":{\"id\":\"bytes\",\"params\":{}},\"mysql.status.bytes.sent\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.cpu\":{\"id\":\"percent\",\"params\":{}},\"nats.stats.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.mem.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"nats.stats.uptime\":{\"id\":\"duration\",\"params\":{}},\"nats.subscriptions.cache.hit_rate\":{\"id\":\"percent\",\"params\":{}},\"network.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.data_file.size.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"oracle.tablespace.space.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"process.pgid\":{\"id\":\"string\",\"params\":{}},\"process.pid\":{\"id\":\"string\",\"params\":{}},\"process.ppid\":{\"id\":\"string\",\"params\":{}},\"process.thread.id\":{\"id\":\"string\",\"params\":{}},\"rabbitmq.connection.frame_max\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.disk.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.disk.free.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.gc.reclaimed.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.io.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.io.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.node.mem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"rabbitmq.queue.consumers.utilisation.pct\":{\"id\":\"percent\",\"params\":{}},\"rabbitmq.queue.memory.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.active\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.allocated\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.fragmentation.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.resident\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.allocator_stats.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.fragmentation.bytes\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.max.value\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.dataset\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.lua\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.peak\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.rss\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.memory.used.value\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.buffer.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.copy_on_write.last_size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.rewrite.buffer.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.rewrite.current_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.aof.rewrite.last_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.aof.size.base\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.aof.size.current\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.persistence.rdb.bgsave.current_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.rdb.bgsave.last_time.sec\":{\"id\":\"duration\",\"params\":{}},\"redis.info.persistence.rdb.copy_on_write.last_size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.replication.backlog.size\":{\"id\":\"bytes\",\"params\":{}},\"redis.info.replication.master.last_io_seconds_ago\":{\"id\":\"duration\",\"params\":{}},\"redis.info.replication.master.sync.last_io_seconds_ago\":{\"id\":\"duration\",\"params\":{}},\"redis.info.replication.master.sync.left_bytes\":{\"id\":\"bytes\",\"params\":{}},\"server.bytes\":{\"id\":\"bytes\",\"params\":{}},\"server.nat.port\":{\"id\":\"string\",\"params\":{}},\"server.port\":{\"id\":\"string\",\"params\":{}},\"source.bytes\":{\"id\":\"bytes\",\"params\":{}},\"source.nat.port\":{\"id\":\"string\",\"params\":{}},\"source.port\":{\"id\":\"string\",\"params\":{}},\"system.core.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.iowait.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.irq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.nice.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.softirq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.steal.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.system.pct\":{\"id\":\"percent\",\"params\":{}},\"system.core.user.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.idle.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.idle.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.iowait.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.iowait.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.irq.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.irq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.nice.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.nice.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.softirq.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.softirq.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.steal.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.steal.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.system.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.system.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.total.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.user.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.cpu.user.pct\":{\"id\":\"percent\",\"params\":{}},\"system.diskio.iostat.read.per_sec.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.iostat.write.per_sec.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.read.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.diskio.write.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.entropy.pct\":{\"id\":\"percent\",\"params\":{}},\"system.filesystem.available\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.free\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.total\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.filesystem.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.fsstat.total_size.free\":{\"id\":\"bytes\",\"params\":{}},\"system.fsstat.total_size.total\":{\"id\":\"bytes\",\"params\":{}},\"system.fsstat.total_size.used\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.actual.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.default_size\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.free\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.reserved\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.surplus\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.total\":{\"id\":\"number\",\"params\":{}},\"system.memory.hugepages.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.hugepages.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.swap.free\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.total\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.swap.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.memory.total\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.memory.used.pct\":{\"id\":\"percent\",\"params\":{}},\"system.network.in.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.network.out.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.blkio.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.kmem_tcp.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.mem.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.usage.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.memsw.usage.max.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.active_anon.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.active_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.cache.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.hierarchical_memory_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.hierarchical_memsw_limit.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.inactive_anon.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.inactive_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.mapped_file.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.rss_huge.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.swap.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cgroup.memory.stats.unevictable.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.cpu.total.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\",\"params\":{}},\"system.process.memory.rss.pct\":{\"id\":\"percent\",\"params\":{}},\"system.process.memory.share\":{\"id\":\"bytes\",\"params\":{}},\"system.process.memory.size\":{\"id\":\"bytes\",\"params\":{}},\"system.socket.summary.tcp.memory\":{\"id\":\"bytes\",\"params\":{}},\"system.socket.summary.udp.memory\":{\"id\":\"bytes\",\"params\":{}},\"system.uptime.duration.ms\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"milliseconds\"}},\"url.port\":{\"id\":\"string\",\"params\":{}},\"vsphere.datastore.capacity.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.datastore.capacity.used.pct\":{\"id\":\"percent\",\"params\":{}},\"vsphere.host.memory.free.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.host.memory.total.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.host.memory.used.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.free.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.total.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.used.guest.bytes\":{\"id\":\"bytes\",\"params\":{}},\"vsphere.virtualmachine.memory.used.host.bytes\":{\"id\":\"bytes\",\"params\":{}},\"windows.service.uptime.ms\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"milliseconds\"}}}", + "timeFieldName": "@timestamp", + "title": "metricbeat-*" + }, + "migrationVersion": { + "index-pattern": "7.6.0" + }, + "type": "index-pattern", + "updated_at": "2020-01-22T15:34:59.061Z" } } } @@ -145,19 +145,19 @@ { "type": "doc", "value": { - "index": ".kibana", - "type": "doc", "id": "index-pattern:logstash-*", + "index": ".kibana_1", "source": { "index-pattern": { - "title": "logstash-*", "timeFieldName": "@timestamp", - "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"runtime_number\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]" + "title": "logstash-*" }, - "type": "index-pattern", "migrationVersion": { - "index-pattern": "6.5.0" + "index-pattern": "7.11.0" }, + "references": [ + ], + "type": "index-pattern", "updated_at": "2018-12-21T00:43:07.096Z" } } @@ -166,20 +166,20 @@ { "type": "doc", "value": { - "index": ".kibana", - "type": "doc", "id": "custom_space:index-pattern:logstash-*", + "index": ".kibana_1", "source": { - "namespace": "custom_space", "index-pattern": { - "title": "logstash-*", "timeFieldName": "@timestamp", - "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]" + "title": "logstash-*" }, - "type": "index-pattern", "migrationVersion": { - "index-pattern": "6.5.0" + "index-pattern": "7.11.0" }, + "namespace": "custom_space", + "references": [ + ], + "type": "index-pattern", "updated_at": "2018-12-21T00:43:07.096Z" } } @@ -188,22 +188,31 @@ { "type": "doc", "value": { - "index": ".kibana", - "type": "doc", "id": "visualization:i-exist", + "index": ".kibana_1", "source": { + "migrationVersion": { + "visualization": "7.12.0" + }, + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2019-01-22T19:32:31.206Z", "visualization": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, "title": "A Pie", - "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}", "uiStateJSON": "{}", - "description": "", "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } - }, - "type": "visualization", - "updated_at": "2019-01-22T19:32:31.206Z" + "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}" + } } } } @@ -211,23 +220,32 @@ { "type": "doc", "value": { - "index": ".kibana", - "type": "doc", "id": "custom_space:visualization:i-exist", + "index": ".kibana_1", "source": { + "migrationVersion": { + "visualization": "7.12.0" + }, "namespace": "custom_space", + "references": [ + { + "id": "logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "visualization", + "updated_at": "2019-01-22T19:32:31.206Z", "visualization": { + "description": "", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, "title": "A Pie", - "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}", "uiStateJSON": "{}", - "description": "", "version": 1, - "kibanaSavedObjectMeta": { - "searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"filter\":[]}" - } - }, - "type": "visualization", - "updated_at": "2019-01-22T19:32:31.206Z" + "visState": "{\"title\":\"A Pie\",\"type\":\"pie\",\"params\":{\"type\":\"pie\",\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"labels\":{\"show\":false,\"values\":true,\"last_level\":true,\"truncate\":100},\"dimensions\":{\"metric\":{\"accessor\":0,\"format\":{\"id\":\"number\"},\"params\":{},\"aggType\":\"count\"}}},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"geo.src\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"}}]}" + } } } } @@ -235,21 +253,48 @@ { "type": "doc", "value": { - "index": ".kibana", - "type": "doc", "id": "query:OKJpgs", + "index": ".kibana_1", "source": { "query": { - "title": "OKJpgs", "description": "Ok responses for jpg files", + "filters": [ + { + "$state": { + "store": "appState" + }, + "meta": { + "alias": null, + "disabled": false, + "index": "b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b", + "key": "extension.raw", + "negate": false, + "params": { + "query": "jpg" + }, + "type": "phrase", + "value": "jpg" + }, + "query": { + "match": { + "extension.raw": { + "query": "jpg", + "type": "phrase" + } + } + } + } + ], "query": { - "query": "response:200", - "language": "kuery" + "language": "kuery", + "query": "response:200" }, - "filters": [{"meta":{"index":"b15b1d40-a8bb-11e9-98cf-2bb06ef63e0b","alias":null,"negate":false,"type":"phrase","key":"extension.raw","value":"jpg","params":{"query":"jpg"},"disabled":false},"query":{"match":{"extension.raw":{"query":"jpg","type":"phrase"}}},"$state":{"store":"appState"}}] + "title": "OKJpgs" }, + "references": [ + ], "type": "query", "updated_at": "2019-07-17T17:54:26.378Z" } } -} +} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/visualize/default/mappings.json b/x-pack/test/functional/es_archives/visualize/default/mappings.json index cc9a80589e5a..a6feafd3922a 100644 --- a/x-pack/test/functional/es_archives/visualize/default/mappings.json +++ b/x-pack/test/functional/es_archives/visualize/default/mappings.json @@ -1,488 +1,2670 @@ { "type": "index", "value": { - "index": ".kibana", - "settings": { - "index": { - "number_of_shards": "1", - "auto_expand_replicas": "0-1", - "number_of_replicas": "0" + "aliases": { + ".kibana": { } }, + "index": ".kibana_1", "mappings": { - "dynamic": "strict", - "properties": { - "apm-telemetry": { - "properties": { - "has_any_services": { - "type": "boolean" + "_meta": { + "migrationMappingPropertyHashes": { + "action": "6e96ac5e648f57523879661ea72525b7", + "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "alert": "49eb3350984bd2a162914d3776e70cfb", + "api_key_pending_invalidation": "16f515278a295f6245149ad7c5ddedb7", + "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", + "apm-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "app_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_daily": "43b8830d5d0df85a6823d290885fc9fd", + "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", + "application_usage_transactional": "3d1b76c39bfb2cc8296b024d73854724", + "canvas-element": "7390014e1091044523666d97247392fc", + "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "canvas-workpad-template": "ae2673f678281e2c055d764b153e9715", + "cases": "0b7746a97518ec67b787d141886ad3c1", + "cases-comments": "8a50736330e953bca91747723a319593", + "cases-configure": "387c5f3a3bda7e0ae0dd4e106f914a69", + "cases-connector-mappings": "6bc7e49411d38be4969dc6aa8bd43776", + "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", + "config": "c63748b75f39d0c54de12d12c1ccbc20", + "core-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", + "dashboard": "40554caf09725935e2c02e02563a2d07", + "endpoint:user-artifact": "4a11183eee21e6fbad864f7a30b39ad0", + "endpoint:user-artifact-manifest": "4b9c0e7cfaf86d82a7ee9ed68065e50d", + "enterprise_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "epm-packages": "0cbbb16506734d341a96aaed65ec6413", + "epm-packages-assets": "44621b2f6052ef966da47b7c3a00f33b", + "exception-list": "67f055ab8c10abd7b2ebfd969b836788", + "exception-list-agnostic": "67f055ab8c10abd7b2ebfd969b836788", + "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", + "fleet-agent-actions": "9511b565b1cc6441a42033db3d5de8e9", + "fleet-agent-events": "e20a508b6e805189356be381dbfac8db", + "fleet-agents": "cb661e8ede2b640c42c8e5ef99db0683", + "fleet-enrollment-api-keys": "a69ef7ae661dab31561d6c6f052ef2a7", + "graph-workspace": "27a94b2edcb0610c6aea54a7c56d7752", + "index-pattern": "45915a1ad866812242df474eb0479052", + "infrastructure-ui-source": "3d1b76c39bfb2cc8296b024d73854724", + "ingest-agent-policies": "8b0733cce189659593659dad8db426f0", + "ingest-outputs": "8854f34453a47e26f86a29f8f3b80b4e", + "ingest-package-policies": "c91ca97b1ff700f0fc64dc6b13d65a85", + "ingest_manager_settings": "02a03095f0e05b7a538fa801b88a217f", + "inventory-view": "3d1b76c39bfb2cc8296b024d73854724", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "lens": "52346cfec69ff7b47d5f0c12361a2797", + "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", + "map": "9134b47593116d7953f6adba096fc463", + "maps-telemetry": "5ef305b18111b77789afefbd36b66171", + "metrics-explorer-view": "3d1b76c39bfb2cc8296b024d73854724", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "ml-job": "3bb64c31915acf93fc724af137a0891b", + "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", + "monitoring-telemetry": "2669d5ec15e82391cf58df4294ee9c68", + "namespace": "2f4316de49999235636386fe51dc06c1", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "originId": "2f4316de49999235636386fe51dc06c1", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "959dde12a55b3118eab009d8b2b72ad6", + "search-session": "dfd06597e582fdbbbc09f1a3615e6ce0", + "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "security-solution-signals-migration": "72761fd374ca11122ac8025a92b84fca", + "siem-detection-engine-rule-actions": "6569b288c169539db10cb262bf79de18", + "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", + "siem-ui-timeline": "d624a677e25046b56e4f111a7b2cc402", + "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", + "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", + "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", + "spaces-usage-stats": "3d1b76c39bfb2cc8296b024d73854724", + "tag": "83d55da58f6530f7055415717ec06474", + "telemetry": "36a616f7026dfa617d6655df850fe16d", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-counter": "0d409297dc5ebe1e3a1da691c6ee32e3", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "upgrade-assistant-reindex-operation": "215107c281839ea9b3ad5f6419819763", + "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", + "uptime-dynamic-settings": "3d1b76c39bfb2cc8296b024d73854724", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "visualization": "f819cf6636b75c9e76ba733a0c6ef355", + "workplace_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724" + } + }, + "dynamic": "strict", + "properties": { + "action": { + "properties": { + "actionTypeId": { + "type": "keyword" + }, + "config": { + "enabled": false, + "type": "object" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } }, - "services_per_agent": { - "properties": { - "go": { - "type": "long", - "null_value": 0 - }, - "java": { - "type": "long", - "null_value": 0 - }, - "js-base": { - "type": "long", - "null_value": 0 - }, - "nodejs": { - "type": "long", - "null_value": 0 - }, - "python": { - "type": "long", - "null_value": 0 - }, - "ruby": { - "type": "long", - "null_value": 0 + "type": "text" + }, + "secrets": { + "type": "binary" + } + } + }, + "action_task_params": { + "properties": { + "actionId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "params": { + "enabled": false, + "type": "object" + } + } + }, + "alert": { + "properties": { + "actions": { + "properties": { + "actionRef": { + "type": "keyword" + }, + "actionTypeId": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + }, + "type": "nested" + }, + "alertTypeId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "apiKeyOwner": { + "type": "keyword" + }, + "consumer": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + }, + "createdBy": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "executionStatus": { + "properties": { + "error": { + "properties": { + "message": { + "type": "keyword" + }, + "reason": { + "type": "keyword" + } } + }, + "lastExecutionDate": { + "type": "date" + }, + "status": { + "type": "keyword" + } + } + }, + "meta": { + "properties": { + "versionApiKeyLastmodified": { + "type": "keyword" } } + }, + "muteAll": { + "type": "boolean" + }, + "mutedInstanceIds": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + }, + "notifyWhen": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledTaskId": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "throttle": { + "type": "keyword" + }, + "updatedAt": { + "type": "date" + }, + "updatedBy": { + "type": "keyword" } - }, - "canvas-workpad": { - "dynamic": "false", - "properties": { - "@created": { - "type": "date" + } + }, + "api_key_pending_invalidation": { + "properties": { + "apiKeyId": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + } + } + }, + "apm-indices": { + "properties": { + "apm_oss": { + "properties": { + "errorIndices": { + "type": "keyword" + }, + "metricsIndices": { + "type": "keyword" + }, + "onboardingIndices": { + "type": "keyword" + }, + "sourcemapIndices": { + "type": "keyword" + }, + "spanIndices": { + "type": "keyword" + }, + "transactionIndices": { + "type": "keyword" + } + } + } + } + }, + "apm-telemetry": { + "dynamic": "false", + "type": "object" + }, + "app_search_telemetry": { + "dynamic": "false", + "type": "object" + }, + "application_usage_daily": { + "dynamic": "false", + "properties": { + "timestamp": { + "type": "date" + } + } + }, + "application_usage_totals": { + "dynamic": "false", + "type": "object" + }, + "application_usage_transactional": { + "dynamic": "false", + "type": "object" + }, + "canvas-element": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "content": { + "type": "text" + }, + "help": { + "type": "text" + }, + "image": { + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } }, - "@timestamp": { - "type": "date" + "type": "text" + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } }, - "id": { - "type": "text", - "index": false + "type": "text" + } + } + }, + "canvas-workpad-template": { + "dynamic": "false", + "properties": { + "help": { + "fields": { + "keyword": { + "type": "keyword" + } }, - "name": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword" - } + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" } - } - } - }, - "config": { - "dynamic": "true", - "properties": { - "accessibility:disableAnimations": { - "type": "boolean" }, - "buildNum": { - "type": "keyword" + "type": "text" + }, + "tags": { + "fields": { + "keyword": { + "type": "keyword" + } }, - "dateFormat:tz": { - "type": "text", + "type": "text" + }, + "template_key": { + "type": "keyword" + } + } + }, + "cases": { + "properties": { + "closed_at": { + "type": "date" + }, + "closed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "connector": { + "properties": { "fields": { - "keyword": { - "type": "keyword", - "ignore_above": 256 + "properties": { + "key": { + "type": "text" + }, + "value": { + "type": "text" + } } + }, + "id": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "type": { + "type": "keyword" } - }, - "defaultIndex": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword", - "ignore_above": 256 + } + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "description": { + "type": "text" + }, + "external_service": { + "properties": { + "connector_id": { + "type": "keyword" + }, + "connector_name": { + "type": "keyword" + }, + "external_id": { + "type": "keyword" + }, + "external_title": { + "type": "text" + }, + "external_url": { + "type": "text" + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } } } - }, - "telemetry:optIn": { - "type": "boolean" + } + }, + "settings": { + "properties": { + "syncAlerts": { + "type": "boolean" + } + } + }, + "status": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } } } - }, - "dashboard": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" + } + }, + "cases-comments": { + "properties": { + "alertId": { + "type": "keyword" + }, + "comment": { + "type": "text" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "index": { + "type": "keyword" + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-configure": { + "properties": { + "closure_type": { + "type": "keyword" + }, + "connector": { + "properties": { + "fields": { + "properties": { + "key": { + "type": "text" + }, + "value": { + "type": "text" + } } + }, + "id": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-connector-mappings": { + "properties": { + "mappings": { + "properties": { + "action_type": { + "type": "keyword" + }, + "source": { + "type": "keyword" + }, + "target": { + "type": "keyword" + } + } + } + } + }, + "cases-user-actions": { + "properties": { + "action": { + "type": "keyword" + }, + "action_at": { + "type": "date" + }, + "action_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "action_field": { + "type": "keyword" + }, + "new_value": { + "type": "text" + }, + "old_value": { + "type": "text" + } + } + }, + "config": { + "dynamic": "false", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "core-usage-stats": { + "dynamic": "false", + "type": "object" + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } + } + }, + "optionsJSON": { + "index": false, + "type": "text" + }, + "panelsJSON": { + "index": false, + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "pause": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "section": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "value": { + "doc_values": false, + "index": false, + "type": "integer" + } + } + }, + "timeFrom": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "timeRestore": { + "doc_values": false, + "index": false, + "type": "boolean" + }, + "timeTo": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "endpoint:user-artifact": { + "properties": { + "body": { + "type": "binary" + }, + "compressionAlgorithm": { + "index": false, + "type": "keyword" + }, + "created": { + "index": false, + "type": "date" + }, + "decodedSha256": { + "index": false, + "type": "keyword" + }, + "decodedSize": { + "index": false, + "type": "long" + }, + "encodedSha256": { + "type": "keyword" + }, + "encodedSize": { + "index": false, + "type": "long" + }, + "encryptionAlgorithm": { + "index": false, + "type": "keyword" + }, + "identifier": { + "type": "keyword" + } + } + }, + "endpoint:user-artifact-manifest": { + "properties": { + "created": { + "index": false, + "type": "date" + }, + "ids": { + "index": false, + "type": "keyword" + }, + "schemaVersion": { + "type": "keyword" + }, + "semanticVersion": { + "index": false, + "type": "keyword" + } + } + }, + "enterprise_search_telemetry": { + "dynamic": "false", + "type": "object" + }, + "epm-packages": { + "properties": { + "es_index_patterns": { + "enabled": false, + "type": "object" + }, + "install_source": { + "type": "keyword" + }, + "install_started_at": { + "type": "date" + }, + "install_status": { + "type": "keyword" + }, + "install_version": { + "type": "keyword" + }, + "installed_es": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" } }, - "optionsJSON": { - "type": "text" + "type": "nested" + }, + "installed_kibana": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } }, - "panelsJSON": { - "type": "text" + "type": "nested" + }, + "internal": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "package_assets": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" + "type": "nested" + }, + "removable": { + "type": "boolean" + }, + "version": { + "type": "keyword" + } + } + }, + "epm-packages-assets": { + "properties": { + "asset_path": { + "type": "keyword" + }, + "data_base64": { + "type": "binary" + }, + "data_utf8": { + "index": false, + "type": "text" + }, + "install_source": { + "type": "keyword" + }, + "media_type": { + "type": "keyword" + }, + "package_name": { + "type": "keyword" + }, + "package_version": { + "type": "keyword" + } + } + }, + "exception-list": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } }, - "value": { - "type": "integer" + "type": "keyword" + } + } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "os_types": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "exception-list-agnostic": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" + } } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "fields": { + "text": { + "type": "text" + } + }, + "type": "keyword" } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "os_types": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" } - }, - "gis-map" : { - "properties" : { - "bounds" : { - "type" : "geo_shape", - "tree" : "quadtree" - }, - "description" : { - "type" : "text" - }, - "layerListJSON" : { - "type" : "text" - }, - "mapStateJSON" : { - "type" : "text" - }, - "title" : { - "type" : "text" - }, - "uiStateJSON" : { - "type" : "text" - }, - "version" : { - "type" : "integer" + } + }, + "file-upload-telemetry": { + "properties": { + "filesUploadedTotalCount": { + "type": "long" + } + } + }, + "fleet-agent-actions": { + "properties": { + "ack_data": { + "type": "text" + }, + "agent_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "data": { + "type": "binary" + }, + "policy_id": { + "type": "keyword" + }, + "policy_revision": { + "type": "integer" + }, + "sent_at": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agent-events": { + "properties": { + "action_id": { + "type": "keyword" + }, + "agent_id": { + "type": "keyword" + }, + "data": { + "type": "text" + }, + "message": { + "type": "text" + }, + "payload": { + "type": "text" + }, + "policy_id": { + "type": "keyword" + }, + "stream_id": { + "type": "keyword" + }, + "subtype": { + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agents": { + "properties": { + "access_api_key_id": { + "type": "keyword" + }, + "active": { + "type": "boolean" + }, + "current_error_events": { + "index": false, + "type": "text" + }, + "default_api_key": { + "type": "binary" + }, + "default_api_key_id": { + "type": "keyword" + }, + "enrolled_at": { + "type": "date" + }, + "last_checkin": { + "type": "date" + }, + "last_checkin_status": { + "type": "keyword" + }, + "last_updated": { + "type": "date" + }, + "local_metadata": { + "type": "flattened" + }, + "packages": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "policy_revision": { + "type": "integer" + }, + "shared_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "unenrolled_at": { + "type": "date" + }, + "unenrollment_started_at": { + "type": "date" + }, + "updated_at": { + "type": "date" + }, + "upgrade_started_at": { + "type": "date" + }, + "upgraded_at": { + "type": "date" + }, + "user_provided_metadata": { + "type": "flattened" + }, + "version": { + "type": "keyword" + } + } + }, + "fleet-enrollment-api-keys": { + "properties": { + "active": { + "type": "boolean" + }, + "api_key": { + "type": "binary" + }, + "api_key_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "expire_at": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "gis-map": { + "dynamic": "false", + "type": "object" + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } } + }, + "legacyIndexPatternRef": { + "index": false, + "type": "text" + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" } - }, - "graph-workspace": { - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } + } + }, + "index-pattern": { + "dynamic": "false", + "properties": { + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "infrastructure-ui-source": { + "dynamic": "false", + "type": "object" + }, + "ingest-agent-policies": { + "properties": { + "description": { + "type": "text" + }, + "is_default": { + "type": "boolean" + }, + "monitoring_enabled": { + "index": false, + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "package_policies": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "status": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest-outputs": { + "properties": { + "ca_sha256": { + "index": false, + "type": "keyword" + }, + "config": { + "type": "flattened" + }, + "config_yaml": { + "type": "text" + }, + "fleet_enroll_password": { + "type": "binary" + }, + "fleet_enroll_username": { + "type": "binary" + }, + "hosts": { + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "ingest-package-policies": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "enabled": { + "type": "boolean" + }, + "inputs": { + "enabled": false, + "properties": { + "compiled_input": { + "type": "flattened" + }, + "config": { + "type": "flattened" + }, + "enabled": { + "type": "boolean" + }, + "streams": { + "properties": { + "compiled_stream": { + "type": "flattened" + }, + "config": { + "type": "flattened" + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + }, + "type": "nested" + }, + "type": { + "type": "keyword" + }, + "vars": { + "type": "flattened" } }, - "numLinks": { - "type": "integer" - }, - "numVertices": { - "type": "integer" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "wsState": { - "type": "text" + "type": "nested" + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "output_id": { + "type": "keyword" + }, + "package": { + "properties": { + "name": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } } + }, + "policy_id": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" } - }, - "index-pattern": { - "properties": { - "fieldFormatMap": { - "type": "text" - }, + } + }, + "ingest_manager_settings": { + "properties": { + "agent_auto_upgrade": { + "type": "keyword" + }, + "has_seen_add_data_notice": { + "index": false, + "type": "boolean" + }, + "kibana_ca_sha256": { + "type": "keyword" + }, + "kibana_urls": { + "type": "keyword" + }, + "package_auto_upgrade": { + "type": "keyword" + } + } + }, + "inventory-view": { + "dynamic": "false", + "type": "object" + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "lens": { + "properties": { + "description": { + "type": "text" + }, + "expression": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "state": { + "type": "flattened" + }, + "title": { + "type": "text" + }, + "visualizationType": { + "type": "keyword" + } + } + }, + "lens-ui-telemetry": { + "properties": { + "count": { + "type": "integer" + }, + "date": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "map": { + "properties": { + "bounds": { + "dynamic": "false", + "type": "object" + }, + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "maps-telemetry": { + "enabled": false, + "type": "object" + }, + "metrics-explorer-view": { + "dynamic": "false", + "type": "object" + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "index-pattern": { "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" + "keyword": { + "ignore_above": 256, + "type": "keyword" + } }, - "sourceFilters": { - "type": "text" + "type": "text" + }, + "space": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } }, - "timeFieldName": { - "type": "keyword" + "type": "text" + }, + "visualization": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } }, - "title": { - "type": "text" + "type": "text" + } + } + }, + "ml-job": { + "properties": { + "datafeed_id": { + "fields": { + "keyword": { + "type": "keyword" + } }, - "type": { - "type": "keyword" + "type": "text" + }, + "job_id": { + "fields": { + "keyword": { + "type": "keyword" + } }, - "typeMeta": { - "type": "keyword" + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "ml-telemetry": { + "properties": { + "file_data_visualizer": { + "properties": { + "index_creation_count": { + "type": "long" + } } } - }, - "kql-telemetry": { - "properties": { - "optInCount": { - "type": "long" - }, - "optOutCount": { - "type": "long" + } + }, + "monitoring-telemetry": { + "properties": { + "reportedClusterUuids": { + "type": "keyword" + } + } + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "originId": { + "type": "keyword" + }, + "query": { + "properties": { + "description": { + "type": "text" + }, + "filters": { + "enabled": false, + "type": "object" + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "index": false, + "type": "keyword" + } } + }, + "timefilter": { + "enabled": false, + "type": "object" + }, + "title": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" } }, - "migrationVersion": { - "dynamic": "true", - "properties": { - "index-pattern": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword", - "ignore_above": 256 - } + "type": "nested" + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "properties": { + "columns": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "description": { + "type": "text" + }, + "grid": { + "enabled": false, + "type": "object" + }, + "hits": { + "doc_values": false, + "index": false, + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" } - }, - "space": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword", - "ignore_above": 256 - } + } + }, + "sort": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "search-session": { + "properties": { + "appId": { + "type": "keyword" + }, + "created": { + "type": "date" + }, + "expires": { + "type": "date" + }, + "idMapping": { + "enabled": false, + "type": "object" + }, + "initialState": { + "enabled": false, + "type": "object" + }, + "name": { + "type": "keyword" + }, + "restoreState": { + "enabled": false, + "type": "object" + }, + "sessionId": { + "type": "keyword" + }, + "status": { + "type": "keyword" + }, + "urlGeneratorId": { + "type": "keyword" + } + } + }, + "search-telemetry": { + "dynamic": "false", + "type": "object" + }, + "security-solution-signals-migration": { + "properties": { + "created": { + "index": false, + "type": "date" + }, + "createdBy": { + "index": false, + "type": "text" + }, + "destinationIndex": { + "index": false, + "type": "keyword" + }, + "error": { + "index": false, + "type": "text" + }, + "sourceIndex": { + "type": "keyword" + }, + "status": { + "index": false, + "type": "keyword" + }, + "taskId": { + "index": false, + "type": "keyword" + }, + "updated": { + "index": false, + "type": "date" + }, + "updatedBy": { + "index": false, + "type": "text" + }, + "version": { + "type": "long" + } + } + }, + "server": { + "dynamic": "false", + "type": "object" + }, + "siem-detection-engine-rule-actions": { + "properties": { + "actions": { + "properties": { + "action_type_id": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" } } + }, + "alertThrottle": { + "type": "keyword" + }, + "ruleAlertId": { + "type": "keyword" + }, + "ruleThrottle": { + "type": "keyword" } - }, - "namespace": { - "type": "keyword" - }, - "search": { - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" + } + }, + "siem-detection-engine-rule-status": { + "properties": { + "alertId": { + "type": "keyword" + }, + "bulkCreateTimeDurations": { + "type": "float" + }, + "gap": { + "type": "text" + }, + "lastFailureAt": { + "type": "date" + }, + "lastFailureMessage": { + "type": "text" + }, + "lastLookBackDate": { + "type": "date" + }, + "lastSuccessAt": { + "type": "date" + }, + "lastSuccessMessage": { + "type": "text" + }, + "searchAfterTimeDurations": { + "type": "float" + }, + "status": { + "type": "keyword" + }, + "statusDate": { + "type": "date" + } + } + }, + "siem-ui-timeline": { + "properties": { + "columns": { + "properties": { + "aggregatable": { + "type": "boolean" + }, + "category": { + "type": "keyword" + }, + "columnHeaderType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "example": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "indexes": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "placeholder": { + "type": "text" + }, + "searchable": { + "type": "boolean" + }, + "type": { + "type": "keyword" + } + } + }, + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "dataProviders": { + "properties": { + "and": { + "properties": { + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "type": { + "type": "text" + } + } + }, + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } } + }, + "type": { + "type": "text" } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" } - } - }, - "server": { - "properties": { - "uuid": { - "type": "keyword" + }, + "dateRange": { + "properties": { + "end": { + "type": "date" + }, + "start": { + "type": "date" + } } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "disabledFeatures": { - "type": "keyword" - }, - "initials": { - "type": "keyword" - }, - "name": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword", - "ignore_above": 2048 + }, + "description": { + "type": "text" + }, + "eventType": { + "type": "keyword" + }, + "excludedRowRendererIds": { + "type": "text" + }, + "favorite": { + "properties": { + "favoriteDate": { + "type": "date" + }, + "fullName": { + "type": "text" + }, + "keySearch": { + "type": "text" + }, + "userName": { + "type": "text" + } + } + }, + "filters": { + "properties": { + "exists": { + "type": "text" + }, + "match_all": { + "type": "text" + }, + "meta": { + "properties": { + "alias": { + "type": "text" + }, + "controlledBy": { + "type": "text" + }, + "disabled": { + "type": "boolean" + }, + "field": { + "type": "text" + }, + "formattedValue": { + "type": "text" + }, + "index": { + "type": "keyword" + }, + "key": { + "type": "keyword" + }, + "negate": { + "type": "boolean" + }, + "params": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "text" + } } + }, + "missing": { + "type": "text" + }, + "query": { + "type": "text" + }, + "range": { + "type": "text" + }, + "script": { + "type": "text" } } - } - }, - "spaceId": { - "type": "keyword" - }, - "telemetry": { - "properties": { - "enabled": { - "type": "boolean" + }, + "indexNames": { + "type": "text" + }, + "kqlMode": { + "type": "keyword" + }, + "kqlQuery": { + "properties": { + "filterQuery": { + "properties": { + "kuery": { + "properties": { + "expression": { + "type": "text" + }, + "kind": { + "type": "keyword" + } + } + }, + "serializedQuery": { + "type": "text" + } + } + } + } + }, + "savedQueryId": { + "type": "keyword" + }, + "sort": { + "dynamic": "false", + "properties": { + "columnId": { + "type": "keyword" + }, + "columnType": { + "type": "keyword" + }, + "sortDirection": { + "type": "keyword" + } } + }, + "status": { + "type": "keyword" + }, + "templateTimelineId": { + "type": "text" + }, + "templateTimelineVersion": { + "type": "integer" + }, + "timelineType": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" } - }, - "timelion-sheet": { - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } + } + }, + "siem-ui-timeline-note": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "note": { + "type": "text" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-pinned-event": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "space": { + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "imageUrl": { + "index": false, + "type": "text" + }, + "initials": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" } }, - "timelion_chart_height": { - "type": "integer" - }, - "timelion_columns": { - "type": "integer" - }, - "timelion_interval": { - "type": "keyword" - }, - "timelion_other_interval": { - "type": "keyword" - }, - "timelion_rows": { - "type": "integer" - }, - "timelion_sheet": { - "type": "text" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" + "type": "text" + } + } + }, + "spaceId": { + "type": "keyword" + }, + "spaces-usage-stats": { + "dynamic": "false", + "type": "object" + }, + "tag": { + "properties": { + "color": { + "type": "text" + }, + "description": { + "type": "text" + }, + "name": { + "type": "text" + } + } + }, + "telemetry": { + "properties": { + "allowChangingOptInStatus": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "lastReported": { + "type": "date" + }, + "lastVersionChecked": { + "type": "keyword" + }, + "reportFailureCount": { + "type": "integer" + }, + "reportFailureVersion": { + "type": "keyword" + }, + "sendUsageFrom": { + "type": "keyword" + }, + "userHasSeenNotice": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" } - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "url": { - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" + } + }, + "type": { + "type": "keyword" + }, + "ui-counter": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "upgrade-assistant-reindex-operation": { + "properties": { + "errorMessage": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } }, - "createDate": { - "type": "date" + "type": "text" + }, + "indexName": { + "type": "keyword" + }, + "lastCompletedStep": { + "type": "long" + }, + "locked": { + "type": "date" + }, + "newIndexName": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } }, - "url": { - "type": "text", - "fields": { - "keyword": { - "type": "keyword", - "ignore_above": 2048 + "type": "text" + }, + "reindexOptions": { + "properties": { + "openAndClose": { + "type": "boolean" + }, + "queueSettings": { + "properties": { + "queuedAt": { + "type": "long" + }, + "startedAt": { + "type": "long" + } } } } - } - }, - "visualization": { - "properties": { - "description": { - "type": "text" + }, + "reindexTaskId": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" + "type": "text" + }, + "reindexTaskPercComplete": { + "type": "float" + }, + "runningReindexCount": { + "type": "integer" + }, + "status": { + "type": "integer" + } + } + }, + "upgrade-assistant-telemetry": { + "properties": { + "features": { + "properties": { + "deprecation_logging": { + "properties": { + "enabled": { + "null_value": true, + "type": "boolean" + } } } - }, - "savedSearchId": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" + } + }, + "ui_open": { + "properties": { + "cluster": { + "null_value": 0, + "type": "long" + }, + "indices": { + "null_value": 0, + "type": "long" + }, + "overview": { + "null_value": 0, + "type": "long" + } + } + }, + "ui_reindex": { + "properties": { + "close": { + "null_value": 0, + "type": "long" + }, + "open": { + "null_value": 0, + "type": "long" + }, + "start": { + "null_value": 0, + "type": "long" + }, + "stop": { + "null_value": 0, + "type": "long" + } } } - }, - "query": { - "properties": { - "title": { - "type": "text" - }, - "description": { - "type": "text" - }, - "query": { - "properties": { - "language": { - "type": "keyword" - }, - "query": { - "type": "keyword", - "index": false - } + } + }, + "uptime-dynamic-settings": { + "dynamic": "false", + "type": "object" + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" } }, - "filters": { - "type": "object", - "enabled": false - }, - "timefilter": { - "type": "object", - "enabled": false + "type": "text" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "index": false, + "type": "text" + } } + }, + "savedSearchRefName": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "index": false, + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "index": false, + "type": "text" } } + }, + "workplace_search_telemetry": { + "dynamic": "false", + "type": "object" } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } } } -} +} \ No newline at end of file diff --git a/x-pack/test/functional/page_objects/index.ts b/x-pack/test/functional/page_objects/index.ts index 4c523ec5706e..20b8acb9d450 100644 --- a/x-pack/test/functional/page_objects/index.ts +++ b/x-pack/test/functional/page_objects/index.ts @@ -38,6 +38,7 @@ import { SpaceSelectorPageProvider } from './space_selector_page'; import { IngestPipelinesPageProvider } from './ingest_pipelines_page'; import { TagManagementPageProvider } from './tag_management_page'; import { NavigationalSearchProvider } from './navigational_search'; +import { SearchSessionsPageProvider } from './search_sessions_management_page'; // just like services, PageObjects are defined as a map of // names to Providers. Merge in Kibana's or pick specific ones @@ -64,6 +65,7 @@ export const pageObjects = { apiKeys: ApiKeysPageProvider, licenseManagement: LicenseManagementPageProvider, indexManagement: IndexManagementPageProvider, + searchSessionsManagement: SearchSessionsPageProvider, indexLifecycleManagement: IndexLifecycleManagementPageProvider, tagManagement: TagManagementPageProvider, snapshotRestore: SnapshotRestorePageProvider, diff --git a/x-pack/test/functional/page_objects/search_sessions_management_page.ts b/x-pack/test/functional/page_objects/search_sessions_management_page.ts new file mode 100644 index 000000000000..99c3be82a214 --- /dev/null +++ b/x-pack/test/functional/page_objects/search_sessions_management_page.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 { FtrProviderContext } from '../ftr_provider_context'; + +export function SearchSessionsPageProvider({ getService, getPageObjects }: FtrProviderContext) { + const find = getService('find'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common']); + + return { + async goTo() { + await PageObjects.common.navigateToApp('management/kibana/search_sessions'); + }, + + async refresh() { + await testSubjects.click('sessionManagementRefreshBtn'); + }, + + async getList() { + const table = await find.byCssSelector('table'); + const allRows = await table.findAllByTestSubject('searchSessionsRow'); + + return Promise.all( + allRows.map(async (row) => { + const $ = await row.parseDomContent(); + const viewCell = await row.findByTestSubject('sessionManagementNameCol'); + const actionsCell = await row.findByTestSubject('sessionManagementActionsCol'); + return { + name: $.findTestSubject('sessionManagementNameCol').text(), + status: $.findTestSubject('sessionManagementStatusLabel').attr('data-test-status'), + mainUrl: $.findTestSubject('sessionManagementNameCol').text(), + created: $.findTestSubject('sessionManagementCreatedCol').text(), + expires: $.findTestSubject('sessionManagementExpiresCol').text(), + app: $.findTestSubject('sessionManagementAppIcon').attr('data-test-app-id'), + view: async () => { + await viewCell.click(); + }, + reload: async () => { + await actionsCell.click(); + await find.clickByCssSelector( + '[data-test-subj="sessionManagementPopoverAction-reload"]' + ); + }, + cancel: async () => { + await actionsCell.click(); + await find.clickByCssSelector( + '[data-test-subj="sessionManagementPopoverAction-cancel"]' + ); + await PageObjects.common.clickConfirmOnModal(); + }, + }; + }) + ); + }, + }; +} diff --git a/x-pack/test/functional/services/ml/data_visualizer_table.ts b/x-pack/test/functional/services/ml/data_visualizer_table.ts index f8623842a596..ad4625ed4dcb 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_table.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_table.ts @@ -246,7 +246,8 @@ export function MachineLearningDataVisualizerTableProvider( public async assertNumberFieldContents( fieldName: string, docCountFormatted: string, - topValuesCount: number + topValuesCount: number, + checkDistributionPreviewExist = true ) { await this.assertRowExists(fieldName); await this.assertFieldDocCount(fieldName, docCountFormatted); @@ -257,7 +258,9 @@ export function MachineLearningDataVisualizerTableProvider( await testSubjects.existOrFail(this.detailsSelector(fieldName, 'mlTopValues')); await this.assertTopValuesContents(fieldName, topValuesCount); - await this.assertDistributionPreviewExist(fieldName); + if (checkDistributionPreviewExist) { + await this.assertDistributionPreviewExist(fieldName); + } await this.ensureDetailsClosed(fieldName); } @@ -320,5 +323,19 @@ export function MachineLearningDataVisualizerTableProvider( await this.assertTextFieldContents(fieldName, docCountFormatted, exampleCount); } } + + public async ensureNumRowsPerPage(n: 10 | 25 | 100) { + const paginationButton = 'mlDataVisualizerTable > tablePaginationPopoverButton'; + await retry.tryForTime(10000, async () => { + await testSubjects.existOrFail(paginationButton); + await testSubjects.click(paginationButton); + await testSubjects.click(`tablePagination-${n}-rows`); + + const visibleTexts = await testSubjects.getVisibleText(paginationButton); + + const [, pagination] = visibleTexts.split(': '); + expect(pagination).to.eql(n.toString()); + }); + } })(); } diff --git a/x-pack/test/functional_cors/config.ts b/x-pack/test/functional_cors/config.ts index 737cccc5ae33..a5bece6c292e 100644 --- a/x-pack/test/functional_cors/config.ts +++ b/x-pack/test/functional_cors/config.ts @@ -6,11 +6,14 @@ import Url from 'url'; import Path from 'path'; -import getPort from 'get-port'; import type { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { kbnTestConfig } from '@kbn/test'; import { pageObjects } from '../functional/page_objects'; +const pluginPort = process.env.TEST_CORS_SERVER_PORT + ? parseInt(process.env.TEST_CORS_SERVER_PORT, 10) + : 5699; + export default async function ({ readConfigFile }: FtrConfigProviderContext) { const kibanaFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); @@ -27,7 +30,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { }; const { protocol, hostname } = kbnTestConfig.getUrlParts(); - const pluginPort = await getPort(); const originUrl = Url.format({ protocol, hostname, diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts index ab2270b4ce70..72f325bfc2d6 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts @@ -17,8 +17,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const comboBox = getService('comboBox'); const supertest = getService('supertest'); - // FLAKY: https://github.com/elastic/kibana/issues/88796 - describe.skip('Connectors', function () { + describe('Connectors', function () { const objectRemover = new ObjectRemover(supertest); before(async () => { @@ -285,7 +284,17 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.setValue('nameInput', connectorName); - await comboBox.set('connectorIndexesComboBox', indexName); + await retry.try(async () => { + // At times we find the driver controlling the ComboBox in tests + // can select the wrong item, this ensures we always select the correct index + await comboBox.set('connectorIndexesComboBox', indexName); + expect( + await comboBox.isOptionSelected( + await testSubjects.find('connectorIndexesComboBox'), + indexName + ) + ).to.be(true); + }); await find.clickByCssSelector('[data-test-subj="saveNewActionButton"]:not(disabled)'); await pageObjects.common.closeToast(); diff --git a/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/plugin.ts b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/plugin.ts index 0326adb90775..bdb32c7eab75 100644 --- a/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/plugin.ts +++ b/x-pack/test/plugin_api_integration/plugins/sample_task_plugin/server/plugin.ts @@ -167,6 +167,9 @@ export class SampleTaskManagerFixturePlugin }, async beforeMarkRunning(context) { + if (context.taskInstance?.params?.originalParams?.throwOnMarkAsRunning) { + throw new Error(`Sample task ${context.taskInstance.id} threw on MarkAsRunning`); + } return context; }, }); diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts index 7f4585fad472..d94efa2ae10b 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_management.ts @@ -524,6 +524,21 @@ export default function ({ getService }: FtrProviderContext) { }); }); + it('should increment attempts when task fails on markAsRunning', async () => { + const originalTask = await scheduleTask({ + taskType: 'sampleTask', + params: { throwOnMarkAsRunning: true }, + }); + + await delay(DEFAULT_POLL_INTERVAL * 3); + + await retry.try(async () => { + const task = await currentTask(originalTask.id); + expect(task.attempts).to.eql(3); + expect(task.status).to.eql('failed'); + }); + }); + it('should return a task run error result when trying to run a non-existent task', async () => { // runNow should fail const failedRunNowResult = await runTaskNow({ diff --git a/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json b/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json index d9d5c6f9c580..32cae675dea7 100644 --- a/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json +++ b/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json @@ -502,3 +502,119 @@ "type": "doc" } } + +{ + "type": "doc", + "value": { + "index": ".kibana", + "id": "resolvetype:exact-match", + "source": { + "type": "resolvetype", + "updated_at": "2017-09-21T18:51:23.794Z", + "resolvetype": { + "title": "Resolve outcome exactMatch" + }, + "namespaces": ["default", "space_1", "space_2"] + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "id": "resolvetype:alias-match-newid", + "source": { + "type": "resolvetype", + "updated_at": "2017-09-21T18:51:23.794Z", + "resolvetype": { + "title": "Resolve outcome aliasMatch" + }, + "namespaces": ["default", "space_1", "space_2"] + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "id": "legacy-url-alias:space_1:resolvetype:alias-match", + "source": { + "type": "legacy-url-alias", + "updated_at": "2017-09-21T18:51:23.794Z", + "legacy-url-alias": { + "targetNamespace": "space_1", + "targetType": "resolvetype", + "targetId": "alias-match-newid" + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "id": "legacy-url-alias:space_1:resolvetype:disabled", + "source": { + "type": "legacy-url-alias", + "updated_at": "2017-09-21T18:51:23.794Z", + "legacy-url-alias": { + "targetNamespace": "space_1", + "targetType": "resolvetype", + "targetId": "alias-match-newid", + "disabled": true + } + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "id": "resolvetype:conflict", + "source": { + "type": "resolvetype", + "updated_at": "2017-09-21T18:51:23.794Z", + "resolvetype": { + "title": "Resolve outcome conflict (1 of 2)" + }, + "namespaces": ["default", "space_1", "space_2"] + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "id": "resolvetype:conflict-newid", + "source": { + "type": "resolvetype", + "updated_at": "2017-09-21T18:51:23.794Z", + "resolvetype": { + "title": "Resolve outcome conflict (2 of 2)" + }, + "namespaces": ["default", "space_1", "space_2"] + } + } +} + +{ + "type": "doc", + "value": { + "index": ".kibana", + "id": "legacy-url-alias:space_1:resolvetype:conflict", + "source": { + "type": "legacy-url-alias", + "updated_at": "2017-09-21T18:51:23.794Z", + "legacy-url-alias": { + "targetNamespace": "space_1", + "targetType": "resolvetype", + "targetId": "conflict-newid" + } + } + } +} diff --git a/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json b/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json index 73f0e536b929..561c2ecc56fa 100644 --- a/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json +++ b/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/mappings.json @@ -176,6 +176,28 @@ } } }, + "legacy-url-alias": { + "properties": { + "targetNamespace": { + "type": "keyword" + }, + "targetType": { + "type": "keyword" + }, + "targetId": { + "type": "keyword" + }, + "lastResolved": { + "type": "date" + }, + "resolveCounter": { + "type": "integer" + }, + "disabled": { + "type": "boolean" + } + } + }, "namespace": { "type": "keyword" }, @@ -185,6 +207,13 @@ "originId": { "type": "keyword" }, + "resolvetype": { + "properties": { + "title": { + "type": "text" + } + } + }, "search": { "properties": { "columns": { diff --git a/x-pack/test/saved_object_api_integration/common/fixtures/saved_object_test_plugin/server/plugin.ts b/x-pack/test/saved_object_api_integration/common/fixtures/saved_object_test_plugin/server/plugin.ts index 45880635586a..d311e539b168 100644 --- a/x-pack/test/saved_object_api_integration/common/fixtures/saved_object_test_plugin/server/plugin.ts +++ b/x-pack/test/saved_object_api_integration/common/fixtures/saved_object_test_plugin/server/plugin.ts @@ -64,6 +64,13 @@ export class Plugin { namespaceType: 'single', mappings, }); + core.savedObjects.registerType({ + name: 'resolvetype', + hidden: false, + namespaceType: 'multiple', + management, + mappings, + }); } public start() { diff --git a/x-pack/test/saved_object_api_integration/common/suites/resolve.ts b/x-pack/test/saved_object_api_integration/common/suites/resolve.ts new file mode 100644 index 000000000000..250a3b19710a --- /dev/null +++ b/x-pack/test/saved_object_api_integration/common/suites/resolve.ts @@ -0,0 +1,138 @@ +/* + * 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 { SuperTest } from 'supertest'; +import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; +import { SPACES } from '../lib/spaces'; +import { + createRequest, + expectResponses, + getUrlPrefix, + getTestTitle, +} from '../lib/saved_object_test_utils'; +import { ExpectResponseBody, TestCase, TestDefinition, TestSuite } from '../lib/types'; + +const { + DEFAULT: { spaceId: DEFAULT_SPACE_ID }, + SPACE_1: { spaceId: SPACE_1_ID }, + SPACE_2: { spaceId: SPACE_2_ID }, +} = SPACES; + +export interface ResolveTestDefinition extends TestDefinition { + request: { type: string; id: string }; +} +export type ResolveTestSuite = TestSuite; +export interface ResolveTestCase extends TestCase { + expectedOutcome?: 'exactMatch' | 'aliasMatch' | 'conflict'; + expectedId?: string; +} + +const EACH_SPACE = [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID]; + +export const TEST_CASES = Object.freeze({ + EXACT_MATCH: Object.freeze({ + type: 'resolvetype', + id: 'exact-match', + expectedNamespaces: EACH_SPACE, + expectedOutcome: 'exactMatch' as 'exactMatch', + expectedId: 'exact-match', + }), + ALIAS_MATCH: Object.freeze({ + type: 'resolvetype', + id: 'alias-match', + expectedNamespaces: EACH_SPACE, + expectedOutcome: 'aliasMatch' as 'aliasMatch', + expectedId: 'alias-match-newid', + }), + CONFLICT: Object.freeze({ + type: 'resolvetype', + id: 'conflict', + expectedNamespaces: EACH_SPACE, + expectedOutcome: 'conflict' as 'conflict', // only in space 1, where the alias exists + expectedId: 'conflict', + }), + DISABLED: Object.freeze({ + type: 'resolvetype', + id: 'disabled', + }), + DOES_NOT_EXIST: Object.freeze({ + type: 'resolvetype', + id: 'does-not-exist', + }), + HIDDEN: CASES.HIDDEN, +}); + +export function resolveTestSuiteFactory(esArchiver: any, supertest: SuperTest) { + const expectSavedObjectForbidden = expectResponses.forbiddenTypes('get'); + const expectResponseBody = (testCase: ResolveTestCase): ExpectResponseBody => async ( + response: Record + ) => { + if (testCase.failure === 403) { + await expectSavedObjectForbidden(testCase.type)(response); + } else { + // permitted + const object = response.body.saved_object || response.body; // errors do not have a saved_object field + const { expectedId: id, expectedOutcome } = testCase; + await expectResponses.permitted(object, { ...testCase, ...(id && { id }) }); + if (expectedOutcome && !testCase.failure) { + expect(response.body.outcome).to.eql(expectedOutcome); + } + } + }; + const createTestDefinitions = ( + testCases: ResolveTestCase | ResolveTestCase[], + forbidden: boolean, + options?: { + spaceId?: string; + responseBodyOverride?: ExpectResponseBody; + } + ): ResolveTestDefinition[] => { + let cases = Array.isArray(testCases) ? testCases : [testCases]; + if (forbidden) { + // override the expected result in each test case + cases = cases.map((x) => ({ ...x, failure: 403 })); + } + return cases.map((x) => ({ + title: getTestTitle(x), + responseStatusCode: x.failure ?? 200, + request: createRequest(x), + responseBody: options?.responseBodyOverride || expectResponseBody(x), + })); + }; + + const makeResolveTest = (describeFn: Mocha.SuiteFunction) => ( + description: string, + definition: ResolveTestSuite + ) => { + const { user, spaceId = SPACES.DEFAULT.spaceId, tests } = definition; + + describeFn(description, () => { + before(() => esArchiver.load('saved_objects/spaces')); + after(() => esArchiver.unload('saved_objects/spaces')); + + for (const test of tests) { + it(`should return ${test.responseStatusCode} ${test.title}`, async () => { + const { type, id } = test.request; + await supertest + .get(`${getUrlPrefix(spaceId)}/api/saved_objects/resolve/${type}/${id}`) + .auth(user?.username, user?.password) + .expect(test.responseStatusCode) + .then(test.responseBody); + }); + } + }); + }; + + const addTests = makeResolveTest(describe); + // @ts-ignore + addTests.only = makeResolveTest(describe.only); + + return { + addTests, + createTestDefinitions, + }; +} 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 3cc6b85cb97c..5e9e499ffea1 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 @@ -28,6 +28,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./import')); loadTestFile(require.resolve('./resolve_import_errors')); + loadTestFile(require.resolve('./resolve')); loadTestFile(require.resolve('./update')); }); } diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/resolve.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/resolve.ts new file mode 100644 index 000000000000..94df364c9017 --- /dev/null +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/resolve.ts @@ -0,0 +1,82 @@ +/* + * 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 { SPACES } from '../../common/lib/spaces'; +import { testCaseFailures, getTestScenarios } from '../../common/lib/saved_object_test_utils'; +import { TestUser } from '../../common/lib/types'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + resolveTestSuiteFactory, + TEST_CASES as CASES, + ResolveTestDefinition, +} from '../../common/suites/resolve'; + +const { + SPACE_1: { spaceId: SPACE_1_ID }, +} = SPACES; +const { fail404 } = testCaseFailures; + +const createTestCases = (spaceId: string) => { + // for each permitted (non-403) outcome, if failure !== undefined then we expect + // to receive an error; otherwise, we expect to receive a success result + const normalTypes = [ + CASES.EXACT_MATCH, + { ...CASES.ALIAS_MATCH, ...fail404(spaceId !== SPACE_1_ID) }, + { + ...CASES.CONFLICT, + ...(spaceId !== SPACE_1_ID && { expectedOutcome: 'exactMatch' as 'exactMatch' }), + }, + { ...CASES.DISABLED, ...fail404() }, + { ...CASES.DOES_NOT_EXIST, ...fail404() }, + ]; + const hiddenType = [{ ...CASES.HIDDEN, ...fail404() }]; + const allTypes = normalTypes.concat(hiddenType); + return { normalTypes, hiddenType, allTypes }; +}; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertestWithoutAuth'); + const esArchiver = getService('esArchiver'); + + const { addTests, createTestDefinitions } = resolveTestSuiteFactory(esArchiver, supertest); + const createTests = (spaceId: string) => { + const { normalTypes, hiddenType, allTypes } = createTestCases(spaceId); + // use singleRequest to reduce execution time and/or test combined cases + return { + unauthorized: createTestDefinitions(allTypes, true), + authorized: [ + createTestDefinitions(normalTypes, false), + createTestDefinitions(hiddenType, true), + ].flat(), + superuser: createTestDefinitions(allTypes, false), + }; + }; + + describe('_resolve', () => { + getTestScenarios().securityAndSpaces.forEach(({ spaceId, users }) => { + const suffix = ` within the ${spaceId} space`; + const { unauthorized, authorized, superuser } = createTests(spaceId); + const _addTests = (user: TestUser, tests: ResolveTestDefinition[]) => { + addTests(`${user.description}${suffix}`, { user, spaceId, tests }); + }; + + [users.noAccess, users.legacyAll, users.allAtOtherSpace].forEach((user) => { + _addTests(user, unauthorized); + }); + [ + users.dualAll, + users.dualRead, + users.allGlobally, + users.readGlobally, + users.allAtSpace, + users.readAtSpace, + ].forEach((user) => { + _addTests(user, authorized); + }); + _addTests(users.superuser, superuser); + }); + }); +} 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 c52ba3f59571..46b099248076 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 @@ -28,6 +28,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./import')); loadTestFile(require.resolve('./resolve_import_errors')); + loadTestFile(require.resolve('./resolve')); loadTestFile(require.resolve('./update')); }); } diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/resolve.ts b/x-pack/test/saved_object_api_integration/security_only/apis/resolve.ts new file mode 100644 index 000000000000..9f37f9788107 --- /dev/null +++ b/x-pack/test/saved_object_api_integration/security_only/apis/resolve.ts @@ -0,0 +1,73 @@ +/* + * 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 { testCaseFailures, getTestScenarios } from '../../common/lib/saved_object_test_utils'; +import { TestUser } from '../../common/lib/types'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + resolveTestSuiteFactory, + TEST_CASES as CASES, + ResolveTestDefinition, +} from '../../common/suites/resolve'; + +const { fail404 } = testCaseFailures; + +const createTestCases = () => { + // for each permitted (non-403) outcome, if failure !== undefined then we expect + // to receive an error; otherwise, we expect to receive a success result + const normalTypes = [ + { ...CASES.EXACT_MATCH }, + { ...CASES.ALIAS_MATCH, ...fail404() }, + { ...CASES.CONFLICT, expectedOutcome: 'exactMatch' as 'exactMatch' }, + { ...CASES.DISABLED, ...fail404() }, + { ...CASES.DOES_NOT_EXIST, ...fail404() }, + ]; + const hiddenType = [{ ...CASES.HIDDEN, ...fail404() }]; + const allTypes = normalTypes.concat(hiddenType); + return { normalTypes, hiddenType, allTypes }; +}; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertestWithoutAuth'); + const esArchiver = getService('esArchiver'); + + const { addTests, createTestDefinitions } = resolveTestSuiteFactory(esArchiver, supertest); + const createTests = () => { + const { normalTypes, hiddenType, allTypes } = createTestCases(); + return { + unauthorized: createTestDefinitions(allTypes, true), + authorized: [ + createTestDefinitions(normalTypes, false), + createTestDefinitions(hiddenType, true), + ].flat(), + superuser: createTestDefinitions(allTypes, false), + }; + }; + + describe('_resolve', () => { + getTestScenarios().security.forEach(({ users }) => { + const { unauthorized, authorized, superuser } = createTests(); + const _addTests = (user: TestUser, tests: ResolveTestDefinition[]) => { + addTests(user.description, { user, tests }); + }; + + [ + users.noAccess, + users.legacyAll, + users.allAtDefaultSpace, + users.readAtDefaultSpace, + users.allAtSpace1, + users.readAtSpace1, + ].forEach((user) => { + _addTests(user, unauthorized); + }); + [users.dualAll, users.dualRead, users.allGlobally, users.readGlobally].forEach((user) => { + _addTests(user, authorized); + }); + _addTests(users.superuser, superuser); + }); + }); +} diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/index.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/index.ts index c8050733fc6e..137596bc20c4 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/index.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/index.ts @@ -20,6 +20,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./import')); loadTestFile(require.resolve('./resolve_import_errors')); + loadTestFile(require.resolve('./resolve')); loadTestFile(require.resolve('./update')); }); } diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/resolve.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/resolve.ts new file mode 100644 index 000000000000..a6f76fc80044 --- /dev/null +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/resolve.ts @@ -0,0 +1,47 @@ +/* + * 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 { SPACES } from '../../common/lib/spaces'; +import { testCaseFailures, getTestScenarios } from '../../common/lib/saved_object_test_utils'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { resolveTestSuiteFactory, TEST_CASES as CASES } from '../../common/suites/resolve'; + +const { + SPACE_1: { spaceId: SPACE_1_ID }, +} = SPACES; +const { fail404 } = testCaseFailures; + +const createTestCases = (spaceId: string) => [ + // for each outcome, if failure !== undefined then we expect to receive + // an error; otherwise, we expect to receive a success result + CASES.EXACT_MATCH, + { ...CASES.ALIAS_MATCH, ...fail404(spaceId !== SPACE_1_ID) }, + { + ...CASES.CONFLICT, + ...(spaceId !== SPACE_1_ID && { expectedOutcome: 'exactMatch' as 'exactMatch' }), + }, + { ...CASES.DISABLED, ...fail404() }, + { ...CASES.HIDDEN, ...fail404() }, + { ...CASES.DOES_NOT_EXIST, ...fail404() }, +]; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + const { addTests, createTestDefinitions } = resolveTestSuiteFactory(esArchiver, supertest); + const createTests = (spaceId: string) => { + const testCases = createTestCases(spaceId); + return createTestDefinitions(testCases, false, { spaceId }); + }; + + describe('_resolve', () => { + getTestScenarios().spaces.forEach(({ spaceId }) => { + const tests = createTests(spaceId); + addTests(`within the ${spaceId} space`, { spaceId, tests }); + }); + }); +} diff --git a/x-pack/test/send_search_to_background_integration/config.ts b/x-pack/test/send_search_to_background_integration/config.ts index c14678febd81..bad818bb6966 100644 --- a/x-pack/test/send_search_to_background_integration/config.ts +++ b/x-pack/test/send_search_to_background_integration/config.ts @@ -23,6 +23,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { testFiles: [ resolve(__dirname, './tests/apps/dashboard/async_search'), resolve(__dirname, './tests/apps/discover'), + resolve(__dirname, './tests/apps/management/search_sessions'), ], kbnTestServer: { diff --git a/x-pack/test/send_search_to_background_integration/services/index.ts b/x-pack/test/send_search_to_background_integration/services/index.ts index 91b0ad502d05..35eed5a218b4 100644 --- a/x-pack/test/send_search_to_background_integration/services/index.ts +++ b/x-pack/test/send_search_to_background_integration/services/index.ts @@ -9,5 +9,5 @@ import { SendToBackgroundProvider } from './send_to_background'; export const services = { ...functionalServices, - sendToBackground: SendToBackgroundProvider, + searchSessions: SendToBackgroundProvider, }; diff --git a/x-pack/test/send_search_to_background_integration/services/send_to_background.ts b/x-pack/test/send_search_to_background_integration/services/send_to_background.ts index 319496239de3..8c3261c2074a 100644 --- a/x-pack/test/send_search_to_background_integration/services/send_to_background.ts +++ b/x-pack/test/send_search_to_background_integration/services/send_to_background.ts @@ -4,11 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { FtrProviderContext } from '../ftr_provider_context'; +import { SavedObjectsFindResponse } from 'src/core/server'; import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper'; +import { FtrProviderContext } from '../ftr_provider_context'; -const SEND_TO_BACKGROUND_TEST_SUBJ = 'searchSessionIndicator'; -const SEND_TO_BACKGROUND_POPOVER_CONTENT_TEST_SUBJ = 'searchSessionIndicatorPopoverContainer'; +const SEARCH_SESSION_INDICATOR_TEST_SUBJ = 'searchSessionIndicator'; +const SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ = 'searchSessionIndicatorPopoverContainer'; type SessionStateType = | 'none' @@ -21,22 +22,24 @@ type SessionStateType = export function SendToBackgroundProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); + const log = getService('log'); const retry = getService('retry'); const browser = getService('browser'); + const supertest = getService('supertest'); return new (class SendToBackgroundService { public async find(): Promise { - return testSubjects.find(SEND_TO_BACKGROUND_TEST_SUBJ); + return testSubjects.find(SEARCH_SESSION_INDICATOR_TEST_SUBJ); } public async exists(): Promise { - return testSubjects.exists(SEND_TO_BACKGROUND_TEST_SUBJ); + return testSubjects.exists(SEARCH_SESSION_INDICATOR_TEST_SUBJ); } public async expectState(state: SessionStateType) { - return retry.waitFor(`sendToBackground indicator to get into state = ${state}`, async () => { + return retry.waitFor(`searchSessions indicator to get into state = ${state}`, async () => { const currentState = await ( - await testSubjects.find(SEND_TO_BACKGROUND_TEST_SUBJ) + await testSubjects.find(SEARCH_SESSION_INDICATOR_TEST_SUBJ) ).getAttribute('data-state'); return currentState === state; }); @@ -65,23 +68,57 @@ export function SendToBackgroundProvider({ getService }: FtrProviderContext) { await this.ensurePopoverClosed(); } + public async openPopover() { + await this.ensurePopoverOpened(); + } + private async ensurePopoverOpened() { - const isAlreadyOpen = await testSubjects.exists(SEND_TO_BACKGROUND_POPOVER_CONTENT_TEST_SUBJ); + const isAlreadyOpen = await testSubjects.exists(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ); if (isAlreadyOpen) return; - return retry.waitFor(`sendToBackground popover opened`, async () => { - await testSubjects.click(SEND_TO_BACKGROUND_TEST_SUBJ); - return await testSubjects.exists(SEND_TO_BACKGROUND_POPOVER_CONTENT_TEST_SUBJ); + return retry.waitFor(`searchSessions popover opened`, async () => { + await testSubjects.click(SEARCH_SESSION_INDICATOR_TEST_SUBJ); + return await testSubjects.exists(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ); }); } private async ensurePopoverClosed() { const isAlreadyClosed = !(await testSubjects.exists( - SEND_TO_BACKGROUND_POPOVER_CONTENT_TEST_SUBJ + SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ )); if (isAlreadyClosed) return; - return retry.waitFor(`sendToBackground popover closed`, async () => { + return retry.waitFor(`searchSessions popover closed`, async () => { await browser.pressKeys(browser.keys.ESCAPE); - return !(await testSubjects.exists(SEND_TO_BACKGROUND_POPOVER_CONTENT_TEST_SUBJ)); + return !(await testSubjects.exists(SEARCH_SESSIONS_POPOVER_CONTENT_TEST_SUBJ)); + }); + } + + /* + * This cleanup function should be used by tests that create new background sesions. + * Tests should not end with new background sessions remaining in storage since that interferes with functional tests that check the _find API. + * Alternatively, a test can navigate to `Managment > Search Sessions` and use the UI to delete any created tests. + */ + public async deleteAllSearchSessions() { + log.debug('Deleting created background sessions'); + // ignores 409 errs and keeps retrying + await retry.tryForTime(10000, async () => { + const { body } = await supertest + .post('/internal/session/_find') + .set('kbn-xsrf', 'anything') + .set('kbn-system-request', 'true') + .send({ page: 1, perPage: 10000, sortField: 'created', sortOrder: 'asc' }) + .expect(200); + + const { saved_objects: savedObjects } = body as SavedObjectsFindResponse; + log.debug(`Found created background sessions: ${savedObjects.map(({ id }) => id)}`); + await Promise.all( + savedObjects.map(async (so) => { + log.debug(`Deleting background session: ${so.id}`); + await supertest + .delete(`/internal/session/${so.id}`) + .set(`kbn-xsrf`, `anything`) + .expect(200); + }) + ); }); } })(); diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/send_to_background.ts b/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/send_to_background.ts index 2edaeb1918b2..03635efb6113 100644 --- a/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/send_to_background.ts +++ b/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/send_to_background.ts @@ -14,7 +14,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'header', 'dashboard', 'visChart']); const dashboardPanelActions = getService('dashboardPanelActions'); const browser = getService('browser'); - const sendToBackground = getService('sendToBackground'); + const searchSessions = getService('searchSessions'); describe('send to background', () => { before(async function () { @@ -26,6 +26,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.common.navigateToApp('dashboard'); }); + after(async function () { + await searchSessions.deleteAllSearchSessions(); + }); + it('Restore using non-existing sessionId errors out. Refresh starts a new session and completes.', async () => { await PageObjects.dashboard.loadSavedDashboard('Not Delayed'); const url = await browser.getCurrentUrl(); @@ -33,7 +37,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const savedSessionURL = `${url}&searchSessionId=${fakeSessionId}`; await browser.get(savedSessionURL); await PageObjects.header.waitUntilLoadingHasFinished(); - await sendToBackground.expectState('restored'); + await searchSessions.expectState('restored'); await testSubjects.existOrFail('embeddableErrorLabel'); // expected that panel errors out because of non existing session const session1 = await dashboardPanelActions.getSearchSessionIdByTitle( @@ -41,9 +45,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ); expect(session1).to.be(fakeSessionId); - await sendToBackground.refresh(); + await searchSessions.refresh(); await PageObjects.header.waitUntilLoadingHasFinished(); - await sendToBackground.expectState('completed'); + await searchSessions.expectState('completed'); await testSubjects.missingOrFail('embeddableErrorLabel'); const session2 = await dashboardPanelActions.getSearchSessionIdByTitle( 'Sum of Bytes by Extension' @@ -54,9 +58,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('Saves and restores a session', async () => { await PageObjects.dashboard.loadSavedDashboard('Not Delayed'); await PageObjects.dashboard.waitForRenderComplete(); - await sendToBackground.expectState('completed'); - await sendToBackground.save(); - await sendToBackground.expectState('backgroundCompleted'); + await searchSessions.expectState('completed'); + await searchSessions.save(); + await searchSessions.expectState('backgroundCompleted'); const savedSessionId = await dashboardPanelActions.getSearchSessionIdByTitle( 'Sum of Bytes by Extension' ); @@ -69,7 +73,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.waitForRenderComplete(); // Check that session is restored - await sendToBackground.expectState('restored'); + await searchSessions.expectState('restored'); await testSubjects.missingOrFail('embeddableErrorLabel'); const data = await PageObjects.visChart.getBarChartData('Sum of bytes'); expect(data.length).to.be(5); @@ -77,7 +81,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // switching dashboard to edit mode (or any other non-fetch required) state change // should leave session state untouched await PageObjects.dashboard.switchToEditMode(); - await sendToBackground.expectState('restored'); + await searchSessions.expectState('restored'); }); }); } diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/send_to_background_relative_time.ts b/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/send_to_background_relative_time.ts index 9eb42b74668c..ce6c8978c7d6 100644 --- a/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/send_to_background_relative_time.ts +++ b/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/send_to_background_relative_time.ts @@ -25,7 +25,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const find = getService('find'); const dashboardExpect = getService('dashboardExpect'); const browser = getService('browser'); - const sendToBackground = getService('sendToBackground'); + const searchSessions = getService('searchSessions'); describe('send to background with relative time', () => { before(async () => { @@ -60,9 +60,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.waitForRenderComplete(); await checkSampleDashboardLoaded(); - await sendToBackground.expectState('completed'); - await sendToBackground.save(); - await sendToBackground.expectState('backgroundCompleted'); + await searchSessions.expectState('completed'); + await searchSessions.save(); + await searchSessions.expectState('backgroundCompleted'); const savedSessionId = await dashboardPanelActions.getSearchSessionIdByTitle( '[Flights] Airline Carrier' ); @@ -80,7 +80,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await checkSampleDashboardLoaded(); // Check that session is restored - await sendToBackground.expectState('restored'); + await searchSessions.expectState('restored'); }); }); diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts b/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts index 7d00761b2fa9..f590e4413864 100644 --- a/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts +++ b/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts @@ -20,7 +20,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ]); const dashboardPanelActions = getService('dashboardPanelActions'); const browser = getService('browser'); - const sendToBackground = getService('sendToBackground'); + const searchSessions = getService('searchSessions'); describe('dashboard in space', () => { describe('Send to background in space', () => { @@ -73,9 +73,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.waitForRenderComplete(); - await sendToBackground.expectState('completed'); - await sendToBackground.save(); - await sendToBackground.expectState('backgroundCompleted'); + await searchSessions.expectState('completed'); + await searchSessions.save(); + await searchSessions.expectState('backgroundCompleted'); const savedSessionId = await dashboardPanelActions.getSearchSessionIdByTitle( 'A Pie in another space' ); @@ -88,7 +88,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.dashboard.waitForRenderComplete(); // Check that session is restored - await sendToBackground.expectState('restored'); + await searchSessions.expectState('restored'); await testSubjects.missingOrFail('embeddableErrorLabel'); }); }); diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/discover/sessions_in_space.ts b/x-pack/test/send_search_to_background_integration/tests/apps/discover/sessions_in_space.ts index 5c94a50e0a84..6384afb17959 100644 --- a/x-pack/test/send_search_to_background_integration/tests/apps/discover/sessions_in_space.ts +++ b/x-pack/test/send_search_to_background_integration/tests/apps/discover/sessions_in_space.ts @@ -20,7 +20,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'timePicker', ]); const browser = getService('browser'); - const sendToBackground = getService('sendToBackground'); + const searchSessions = getService('searchSessions'); describe('discover in space', () => { describe('Send to background in space', () => { @@ -74,9 +74,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitForDocTableLoadingComplete(); - await sendToBackground.expectState('completed'); - await sendToBackground.save(); - await sendToBackground.expectState('backgroundCompleted'); + await searchSessions.expectState('completed'); + await searchSessions.save(); + await searchSessions.expectState('backgroundCompleted'); await inspector.open(); const savedSessionId = await ( @@ -92,7 +92,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitForDocTableLoadingComplete(); // Check that session is restored - await sendToBackground.expectState('restored'); + await searchSessions.expectState('restored'); await testSubjects.missingOrFail('embeddableErrorLabel'); }); }); diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/index.ts b/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/index.ts new file mode 100644 index 000000000000..6a11a15f3156 --- /dev/null +++ b/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/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 { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile, getService }: FtrProviderContext) { + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + + describe('search sessions management', function () { + this.tags('ciGroup3'); + + before(async () => { + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.load('dashboard/async_search'); + await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); + await kibanaServer.uiSettings.replace({ 'search:timeout': 10000 }); + }); + + loadTestFile(require.resolve('./sessions_management')); + }); +} diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts b/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts new file mode 100644 index 000000000000..f06e8eba0bf6 --- /dev/null +++ b/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts @@ -0,0 +1,148 @@ +/* + * 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, getPageObjects }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects([ + 'common', + 'header', + 'dashboard', + 'visChart', + 'searchSessionsManagement', + ]); + const searchSessions = getService('searchSessions'); + const esArchiver = getService('esArchiver'); + const retry = getService('retry'); + + describe('Search search sessions Management UI', () => { + describe('New search sessions', () => { + before(async () => { + await PageObjects.common.navigateToApp('dashboard'); + }); + + after(async () => { + await searchSessions.deleteAllSearchSessions(); + }); + + it('Saves a session and verifies it in the Management app', async () => { + await PageObjects.dashboard.loadSavedDashboard('Not Delayed'); + await PageObjects.dashboard.waitForRenderComplete(); + await searchSessions.expectState('completed'); + await searchSessions.save(); + await searchSessions.expectState('backgroundCompleted'); + + await searchSessions.openPopover(); + await searchSessions.viewSearchSessions(); + + await retry.waitFor(`wait for first item to complete`, async function () { + const s = await PageObjects.searchSessionsManagement.getList(); + return s[0] && s[0].status === 'complete'; + }); + + // find there is only one item in the table which is the newly saved session + const searchSessionList = await PageObjects.searchSessionsManagement.getList(); + expect(searchSessionList.length).to.be(1); + expect(searchSessionList[0].expires).not.to.eql('--'); + expect(searchSessionList[0].name).to.eql('Not Delayed'); + + // navigate to dashboard + await searchSessionList[0].view(); + + // embeddable has loaded + await testSubjects.existOrFail('embeddablePanelHeading-SumofBytesbyExtension'); + await PageObjects.dashboard.waitForRenderComplete(); + + // search session was restored + await searchSessions.expectState('restored'); + }); + + it('Reloads as new session from management', async () => { + await PageObjects.searchSessionsManagement.goTo(); + + const searchSessionList = await PageObjects.searchSessionsManagement.getList(); + + expect(searchSessionList.length).to.be(1); + await searchSessionList[0].reload(); + + // embeddable has loaded + await PageObjects.dashboard.waitForRenderComplete(); + + // new search session was completed + await searchSessions.expectState('completed'); + }); + + it('Cancels a session from management', async () => { + await PageObjects.searchSessionsManagement.goTo(); + + const searchSessionList = await PageObjects.searchSessionsManagement.getList(); + + expect(searchSessionList.length).to.be(1); + await searchSessionList[0].cancel(); + + // TODO: update this once canceling doesn't delete the object! + await retry.waitFor(`wait for list to be empty`, async function () { + const s = await PageObjects.searchSessionsManagement.getList(); + + return s.length === 0; + }); + }); + }); + + describe('Archived search sessions', () => { + before(async () => { + await PageObjects.searchSessionsManagement.goTo(); + }); + + after(async () => { + await searchSessions.deleteAllSearchSessions(); + }); + + it('shows no items found', async () => { + const searchSessionList = await PageObjects.searchSessionsManagement.getList(); + expect(searchSessionList.length).to.be(0); + }); + + it('autorefreshes and shows items on the server', async () => { + await esArchiver.load('data/search_sessions'); + + const searchSessionList = await PageObjects.searchSessionsManagement.getList(); + + expect(searchSessionList.length).to.be(10); + + expect(searchSessionList.map((ss) => ss.created)).to.eql([ + '25 Dec, 2020, 00:00:00', + '24 Dec, 2020, 00:00:00', + '23 Dec, 2020, 00:00:00', + '22 Dec, 2020, 00:00:00', + '21 Dec, 2020, 00:00:00', + '20 Dec, 2020, 00:00:00', + '19 Dec, 2020, 00:00:00', + '18 Dec, 2020, 00:00:00', + '17 Dec, 2020, 00:00:00', + '16 Dec, 2020, 00:00:00', + ]); + + expect(searchSessionList.map((ss) => ss.expires)).to.eql([ + '--', + '--', + '--', + '23 Dec, 2020, 00:00:00', + '22 Dec, 2020, 00:00:00', + '--', + '--', + '--', + '18 Dec, 2020, 00:00:00', + '17 Dec, 2020, 00:00:00', + ]); + + await esArchiver.unload('data/search_sessions'); + }); + }); + }); +} diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index cfe328236cd3..699ff64af3f8 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -36,6 +36,8 @@ { "path": "../../src/plugins/ui_actions/tsconfig.json" }, { "path": "../../src/plugins/url_forwarding/tsconfig.json" }, + { "path": "../plugins/actions/tsconfig.json"}, + { "path": "../plugins/alerts/tsconfig.json"}, { "path": "../plugins/console_extensions/tsconfig.json" }, { "path": "../plugins/data_enhanced/tsconfig.json" }, { "path": "../plugins/global_search/tsconfig.json" }, @@ -46,10 +48,12 @@ { "path": "../plugins/licensing/tsconfig.json" }, { "path": "../plugins/task_manager/tsconfig.json" }, { "path": "../plugins/telemetry_collection_xpack/tsconfig.json" }, + { "path": "../plugins/triggers_actions_ui/tsconfig.json" }, { "path": "../plugins/ui_actions_enhanced/tsconfig.json" }, { "path": "../plugins/spaces/tsconfig.json" }, { "path": "../plugins/security/tsconfig.json" }, { "path": "../plugins/encrypted_saved_objects/tsconfig.json" }, + { "path": "../plugins/stack_alerts/tsconfig.json" }, { "path": "../plugins/beats_management/tsconfig.json" }, { "path": "../plugins/cloud/tsconfig.json" }, { "path": "../plugins/saved_objects_tagging/tsconfig.json" }, diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json index 812ead39ba41..ae1277302366 100644 --- a/x-pack/tsconfig.json +++ b/x-pack/tsconfig.json @@ -2,6 +2,8 @@ "extends": "../tsconfig.base.json", "include": ["mocks.ts", "typings/**/*", "plugins/**/*", "tasks/**/*"], "exclude": [ + "plugins/actions/**/*", + "plugins/alerts/**/*", "plugins/apm/e2e/cypress/**/*", "plugins/apm/ftr_e2e/**/*", "plugins/apm/scripts/**/*", @@ -21,10 +23,12 @@ "plugins/task_manager/**/*", "plugins/telemetry_collection_xpack/**/*", "plugins/translations/**/*", + "plugins/triggers_actions_ui/**/*", "plugins/ui_actions_enhanced/**/*", "plugins/vis_type_timeseries_enhanced/**/*", "plugins/spaces/**/*", "plugins/security/**/*", + "plugins/stack_alerts/**/*", "plugins/encrypted_saved_objects/**/*", "plugins/beats_management/**/*", "plugins/cloud/**/*", @@ -92,6 +96,10 @@ { "path": "./plugins/beats_management/tsconfig.json" }, { "path": "./plugins/cloud/tsconfig.json" }, { "path": "./plugins/saved_objects_tagging/tsconfig.json" }, - { "path": "./plugins/global_search_bar/tsconfig.json" } + { "path": "./plugins/global_search_bar/tsconfig.json" }, + { "path": "./plugins/actions/tsconfig.json"}, + { "path": "./plugins/alerts/tsconfig.json"}, + { "path": "./plugins/triggers_actions_ui/tsconfig.json"}, + { "path": "./plugins/stack_alerts/tsconfig.json"} ] } diff --git a/x-pack/tsconfig.refs.json b/x-pack/tsconfig.refs.json index edee8e228f76..02623b11ce31 100644 --- a/x-pack/tsconfig.refs.json +++ b/x-pack/tsconfig.refs.json @@ -1,6 +1,8 @@ { "include": [], "references": [ + { "path": "./plugins/actions/tsconfig.json"}, + { "path": "./plugins/alerts/tsconfig.json"}, { "path": "./plugins/dashboard_enhanced/tsconfig.json" }, { "path": "./plugins/licensing/tsconfig.json" }, { "path": "./plugins/console_extensions/tsconfig.json" }, @@ -18,8 +20,10 @@ { "path": "./plugins/ui_actions_enhanced/tsconfig.json" }, { "path": "./plugins/vis_type_timeseries_enhanced/tsconfig.json" }, { "path": "./plugins/translations/tsconfig.json" }, + { "path": "./plugins/triggers_actions_ui/tsconfig.json"}, { "path": "./plugins/spaces/tsconfig.json" }, { "path": "./plugins/security/tsconfig.json" }, + { "path": "./plugins/stack_alerts/tsconfig.json"}, { "path": "./plugins/encrypted_saved_objects/tsconfig.json" }, { "path": "./plugins/beats_management/tsconfig.json" }, { "path": "./plugins/cloud/tsconfig.json" }, diff --git a/yarn.lock b/yarn.lock index f25806063157..21501721d96c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9027,6 +9027,11 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3" integrity sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw== +callsites@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + camel-case@3.0.x, camel-case@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" @@ -28640,10 +28645,10 @@ vega-functions@^5.10.0: vega-time "^2.0.4" vega-util "^1.16.0" -vega-functions@~5.11.0: - version "5.11.0" - resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.11.0.tgz#a590d016f93c81730bdbc336b377231d7ae48569" - integrity sha512-/p0QIDiA3RaUZ7drxHuClpDQCrIScSHJlY0oo0+GFYGfp+lvb29Ox1T4a+wtqeCp6NRaTWry+EwDxojnshTZIQ== +vega-functions@^5.12.0, vega-functions@~5.12.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/vega-functions/-/vega-functions-5.12.0.tgz#44bf08a7b20673dc8cf51d6781c8ea1399501668" + integrity sha512-3hljmGs+gR7TbO/yYuvAP9P5laKISf1GKk4yRHLNdM61fWgKm8pI3f6LY2Hvq9cHQFTiJ3/5/Bx2p1SX5R4quQ== dependencies: d3-array "^2.7.1" d3-color "^2.0.0" @@ -28651,8 +28656,8 @@ vega-functions@~5.11.0: vega-dataflow "^5.7.3" vega-expression "^4.0.1" vega-scale "^7.1.1" - vega-scenegraph "^4.9.2" - vega-selections "^5.2.0" + vega-scenegraph "^4.9.3" + vega-selections "^5.3.0" vega-statistics "^1.7.9" vega-time "^2.0.4" vega-util "^1.16.0" @@ -28719,16 +28724,16 @@ vega-loader@^4.3.2, vega-loader@^4.3.3, vega-loader@~4.4.0: vega-format "^1.0.4" vega-util "^1.16.0" -vega-parser@~6.1.2: - version "6.1.2" - resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.2.tgz#7f25751177e38c3239560a9c427ded8d2ba617bb" - integrity sha512-aGyZrNzPrBruEb/WhemKDuDjQsIkMDGIgnSJci0b+9ZVxjyAzMl7UfGbiYorPiJlnIercjUJbMoFD6fCIf4gqQ== +vega-parser@~6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.3.tgz#df72785e4b086eceb90ee6219a399210933b507b" + integrity sha512-8oiVhhW26GQ4GZBvolId8FVFvhn3s1KGgPlD7Z+4P2wkV+xe5Nqu0TEJ20F/cn3b88fd0Vj48X3BH3dlSeKNFg== dependencies: vega-dataflow "^5.7.3" vega-event-selector "^2.0.6" - vega-functions "^5.10.0" + vega-functions "^5.12.0" vega-scale "^7.1.1" - vega-util "^1.15.2" + vega-util "^1.16.0" vega-projection@^1.4.5, vega-projection@~1.4.5: version "1.4.5" @@ -28767,7 +28772,7 @@ vega-scale@^7.0.3, vega-scale@^7.1.1, vega-scale@~7.1.1: vega-time "^2.0.4" vega-util "^1.15.2" -vega-scenegraph@^4.9.2, vega-scenegraph@~4.9.2: +vega-scenegraph@^4.9.2: version "4.9.2" resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.9.2.tgz#83b1dbc34a9ab5595c74d547d6d95849d74451ed" integrity sha512-epm1CxcB8AucXQlSDeFnmzy0FCj+HV2k9R6ch2lfLRln5lPLEfgJWgFcFhVf5jyheY0FSeHH52Q5zQn1vYI1Ow== @@ -28779,6 +28784,18 @@ vega-scenegraph@^4.9.2, vega-scenegraph@~4.9.2: vega-scale "^7.1.1" vega-util "^1.15.2" +vega-scenegraph@^4.9.3, vega-scenegraph@~4.9.3: + version "4.9.3" + resolved "https://registry.yarnpkg.com/vega-scenegraph/-/vega-scenegraph-4.9.3.tgz#c4720550ea7ff5c8d9d0690f47fe2640547cfc6b" + integrity sha512-lBvqLbXqrqRCTGJmSgzZC/tLR/o+TXfakbdhDzNdpgTavTaQ65S/67Gpj5hPpi77DvsfZUIY9lCEeO37aJhy0Q== + dependencies: + d3-path "^2.0.0" + d3-shape "^2.0.0" + vega-canvas "^1.2.5" + vega-loader "^4.3.3" + vega-scale "^7.1.1" + vega-util "^1.15.2" + vega-schema-url-parser@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/vega-schema-url-parser/-/vega-schema-url-parser-2.1.0.tgz#847f9cf9f1624f36f8a51abc1adb41ebc6673cb4" @@ -28792,10 +28809,10 @@ vega-selections@^5.1.5: vega-expression "^4.0.0" vega-util "^1.15.2" -vega-selections@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.2.0.tgz#d85968d1bccc175fd92661c91d88151ffd5ade83" - integrity sha512-Xf3nTTJHRGw4tQMbt+0sBI/7WkEIzPG9E4HXkZk5Y9Q2HsGRVLmrAEXHSfpENrBLWTBZk/uvmP9rKDG7cbcTrg== +vega-selections@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/vega-selections/-/vega-selections-5.3.0.tgz#810f2e7b7642fa836cf98b2e5dcc151093b1f6a7" + integrity sha512-vC4NPsuN+IffruFXfH0L3i2A51RgG4PqpLv85TvrEAIYnSkyKDE4bf+wVraR3aPdnLLkc3+tYuMi6le5FmThIA== dependencies: vega-expression "^4.0.1" vega-util "^1.16.0" @@ -28894,10 +28911,10 @@ vega-wordcloud@~4.1.3: vega-statistics "^1.7.9" vega-util "^1.15.2" -vega@^5.18.0: - version "5.18.0" - resolved "https://registry.yarnpkg.com/vega/-/vega-5.18.0.tgz#98645e5d3bd5267d66ea3e701d99dcff63cfff8a" - integrity sha512-ysqouhboWNXSuQNN7W5IGOXsnEJNFVX5duCi0tTwRsFLc61FshpqVh4+4VoXg5pH0ZCxwpqbOwd2ULZWjJTx6g== +vega@^5.19.1: + version "5.19.1" + resolved "https://registry.yarnpkg.com/vega/-/vega-5.19.1.tgz#64c8350740fe1a11d56cc6617ab3a76811fd704c" + integrity sha512-UE6/c9q9kzuz4HULFuU9HscBASoZa+zcXqGKdbQP545Nwmhd078QpcH+wZsq9lYfiTxmFtzLK/a0OH0zhkghvA== dependencies: vega-crossfilter "~4.0.5" vega-dataflow "~5.7.3" @@ -28906,17 +28923,17 @@ vega@^5.18.0: vega-expression "~4.0.1" vega-force "~4.0.7" vega-format "~1.0.4" - vega-functions "~5.11.0" + vega-functions "~5.12.0" vega-geo "~4.3.8" vega-hierarchy "~4.0.9" vega-label "~1.0.0" vega-loader "~4.4.0" - vega-parser "~6.1.2" + vega-parser "~6.1.3" vega-projection "~1.4.5" vega-regression "~1.0.9" vega-runtime "~6.1.3" vega-scale "~7.1.1" - vega-scenegraph "~4.9.2" + vega-scenegraph "~4.9.3" vega-statistics "~1.7.9" vega-time "~2.0.4" vega-transforms "~4.9.3"