diff --git a/.backportrc.json b/.backportrc.json index af064451595c8..77456cf33f625 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -1,5 +1,5 @@ { "upstream": "elastic/kibana", - "branches": [{ "name": "7.x", "checked": true }, "7.5", "7.4", "7.3", "7.2", "7.1", "7.0", "6.8", "6.7", "6.6", "6.5", "6.4", "6.3", "6.2", "6.1", "6.0", "5.6"], + "branches": [{ "name": "7.x", "checked": true }, "7.6", "7.5", "7.4", "7.3", "7.2", "7.1", "7.0", "6.8", "6.7", "6.6", "6.5", "6.4", "6.3", "6.2", "6.1", "6.0", "5.6"], "labels": ["backport"] } diff --git a/.eslintignore b/.eslintignore index 90155ca9cb681..e5b17567b562c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -9,7 +9,7 @@ bower_components /built_assets /html_docs /src/plugins/data/common/es_query/kuery/ast/_generated_/** -/src/fixtures/vislib/mock_data +src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data /src/legacy/ui/public/angular-bootstrap /src/legacy/ui/public/flot-charts /test/fixtures/scenarios diff --git a/.eslintrc.js b/.eslintrc.js index a7bb204da4775..2c5804da053a6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -77,7 +77,7 @@ module.exports = { }, }, { - files: ['src/legacy/core_plugins/kbn_vislib_vis_types/**/*.{js,ts,tsx}'], + files: ['src/legacy/core_plugins/vis_type_vislib/**/*.{js,ts,tsx}'], rules: { 'react-hooks/exhaustive-deps': 'off', }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index acfb7307f49c4..ed5721e8756e8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -117,21 +117,21 @@ /x-pack/test/api_integration/apis/security/ @elastic/kibana-security # Kibana Localization -/src/dev/i18n @elastic/kibana-localization +/src/dev/i18n/ @elastic/kibana-localization # Pulse /packages/kbn-analytics/ @elastic/pulse /src/legacy/core_plugins/ui_metric/ @elastic/pulse /src/plugins/usage_collection/ @elastic/pulse -/x-pack/legacy/plugins/telemetry @elastic/pulse +/x-pack/legacy/plugins/telemetry/ @elastic/pulse # Kibana Alerting Services -/x-pack/legacy/plugins/alerting @elastic/kibana-alerting-services -/x-pack/legacy/plugins/actions @elastic/kibana-alerting-services -/x-pack/legacy/plugins/task_manager @elastic/kibana-alerting-services -/x-pack/test/alerting_api_integration @elastic/kibana-alerting-services -/x-pack/test/plugin_api_integration/plugins/task_manager @elastic/kibana-alerting-services -/x-pack/test/plugin_api_integration/test_suites/task_manager @elastic/kibana-alerting-services +/x-pack/legacy/plugins/alerting/ @elastic/kibana-alerting-services +/x-pack/legacy/plugins/actions/ @elastic/kibana-alerting-services +/x-pack/plugins/task_manager/ @elastic/kibana-alerting-services +/x-pack/test/alerting_api_integration/ @elastic/kibana-alerting-services +/x-pack/test/plugin_api_integration/plugins/task_manager/ @elastic/kibana-alerting-services +/x-pack/test/plugin_api_integration/test_suites/task_manager/ @elastic/kibana-alerting-services /x-pack/legacy/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services /x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/ @elastic/kibana-alerting-services /x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/ @elastic/kibana-alerting-services diff --git a/.i18nrc.json b/.i18nrc.json index 4bc0f773ee8b5..6986d36e8e94f 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -21,7 +21,7 @@ "interpreter": "src/legacy/core_plugins/interpreter", "kbn": "src/legacy/core_plugins/kibana", "kbnDocViews": "src/legacy/core_plugins/kbn_doc_views", - "kbnVislibVisTypes": "src/legacy/core_plugins/kbn_vislib_vis_types", + "kbnVislibVisTypes": "src/legacy/core_plugins/vis_type_vislib", "management": ["src/legacy/core_plugins/management", "src/plugins/management"], "kibana_react": "src/legacy/core_plugins/kibana_react", "kibana-react": "src/plugins/kibana_react", diff --git a/.sass-lint.yml b/.sass-lint.yml index f6c0f5bb83fcb..fba2c003484f6 100644 --- a/.sass-lint.yml +++ b/.sass-lint.yml @@ -2,7 +2,7 @@ files: include: - 'src/legacy/core_plugins/metrics/**/*.s+(a|c)ss' - 'src/legacy/core_plugins/timelion/**/*.s+(a|c)ss' - - 'src/legacy/ui/public/vislib/**/*.s+(a|c)ss' + - 'src/legacy/core_plugins/vis_type_vislib/**/*.s+(a|c)ss' - 'x-pack/legacy/plugins/rollup/**/*.s+(a|c)ss' - 'x-pack/legacy/plugins/security/**/*.s+(a|c)ss' - 'x-pack/legacy/plugins/canvas/**/*.s+(a|c)ss' diff --git a/docs/development/core/server/kibana-plugin-server.deprecationsettings.doclinkskey.md b/docs/development/core/server/kibana-plugin-server.deprecationsettings.doclinkskey.md new file mode 100644 index 0000000000000..4296d0d229988 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.deprecationsettings.doclinkskey.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [DeprecationSettings](./kibana-plugin-server.deprecationsettings.md) > [docLinksKey](./kibana-plugin-server.deprecationsettings.doclinkskey.md) + +## DeprecationSettings.docLinksKey property + +Key to documentation links + +Signature: + +```typescript +docLinksKey: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.deprecationsettings.md b/docs/development/core/server/kibana-plugin-server.deprecationsettings.md new file mode 100644 index 0000000000000..64a654c1bcea8 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.deprecationsettings.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [DeprecationSettings](./kibana-plugin-server.deprecationsettings.md) + +## DeprecationSettings interface + +UiSettings deprecation field options. + +Signature: + +```typescript +export interface DeprecationSettings +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [docLinksKey](./kibana-plugin-server.deprecationsettings.doclinkskey.md) | string | Key to documentation links | +| [message](./kibana-plugin-server.deprecationsettings.message.md) | string | Deprecation message | + diff --git a/docs/development/core/server/kibana-plugin-server.deprecationsettings.message.md b/docs/development/core/server/kibana-plugin-server.deprecationsettings.message.md new file mode 100644 index 0000000000000..ed52929c3551e --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.deprecationsettings.message.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [DeprecationSettings](./kibana-plugin-server.deprecationsettings.md) > [message](./kibana-plugin-server.deprecationsettings.message.md) + +## DeprecationSettings.message property + +Deprecation message + +Signature: + +```typescript +message: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.imagevalidation.maxsize.md b/docs/development/core/server/kibana-plugin-server.imagevalidation.maxsize.md new file mode 100644 index 0000000000000..9b03924ad2e0f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.imagevalidation.maxsize.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ImageValidation](./kibana-plugin-server.imagevalidation.md) > [maxSize](./kibana-plugin-server.imagevalidation.maxsize.md) + +## ImageValidation.maxSize property + +Signature: + +```typescript +maxSize: { + length: number; + description: string; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-server.imagevalidation.md b/docs/development/core/server/kibana-plugin-server.imagevalidation.md new file mode 100644 index 0000000000000..8d81a7eae1915 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.imagevalidation.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [ImageValidation](./kibana-plugin-server.imagevalidation.md) + +## ImageValidation interface + +Signature: + +```typescript +export interface ImageValidation +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [maxSize](./kibana-plugin-server.imagevalidation.maxsize.md) | {
length: number;
description: string;
} | | + diff --git a/docs/development/core/server/kibana-plugin-server.iscopedrenderingclient.render.md b/docs/development/core/server/kibana-plugin-server.iscopedrenderingclient.render.md index 1bc78dd84571d..42cbc59c536a6 100644 --- a/docs/development/core/server/kibana-plugin-server.iscopedrenderingclient.render.md +++ b/docs/development/core/server/kibana-plugin-server.iscopedrenderingclient.render.md @@ -9,14 +9,14 @@ Generate a `KibanaResponse` which renders an HTML page bootstrapped with the `co Signature: ```typescript -render(options?: IRenderOptions): Promise; +render(options?: Pick): Promise; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| options | IRenderOptions | | +| options | Pick<IRenderOptions, 'includeUserSettings'> | | Returns: diff --git a/docs/development/core/server/kibana-plugin-server.md b/docs/development/core/server/kibana-plugin-server.md index 5e28643843af3..00ab83123319a 100644 --- a/docs/development/core/server/kibana-plugin-server.md +++ b/docs/development/core/server/kibana-plugin-server.md @@ -56,6 +56,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [DeprecationAPIClientParams](./kibana-plugin-server.deprecationapiclientparams.md) | | | [DeprecationAPIResponse](./kibana-plugin-server.deprecationapiresponse.md) | | | [DeprecationInfo](./kibana-plugin-server.deprecationinfo.md) | | +| [DeprecationSettings](./kibana-plugin-server.deprecationsettings.md) | UiSettings deprecation field options. | | [DiscoveredPlugin](./kibana-plugin-server.discoveredplugin.md) | Small container object used to expose information about discovered plugins that may or may not have been started. | | [ElasticsearchError](./kibana-plugin-server.elasticsearcherror.md) | | | [ElasticsearchServiceSetup](./kibana-plugin-server.elasticsearchservicesetup.md) | | @@ -69,6 +70,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ICspConfig](./kibana-plugin-server.icspconfig.md) | CSP configuration for use in Kibana. | | [IKibanaResponse](./kibana-plugin-server.ikibanaresponse.md) | A response data object, expected to returned as a result of [RequestHandler](./kibana-plugin-server.requesthandler.md) execution | | [IKibanaSocket](./kibana-plugin-server.ikibanasocket.md) | A tiny abstraction for TCP socket. | +| [ImageValidation](./kibana-plugin-server.imagevalidation.md) | | | [IndexSettingsDeprecationInfo](./kibana-plugin-server.indexsettingsdeprecationinfo.md) | | | [IRenderOptions](./kibana-plugin-server.irenderoptions.md) | | | [IRouter](./kibana-plugin-server.irouter.md) | Registers route handlers for specified resource path and method. See [RouteConfig](./kibana-plugin-server.routeconfig.md) and [RequestHandler](./kibana-plugin-server.requesthandler.md) for more information about arguments to route registrations. | @@ -140,6 +142,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SessionStorage](./kibana-plugin-server.sessionstorage.md) | Provides an interface to store and retrieve data across requests. | | [SessionStorageCookieOptions](./kibana-plugin-server.sessionstoragecookieoptions.md) | Configuration used to create HTTP session storage based on top of cookie mechanism. | | [SessionStorageFactory](./kibana-plugin-server.sessionstoragefactory.md) | SessionStorage factory to bind one to an incoming request | +| [StringValidation](./kibana-plugin-server.stringvalidation.md) | | | [UiSettingsParams](./kibana-plugin-server.uisettingsparams.md) | UiSettings parameters defined by the plugins. | | [UiSettingsServiceSetup](./kibana-plugin-server.uisettingsservicesetup.md) | | | [UiSettingsServiceStart](./kibana-plugin-server.uisettingsservicestart.md) | | diff --git a/docs/development/core/server/kibana-plugin-server.stringvalidation.md b/docs/development/core/server/kibana-plugin-server.stringvalidation.md new file mode 100644 index 0000000000000..cc52c853ce248 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.stringvalidation.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [StringValidation](./kibana-plugin-server.stringvalidation.md) + +## StringValidation interface + +Signature: + +```typescript +export interface StringValidation +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [message](./kibana-plugin-server.stringvalidation.message.md) | string | | +| [regexString](./kibana-plugin-server.stringvalidation.regexstring.md) | string | | + diff --git a/docs/development/core/server/kibana-plugin-server.stringvalidation.message.md b/docs/development/core/server/kibana-plugin-server.stringvalidation.message.md new file mode 100644 index 0000000000000..a15fe8b931403 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.stringvalidation.message.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [StringValidation](./kibana-plugin-server.stringvalidation.md) > [message](./kibana-plugin-server.stringvalidation.message.md) + +## StringValidation.message property + +Signature: + +```typescript +message: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.stringvalidation.regexstring.md b/docs/development/core/server/kibana-plugin-server.stringvalidation.regexstring.md new file mode 100644 index 0000000000000..e19560237f77d --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.stringvalidation.regexstring.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [StringValidation](./kibana-plugin-server.stringvalidation.md) > [regexString](./kibana-plugin-server.stringvalidation.regexstring.md) + +## StringValidation.regexString property + +Signature: + +```typescript +regexString: string; +``` diff --git a/docs/maps/images/gs_add_cloropeth_layer.png b/docs/maps/images/gs_add_cloropeth_layer.png index b2ee35f025b6f..2800a5a2d2584 100644 Binary files a/docs/maps/images/gs_add_cloropeth_layer.png and b/docs/maps/images/gs_add_cloropeth_layer.png differ diff --git a/docs/maps/images/gs_add_es_layer.png b/docs/maps/images/gs_add_es_layer.png deleted file mode 100644 index b80ddc47d7e5d..0000000000000 Binary files a/docs/maps/images/gs_add_es_layer.png and /dev/null differ diff --git a/docs/maps/images/sample_data_web_logs.png b/docs/maps/images/sample_data_web_logs.png index dda0926a07dfe..3b0c2ba3f12c0 100644 Binary files a/docs/maps/images/sample_data_web_logs.png and b/docs/maps/images/sample_data_web_logs.png differ diff --git a/docs/maps/maps-aggregations.asciidoc b/docs/maps/maps-aggregations.asciidoc index 627fd49dafa51..05d8c0c605e7f 100644 --- a/docs/maps/maps-aggregations.asciidoc +++ b/docs/maps/maps-aggregations.asciidoc @@ -24,13 +24,13 @@ The *Grid aggregation* source uses {ref}/search-aggregations-bucket-geotilegrid- You can symbolize grid aggregation metrics as: -*Points*:: Creates a <> with a point for each gridded cell. -The point location is the weighted centroid for all geo-points in the gridded cell. - *Grid rectangles*:: Creates a <> with a bounding box polygon for each gridded cell. *Heat map*:: Creates a <> that clusters the weighted centroids for each gridded cell. +*Clusters*:: Creates a <> with a cluster symbol for each gridded cell. +The cluster location is the weighted centroid for all geo-points in the gridded cell. + [role="xpack"] [[maps-top-hits-aggregation]] diff --git a/docs/maps/maps-getting-started.asciidoc b/docs/maps/maps-getting-started.asciidoc index e6908ca773a2f..b13eeebe56fd8 100644 --- a/docs/maps/maps-getting-started.asciidoc +++ b/docs/maps/maps-getting-started.asciidoc @@ -68,14 +68,16 @@ and lighter shades symbolize countries with less traffic. . Click the *EMS Boundaries* data source. . From the *Layer* dropdown menu, select *World Countries*. . Click the *Add layer* button. -. Set *Layer name* to `Total Requests by Country`. -. Set *Layer transparency* to 0.5. +. Set *Name* to `Total Requests by Country`. +. Set *Opacity* to 50%. +. Click *Add* under *Tooltip fields*. +. In the popover, select *ISO 3166-1 alpha-2 code* and *name* and click *Add*. ===== Join the vector layer with the sample web log index You now have a vector layer containing the world countries. To symbolize countries by web traffic, you'll need to augment the world country features with the count of Elasticsearch weblog documents originating from each country. -To do this, you'll create a <> to link the vector source *World Countries* to +To do this, you'll create a <> to link the vector source *World Countries* to the {es} index `kibana_sample_data_logs` on the shared key iso2 = geo.src. . Click plus image:maps/images/gs_plus_icon.png[] to the right of *Term Joins* label. @@ -83,15 +85,18 @@ the {es} index `kibana_sample_data_logs` on the shared key iso2 = geo.src. . Set *Left field* to *ISO 3166-1 alpha-2 code*. . Set *Right source* to *kibana_sample_data_logs*. . Set *Right field* to *geo.src*. +. Click *and use metric count*. +. Set *Custom label* to *web logs count*. ===== Set the layer style All of the world countries are still a single color because the layer is using <>. To shade the world countries based on which country is sending the most requests, you'll need to use <>. -. Click image:maps/images/gs_link_icon.png[] to the right of *Fill color*. +. Under *Fill color*, change the selected value from *Solid* to *By value*. +. In the field select input, select *web logs count*. . Select the grey color ramp. -. In the field select input, select *count of kibana_sample_data_logs:geo.src*. +. Under *Border color*, change the selected color to *white*. . Click *Save & close*. + Your map now looks like this: @@ -119,9 +124,11 @@ The layer is only visible when users zoom in the map past zoom level 9. . Click the *Documents* data source. . Set *Index pattern* to *kibana_sample_data_logs*. . Click the *Add layer* button. -. Set *Layer name* to `Actual Requests`. -. Set *Zoom range for layer visibility* to the range [9, 24]. -. Set *Layer transparency* to 1. +. Set *Name* to `Actual Requests`. +. Set *Visibilty* to the range [9, 24]. +. Set *Opacity* to 100%. +. Click *Add* under *Tooltip fields*. +. In the popover, select *clientip*, *timestamp*, *host*, *request*, *response*, *machine.os*, *agent*, and *bytes* and click *Add*. . Set *Fill color* to *#2200ff*. . Click *Save & close*. + @@ -150,30 +157,30 @@ image::maps/images/grid_metrics_both.png[] . In the map legend, click *Add layer*. . Click the *Grid aggregation* data source. . Set *Index pattern* to *kibana_sample_data_logs*. -. Set *Show as* to *points*. +. Set *Show as* to *clusters*. . Click the *Add layer* button. -. Set *Layer name* to `Total Requests and Bytes`. -. Set *Zoom range for layer visibility* to the range [0, 9]. -. Set *Layer transparency* to 1. +. Set *Name* to `Total Requests and Bytes`. +. Set *Visibility* to the range [0, 9]. +. Set *Opacity* to 100%. ===== Configure the aggregation metrics -. Click plus image:maps/images/gs_plus_icon.png[] to the right of *Metrics* label. +. Click *Add metric* under of *Metrics* label. . Select *Sum* in the aggregation select. . Select *bytes* in the field select. ===== Set the layer style . In *Layer style*, change *Symbol size*: - .. Set *Min size* to 1. + .. Set *Min size* to 7. .. Set *Max size* to 25. - .. In the field select, select *sum of bytes*. + .. Change the field select from *count* to *sum of bytes*. . Click *Save & close* button. + Your map now looks like this between zoom levels 0 and 9: + [role="screenshot"] -image::maps/images/gs_add_es_layer.png[] +image::maps/images/sample_data_web_logs.png[] [role="xpack"] [[maps-save]] diff --git a/docs/maps/vector-layer.asciidoc b/docs/maps/vector-layer.asciidoc index 1d4ba9912529a..17c57c82b0f17 100644 --- a/docs/maps/vector-layer.asciidoc +++ b/docs/maps/vector-layer.asciidoc @@ -19,7 +19,7 @@ NOTE: Document results are limited to the `index.max_result_window` index settin Use <> to plot large data sets. *Grid aggregation*:: Geospatial data grouped in grids with metrics for each gridded cell. -Set *Show as* to *grid rectangles* or *points*. +Set *Show as* to *grid rectangles* or *clusters*. The index must contain at least one field mapped as {ref}/geo-point.html[geo_point]. *EMS Boundaries*:: Administrative boundaries from https://www.elastic.co/elastic-maps-service[Elastic Maps Service]. diff --git a/docs/user/security/images/mutual-tls-role-mapping.png b/docs/user/security/images/mutual-tls-role-mapping.png new file mode 100644 index 0000000000000..d95ce41e130c2 Binary files /dev/null and b/docs/user/security/images/mutual-tls-role-mapping.png differ diff --git a/docs/user/security/securing-communications/elasticsearch-mutual-tls.asciidoc b/docs/user/security/securing-communications/elasticsearch-mutual-tls.asciidoc new file mode 100644 index 0000000000000..1c7e7080e676b --- /dev/null +++ b/docs/user/security/securing-communications/elasticsearch-mutual-tls.asciidoc @@ -0,0 +1,120 @@ +[role="xpack"] +[[elasticsearch-mutual-tls]] +=== Mutual TLS authentication between {kib} and {es} +++++ +Mutual TLS with {es} +++++ + +In a standard Transport Layer Security (TLS/SSL) configuration, the server presents a signed certificate to authenticate itself to the +client. In a mutual TLS configuration, the client also presents a signed certificate to authenticate itself to the server. + +When {security} is enabled on your cluster, each request that {kib} makes to {es} must be authenticated. Most requests made through {kib} to +{es} are authenticated by using the credentials of the logged-in user. There are, however, a few internal requests that the {kib} server +needs to make to the {es} cluster. For this reason, you must configure credentials for the {kib} server to use for those requests. + +If {kib} has `elasticsearch.username` and `elasticsearch.password` configured, it will attempt to use these to authenticate to {es} via the +{ref}/native-realm.html[Native realm]. However, {kib} also supports mutual TLS authentication with {es} via a {ref}/pki-realm.html[Public +Key Infrastructure (PKI) realm]. To do so, {es} needs to verify the signature on the {kib} client certificate, and it also needs to map the +certificate's distinguished name (DN) to the appropriate `kibana_system` role. + +NOTE: Using a PKI realm is a gold feature. For a comparison of the Elastic license levels, see https://www.elastic.co/subscriptions[the +subscription page]. + +To configure {kib} and {es} to use mutual TLS authentication: + +. <> with a username and password. + +. <>. At a minimum, this requires a server certificate for {es}. + +. Create a client certificate and private key for {kib} to use when connecting to {es}. ++ +-- +NOTE: This is not the same as the <> that {kib} will present to web browsers. + +You may choose to generate a certificate and private key using {ref}/certutil.html[the {es} certutil tool]. At this point, you will have +already set up a certificate authority (CA) to sign the {es} server certificate. You may choose to use the same CA to sign the {kib} client +certificate. You would do this like so: + +[source,sh] +-------------------------------------------------------------------------------- +bin/elasticsearch-certutil cert -ca elastic-stack-ca.p12 -name kibana-client +-------------------------------------------------------------------------------- + +This will generate a certificate and private key in a PKCS #12 keystore named `kibana-client.p12`. The certificate has a Common Name (CN) of +"kibana-client". + +You will also need to use the CA certificate when setting up the PKI realm in {es}. While you could use the CA keystore in the above example +for this purpose, it is bad practice to expose the CA's private key in such a manner. Instead, you can extract the CA certificate (without +its private key) like so: + +[source,sh] +-------------------------------------------------------------------------------- +openssl pkcs12 -in kibana-client.p12 -cacerts -nokeys -out ca.crt +-------------------------------------------------------------------------------- +-- + +. Configure a PKI realm and a Native realm in your {es} cluster: ++ +-- +By default, {es} provides a Native realm. However, to support both a PKI realm (for {kib}) and a Native realm (for end users), you must +configure each realm in `elasticsearch.yml`: + +[source,yaml] +-------------------------------------------------------------------------------- +xpack.security.authc.realms.pki.realm1.order: 1 +xpack.security.authc.realms.pki.realm1.certificate_authorities: "/path/to/ca.crt" +xpack.security.authc.realms.native.realm2.order: 2 +-------------------------------------------------------------------------------- + +-- + +. Configure your {es} cluster to request client certificates: ++ +-- +By default, {es} will not request a client certificate when establishing a TLS connection. To change this, you must set up optional client +certificate authentication in `elasticsearch.yml`: + +[source,yaml] +-------------------------------------------------------------------------------- +xpack.security.http.ssl.client_authentication: "optional" +-------------------------------------------------------------------------------- +-- + +. Restart your {es} cluster. + +. Use {kib} to create a <> for your new client certificate: ++ +-- +This role mapping will assign the `kibana_system` role to any user that matches the included mapping rule, which is set to equal the client +certificate's DN attribute: + +[role="screenshot"] +image:user/security/images/mutual-tls-role-mapping.png["Role mapping for the {kib} client certificate"] +-- + +. Configure {kib} to use the client certificate: ++ +-- +Assuming you used the {es} certutil tool to generate a certificate and private key in a PKCS #12 keystore, add the following values to +`kibana.yml`: + +[source,yaml] +-------------------------------------------------------------------------------- +elasticsearch.ssl.keystore.path: "/path/to/kibana-client.p12" +elasticsearch.ssl.keystore.password: "decryption password" +-------------------------------------------------------------------------------- + +The decryption password should match what you entered when prompted by the {es} certutil tool. + +You must also remove the `elasticsearch.username` and `elasticsearch.password` values from the configuration file. Otherwise, {kib} will +attempt to use those to authenticate via the Native realm. + +TIP: Alternatively, {kib} also supports using a client certificate and private key in PEM format with the `elasticsearch.ssl.certificate` +and `elasticsearch.ssl.key` settings. For more information, see <>. +-- + +. Restart {kib}. + +NOTE: The steps above enable {kib} to authenticate to {es} using a certificate. However, end users will only be able to authenticate to +{kib} with a username and password. To allow end users to authenticate to {kib} using certificates, see <>. diff --git a/docs/user/security/securing-kibana.asciidoc b/docs/user/security/securing-kibana.asciidoc index a68a2ee285ee3..2d07b57bfabe1 100644 --- a/docs/user/security/securing-kibana.asciidoc +++ b/docs/user/security/securing-kibana.asciidoc @@ -88,6 +88,8 @@ xpack.security.session.lifespan: "8h" . Optional: <>. +. Optional: <>. + . Restart {kib}. . [[kibana-roles]]Choose an authentication mechanism and grant users the privileges they need to @@ -141,4 +143,5 @@ NOTE: This must be a user who has been assigned <> instead, which offers more functionality and is easier to use. -If you want to create new coordinate map visualizations, set `xpack.maps.showMapVisualizationTypes` to `true`. +NOTE: Coordinate maps have been replaced with <>, which offers more functionality and is easier to use. -Kibana uses the https://www.elastic.co/elastic-maps-service[Elastic Maps Service] -to display map tiles. To use other tile service providers, configure the <> +To create coordinate maps in Visualize: + +* Set `xpack.maps.showMapVisualizationTypes` to `true`. + +* To display map tiles, {kib} uses the https://www.elastic.co/elastic-maps-service[Elastic Maps Service]. +To use other tile service providers, configure the <> in `kibana.yml`. [float] -[[tilemap-configuration]] -=== Configuration +[[coordinate-map-aggregation]] +=== Supported aggregations -[float] -==== Data +Coordinate maps support the metric and bucket aggregations. [float] -===== Metrics - -The default _metrics_ aggregation for a coordinate map is the *Count* aggregation. You can select any of the following -aggregations as the metrics aggregation: - -*Count*:: The {ref}/search-aggregations-metrics-valuecount-aggregation.html[_count_] aggregation returns a raw count of -the elements in the selected index pattern. -*Average*:: This aggregation returns the {ref}/search-aggregations-metrics-avg-aggregation.html[_average_] of a numeric -field. Select a field from the drop-down. -*Sum*:: The {ref}/search-aggregations-metrics-sum-aggregation.html[_sum_] aggregation returns the total sum of a numeric -field. Select a field from the drop-down. -*Min*:: The {ref}/search-aggregations-metrics-min-aggregation.html[_min_] aggregation returns the minimum value of a -numeric field. Select a field from the drop-down. -*Max*:: The {ref}/search-aggregations-metrics-max-aggregation.html[_max_] aggregation returns the maximum value of a -numeric field. Select a field from the drop-down. -*Unique Count*:: The {ref}/search-aggregations-metrics-cardinality-aggregation.html[_cardinality_] aggregation returns -the number of unique values in a field. Select a field from the drop-down. - -Enter a string in the *Custom Label* field to change the display label. +===== Metric aggregations -[float] -===== Buckets +The following metric aggregations are supported: -Coordinate maps use the {ref}/search-aggregations-bucket-geohashgrid-aggregation.html[_geohash_] aggregation. Select a field, typically coordinates, from the -drop-down. +{ref}/search-aggregations-metrics-valuecount-aggregation.html[Count]:: Returns a raw count of +the elements in the index pattern. The default metrics aggregation for a coordinate map is *Count*. -- The _Change precision on map zoom_ box is checked by default. Uncheck the box to disable this behavior. -The _Precision_ slider determines the granularity of the results displayed on the map. See the documentation -for the {ref}/search-aggregations-bucket-geohashgrid-aggregation.html#_cell_dimensions_at_the_equator[geohash grid] -aggregation for details on the area specified by each precision level. +{ref}/search-aggregations-metrics-avg-aggregation.html[Average]:: Returns the average of a numeric +field. -NOTE: Higher precisions increase memory usage for the browser displaying Kibana as well as for the underlying -Elasticsearch cluster. +{ref}/search-aggregations-metrics-sum-aggregation.html[Sum]:: Returns the total sum of a numeric +field. -- The _place markers off grid (use {ref}/search-aggregations-metrics-geocentroid-aggregation.html[geocentroid])_ box is checked by default. When this box is checked, the markers are -placed in the center of all the documents in that bucket. When unchecked, the markers are placed in the center -of the geohash grid cell. Leaving this checked generally results in a more accurate visualization. +{ref}/search-aggregations-metrics-min-aggregation.html[Min]:: Returns the minimum value of a +numeric field. +{ref}/search-aggregations-metrics-max-aggregation.html[Max]:: Returns the maximum value of a +numeric field. -Enter a string in the *Custom Label* field to change the display label. +{ref}/search-aggregations-metrics-cardinality-aggregation.html[Unique Count]:: Returns +the number of unique values in a field. [float] -==== Options - -*Map type*:: Select one of the following options from the drop-down. -*_Scaled Circle Markers_*:: Scale the size of the markers based on the metric aggregation's value. -*_Shaded Circle Markers_*:: Displays the markers with different shades based on the metric aggregation's value. -*_Shaded Geohash Grid_*:: Displays the rectangular cells of the geohash grid instead of circular markers, with different -shades based on the metric aggregation's value. -*_Heatmap_*:: A heat map applies blurring to the circle markers and applies shading based on the amount of overlap. -Heatmaps have the following options: - -* *Cluster size*: Adjust the size of the heatmap clustering. -* *Show Tooltip*: Check this box to have a tooltip with the values for a given dot when the cursor is on that dot. - -*Desaturate map tiles*:: Desaturate the map's color in order to make the markers stand out more clearly. -*WMS compliant map server*:: Check this box to enable the use of a third-party mapping service that complies with the Web -Map Service (WMS) standard. Specify the following elements: - -* *WMS url*: The URL for the WMS map service. -* *WMS layers*: A comma-separated list of the layers to use in this visualization. Each map server provides its own list of -layers. -* *WMS version*: The WMS version used by this map service. -* *WMS format*: The image format used by this map service. The two most common formats are `image/png` and `image/jpeg`. -* *WMS attribution*: An optional, user-defined string that identifies the map source. Maps display the attribution string -in the lower right corner. -* *WMS styles*: A comma-separated list of the styles to use in this visualization. Each map server provides its own styling -options. - -After changing options, click the *Apply changes* button to update your visualization, or the grey *Discard -changes* button to keep your visualization in its current state. +[[coordinate-bucket-aggregation]] +===== Bucket aggregation + +Coordinate maps support the {ref}/search-aggregations-bucket-geohashgrid-aggregation.html[_geohash_] bucket aggregation. + +When you deselect *Change precision on map zoom*, the *Precision* slider appears. The *Precision* slider determines the granularity of the results displayed on the map. For details on the area specified by each precision level, refer to {ref}/search-aggregations-bucket-geohashgrid-aggregation.html#_cell_dimensions_at_the_equator[geohash grid]. + +NOTE: Higher precisions increase memory usage for the browser that displays {kib} and the underlying +{es} cluster. + +When you select *Place markers off grid (use {ref}/search-aggregations-metrics-geocentroid-aggregation.html[geocentroid])*, the markers are +placed in the center of all documents in the bucket, and a more accurate visualization is created. +NOTE: When you have multiple values in the geo_point, the coordinate map is unable to accurately calculate the geo_centroid. + +When you deselect *Place markers off grid (use {ref}/search-aggregations-metrics-geocentroid-aggregation.html[geocentroid])*, the markers are placed in the center +of the geohash grid cell. [float] [[navigate-map]] -=== Navigating the Map +=== Navigate the coordinate map -Once your tilemap visualization is ready, you can explore the map in several ways: +Use the following navigation options: -* Click and hold anywhere on the map and move the cursor to move the map center. Hold Shift and drag a bounding box -across the map to zoom in on the selection. -* Click the *Zoom In/Out* image:images/viz-zoom.png[] buttons to change the zoom level manually. -* Click the *Fit Data Bounds* image:images/viz-fit-bounds.png[] button to automatically crop the map boundaries to the -geohash buckets that have at least one result. -* Click the *Latitude/Longitude Filter* image:images/viz-lat-long-filter.png[] button, then drag a bounding box across the -map, to create a filter for the box coordinates. +* To move the map center, click and hold anywhere on the map and move the cursor. +* To change the zoom level, click *Zoom In* or *Zoom out* image:images/viz-zoom.png[]. +* To automatically crop the map boundaries to the +geohash buckets that have at least one result, click *Fit Data Bounds* image:images/viz-fit-bounds.png[]. diff --git a/examples/README.md b/examples/README.md index 7cade0b35f820..2b214a8d1eb52 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,4 +5,3 @@ This folder contains example plugins. To run the plugins in this folder, use th ``` yarn start --run-examples ``` - diff --git a/examples/bfetch_explorer/kibana.json b/examples/bfetch_explorer/kibana.json new file mode 100644 index 0000000000000..cbdd9be0e658c --- /dev/null +++ b/examples/bfetch_explorer/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "bfetchExplorer", + "version": "0.0.1", + "kibanaVersion": "kibana", + "configPath": ["bfetch_explorer"], + "server": true, + "ui": true, + "requiredPlugins": ["bfetch"], + "optionalPlugins": [] +} diff --git a/examples/bfetch_explorer/package.json b/examples/bfetch_explorer/package.json new file mode 100644 index 0000000000000..ea5a1b1848613 --- /dev/null +++ b/examples/bfetch_explorer/package.json @@ -0,0 +1,17 @@ +{ + "name": "bfetch_explorer", + "version": "1.0.0", + "main": "target/examples/bfetch_explorer", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "kbn": "node ../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.7.2" + } +} diff --git a/examples/bfetch_explorer/public/components/count_until/index.tsx b/examples/bfetch_explorer/public/components/count_until/index.tsx new file mode 100644 index 0000000000000..ce48ce9dfe61f --- /dev/null +++ b/examples/bfetch_explorer/public/components/count_until/index.tsx @@ -0,0 +1,93 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useState } from 'react'; +import useMountedState from 'react-use/lib/useMountedState'; +import useList from 'react-use/lib/useList'; +import { EuiForm, EuiSpacer, EuiFieldNumber, EuiFormRow, EuiButton } from '@elastic/eui'; +import { BfetchPublicSetup } from '../../../../../src/plugins/bfetch/public'; + +export interface Props { + fetchStreaming: BfetchPublicSetup['fetchStreaming']; +} + +export const CountUntil: React.FC = ({ fetchStreaming }) => { + const isMounted = useMountedState(); + const [data, setData] = useState(5); + const [showingResults, setShowingResults] = useState(false); + const [results, { push: pushResult, clear: clearList }] = useList([]); + const [completed, setCompleted] = useState(false); + const [error, setError] = useState(null); + + const handleSubmit = () => { + setShowingResults(true); + const { stream } = fetchStreaming({ + url: '/bfetch_explorer/count', + body: JSON.stringify({ data }), + }); + stream.subscribe({ + next: (next: string) => { + if (!isMounted()) return; + pushResult(next); + }, + error: (nextError: any) => { + if (!isMounted()) return; + setError(nextError); + }, + complete: () => { + if (!isMounted()) return; + setCompleted(true); + }, + }); + }; + + const handleReset = () => { + setShowingResults(false); + clearList(); + setError(null); + setCompleted(false); + }; + + if (showingResults) { + return ( + +
{JSON.stringify(error || results, null, 4)}
+ + + Reset + +
+ ); + } + + return ( + + + setData(Number(e.target.value))} + /> + + + Start + + + ); +}; diff --git a/examples/bfetch_explorer/public/components/double_integers/index.tsx b/examples/bfetch_explorer/public/components/double_integers/index.tsx new file mode 100644 index 0000000000000..d8fbe33ec73be --- /dev/null +++ b/examples/bfetch_explorer/public/components/double_integers/index.tsx @@ -0,0 +1,105 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useState } from 'react'; +import useMountedState from 'react-use/lib/useMountedState'; +import useList from 'react-use/lib/useList'; +import useCounter from 'react-use/lib/useCounter'; +import { EuiForm, EuiSpacer, EuiTextArea, EuiFormRow, EuiButton } from '@elastic/eui'; +import { ExplorerService } from '../../plugin'; + +interface ResultItem { + num: number; + result?: { + num: number; + }; + error?: any; +} + +const defaultNumbers = [2000, 300, -1, 1000].join('\n'); + +export interface Props { + double: ExplorerService['double']; +} + +export const DoubleIntegers: React.FC = ({ double }) => { + const isMounted = useMountedState(); + const [numbers, setNumbers] = useState(defaultNumbers); + const [showingResults, setShowingResults] = useState(false); + const [numberOfResultsAwaiting, counter] = useCounter(0); + const [results, { push: pushResult, clear: clearList }] = useList([]); + + const handleSubmit = () => { + setShowingResults(true); + const nums = numbers + .split('\n') + .map(num => num.trim()) + .filter(Boolean) + .map(Number); + counter.set(nums.length); + nums.forEach(num => { + double({ num }).then( + result => { + if (!isMounted()) return; + counter.dec(); + pushResult({ num, result }); + }, + error => { + if (!isMounted()) return; + counter.dec(); + pushResult({ num, error }); + } + ); + }); + }; + + const handleReset = () => { + setShowingResults(false); + counter.reset(); + clearList(); + }; + + if (showingResults) { + return ( + +
{JSON.stringify(results, null, 4)}
+ + + Reset + +
+ ); + } + + return ( + + + setNumbers(e.target.value)} + /> + + + Send + + + ); +}; diff --git a/examples/bfetch_explorer/public/components/page/index.tsx b/examples/bfetch_explorer/public/components/page/index.tsx new file mode 100644 index 0000000000000..0e7855178a884 --- /dev/null +++ b/examples/bfetch_explorer/public/components/page/index.tsx @@ -0,0 +1,51 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as React from 'react'; +import { + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageHeader, + EuiPageHeaderSection, + EuiTitle, +} from '@elastic/eui'; + +export interface PageProps { + title?: React.ReactNode; +} + +export const Page: React.FC = ({ title = 'Untitled', children }) => { + return ( + + + + +

{title}

+
+
+
+ + + {children} + + +
+ ); +}; diff --git a/examples/bfetch_explorer/public/containers/app/index.tsx b/examples/bfetch_explorer/public/containers/app/index.tsx new file mode 100644 index 0000000000000..a448c9e4f3a6a --- /dev/null +++ b/examples/bfetch_explorer/public/containers/app/index.tsx @@ -0,0 +1,48 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { BrowserRouter as Router, Route, Redirect, Switch } from 'react-router-dom'; +import { EuiPage } from '@elastic/eui'; +import { useDeps } from '../../hooks/use_deps'; +import { Sidebar } from './sidebar'; +import { routes } from '../../routes'; + +export const App: React.FC = () => { + const { appBasePath } = useDeps(); + + const routeElements: React.ReactElement[] = []; + for (const { items } of routes) { + for (const { id, component } of items) { + routeElements.push( component} />); + } + } + + return ( + + + + + {routeElements} + + + + + ); +}; diff --git a/examples/bfetch_explorer/public/containers/app/pages/page_count_until/index.tsx b/examples/bfetch_explorer/public/containers/app/pages/page_count_until/index.tsx new file mode 100644 index 0000000000000..7b4eac6eea44c --- /dev/null +++ b/examples/bfetch_explorer/public/containers/app/pages/page_count_until/index.tsx @@ -0,0 +1,45 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as React from 'react'; +import { EuiPanel, EuiText } from '@elastic/eui'; +import { CountUntil } from '../../../../components/count_until'; +import { Page } from '../../../../components/page'; +import { useDeps } from '../../../../hooks/use_deps'; + +// eslint-disable-next-line +export interface Props {} + +export const PageCountUntil: React.FC = () => { + const { plugins } = useDeps(); + + return ( + + + This demo sends a single number N using fetchStreaming to the server. The + server will stream back N number of messages with 1 second delay each containing a number + from 1 to N, after which it will close the stream. + +
+ + + +
+ ); +}; diff --git a/examples/bfetch_explorer/public/containers/app/pages/page_double_integers/index.tsx b/examples/bfetch_explorer/public/containers/app/pages/page_double_integers/index.tsx new file mode 100644 index 0000000000000..7bd5feb836674 --- /dev/null +++ b/examples/bfetch_explorer/public/containers/app/pages/page_double_integers/index.tsx @@ -0,0 +1,45 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as React from 'react'; +import { EuiPanel, EuiText } from '@elastic/eui'; +import { DoubleIntegers } from '../../../../components/double_integers'; +import { Page } from '../../../../components/page'; +import { useDeps } from '../../../../hooks/use_deps'; + +// eslint-disable-next-line +export interface Props {} + +export const PageDoubleIntegers: React.FC = () => { + const { explorer } = useDeps(); + + return ( + + + Below is a list of numbers in milliseconds. They are sent as a batch to the server. For each + number server waits given number of milliseconds then doubles the number and streams it + back. + +
+ + + +
+ ); +}; diff --git a/examples/bfetch_explorer/public/containers/app/sidebar/index.tsx b/examples/bfetch_explorer/public/containers/app/sidebar/index.tsx new file mode 100644 index 0000000000000..cc50698e05908 --- /dev/null +++ b/examples/bfetch_explorer/public/containers/app/sidebar/index.tsx @@ -0,0 +1,54 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { EuiPageSideBar, EuiSideNav } from '@elastic/eui'; +import { useHistory } from 'react-router-dom'; +import { routes } from '../../../routes'; + +// eslint-disable-next-line +interface SidebarProps {} + +export const Sidebar: React.FC = () => { + const history = useHistory(); + + return ( + + ({ + id, + name: title, + isSelected: true, + items: items.map(route => ({ + id: route.id, + name: route.title, + onClick: () => history.push(`/${route.id}`), + 'data-test-subj': route.id, + })), + })), + }, + ]} + /> + + ); +}; diff --git a/examples/bfetch_explorer/public/hooks/use_deps.ts b/examples/bfetch_explorer/public/hooks/use_deps.ts new file mode 100644 index 0000000000000..c68b4e759c21c --- /dev/null +++ b/examples/bfetch_explorer/public/hooks/use_deps.ts @@ -0,0 +1,23 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { useKibana } from '../../../../src/plugins/kibana_react/public'; +import { BfetchDeps } from '../mount'; + +export const useDeps = () => useKibana().services as BfetchDeps; diff --git a/src/legacy/core_plugins/interpreter/public/canvas/consts.ts b/examples/bfetch_explorer/public/index.ts similarity index 86% rename from src/legacy/core_plugins/interpreter/public/canvas/consts.ts rename to examples/bfetch_explorer/public/index.ts index 2600ada36afdc..76d0a1d1c6334 100644 --- a/src/legacy/core_plugins/interpreter/public/canvas/consts.ts +++ b/examples/bfetch_explorer/public/index.ts @@ -17,5 +17,6 @@ * under the License. */ -// The server endpoint for retrieiving and running Canvas functions. -export const FUNCTIONS_URL = '/api/interpreter/fns'; +import { BfetchExplorerPlugin } from './plugin'; + +export const plugin = () => new BfetchExplorerPlugin(); diff --git a/examples/bfetch_explorer/public/mount.tsx b/examples/bfetch_explorer/public/mount.tsx new file mode 100644 index 0000000000000..5ad53ef4a1988 --- /dev/null +++ b/examples/bfetch_explorer/public/mount.tsx @@ -0,0 +1,47 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { CoreSetup, CoreStart, AppMountParameters } from 'kibana/public'; +import { KibanaContextProvider } from '../../../src/plugins/kibana_react/public'; +import { BfetchExplorerStartPlugins, ExplorerService } from './plugin'; +import { App } from './containers/app'; + +export interface BfetchDeps { + appBasePath: string; + core: CoreStart; + plugins: BfetchExplorerStartPlugins; + explorer: ExplorerService; +} + +export const mount = ( + coreSetup: CoreSetup, + explorer: ExplorerService +) => async ({ appBasePath, element }: AppMountParameters) => { + const [core, plugins] = await coreSetup.getStartServices(); + const deps: BfetchDeps = { appBasePath, core, plugins, explorer }; + const reactElement = ( + + + + ); + render(reactElement, element); + return () => unmountComponentAtNode(element); +}; diff --git a/examples/bfetch_explorer/public/plugin.tsx b/examples/bfetch_explorer/public/plugin.tsx new file mode 100644 index 0000000000000..3155354c91fd4 --- /dev/null +++ b/examples/bfetch_explorer/public/plugin.tsx @@ -0,0 +1,55 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Plugin, CoreSetup } from 'kibana/public'; +import { BfetchPublicSetup, BfetchPublicStart } from '../../../src/plugins/bfetch/public'; +import { mount } from './mount'; + +export interface ExplorerService { + double: (number: { num: number }) => Promise<{ num: number }>; +} + +export interface BfetchExplorerSetupPlugins { + bfetch: BfetchPublicSetup; +} + +export interface BfetchExplorerStartPlugins { + bfetch: BfetchPublicStart; +} + +export class BfetchExplorerPlugin implements Plugin { + public setup(core: CoreSetup, plugins: BfetchExplorerSetupPlugins) { + const double = plugins.bfetch.batchedFunction<{ num: number }, { num: number }>({ + url: '/bfetch_explorer/double', + }); + + const explorer: ExplorerService = { + double, + }; + + core.application.register({ + id: 'bfetch-explorer', + title: 'bfetch explorer', + mount: mount(core, explorer), + }); + } + + public start() {} + public stop() {} +} diff --git a/src/legacy/ui/public/vis/editors/default/schemas.d.ts b/examples/bfetch_explorer/public/routes.tsx similarity index 51% rename from src/legacy/ui/public/vis/editors/default/schemas.d.ts rename to examples/bfetch_explorer/public/routes.tsx index 236421f30fb09..2008811d75795 100644 --- a/src/legacy/ui/public/vis/editors/default/schemas.d.ts +++ b/examples/bfetch_explorer/public/routes.tsx @@ -17,21 +17,43 @@ * under the License. */ -import { AggParam } from '../../../agg_types'; -import { AggGroupNames } from './agg_groups'; -import { AggControlProps } from './controls/agg_control_props'; +import React from 'react'; +import { PageDoubleIntegers } from './containers/app/pages/page_double_integers'; +import { PageCountUntil } from './containers/app/pages/page_count_until'; -export interface Schema { - aggFilter: string | string[]; - editor: boolean | string; - group: AggGroupNames; - max: number; - min: number; - name: string; - params: AggParam[]; +interface RouteSectionDef { title: string; - defaults: unknown; - hideCustomLabel?: boolean; - mustBeFirst?: boolean; - editorComponent?: React.ComponentType; + id: string; + items: RouteDef[]; } + +interface RouteDef { + title: string; + id: string; + component: React.ReactNode; +} + +export const routes: RouteSectionDef[] = [ + { + title: 'fetchStreaming', + id: 'fetchStreaming', + items: [ + { + title: 'Count until', + id: 'count-until', + component: , + }, + ], + }, + { + title: 'batchedFunction', + id: 'batchedFunction', + items: [ + { + title: 'Double integers', + id: 'double-integers', + component: , + }, + ], + }, +]; diff --git a/src/legacy/core_plugins/interpreter/server/routes/index.ts b/examples/bfetch_explorer/server/index.ts similarity index 85% rename from src/legacy/core_plugins/interpreter/server/routes/index.ts rename to examples/bfetch_explorer/server/index.ts index 50385147dd38e..76d0a1d1c6334 100644 --- a/src/legacy/core_plugins/interpreter/server/routes/index.ts +++ b/examples/bfetch_explorer/server/index.ts @@ -17,8 +17,6 @@ * under the License. */ -import { registerServerFunctions } from './server_functions'; +import { BfetchExplorerPlugin } from './plugin'; -export function routes(server: any) { - registerServerFunctions(server); -} +export const plugin = () => new BfetchExplorerPlugin(); diff --git a/examples/bfetch_explorer/server/plugin.ts b/examples/bfetch_explorer/server/plugin.ts new file mode 100644 index 0000000000000..bf3b7f50ca6c8 --- /dev/null +++ b/examples/bfetch_explorer/server/plugin.ts @@ -0,0 +1,68 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Subject } from 'rxjs'; +import { Plugin, CoreSetup, CoreStart } from '../../../src/core/server'; +import { BfetchServerSetup, BfetchServerStart } from '../../../src/plugins/bfetch/server'; + +export interface BfetchExplorerSetupPlugins { + bfetch: BfetchServerSetup; +} + +export interface BfetchExplorerStartPlugins { + bfetch: BfetchServerStart; +} + +export class BfetchExplorerPlugin implements Plugin { + public setup(core: CoreSetup, plugins: BfetchExplorerSetupPlugins) { + plugins.bfetch.addStreamingResponseRoute('/bfetch_explorer/count', () => ({ + getResponseStream: ({ data }: any) => { + const subject = new Subject(); + const countTo = Number(data); + for (let cnt = 1; cnt <= countTo; cnt++) { + setTimeout(() => { + subject.next(String(cnt)); + }, cnt * 1000); + } + setTimeout(() => { + subject.complete(); + }, countTo * 1000); + return subject; + }, + })); + + plugins.bfetch.addBatchProcessingRoute<{ num: number }, { num: number }>( + '/bfetch_explorer/double', + () => ({ + onBatchItem: async ({ num }) => { + // Validate inputs. + if (num < 0) throw new Error('Invalid number'); + // Wait number of specified milliseconds. + await new Promise(r => setTimeout(r, num)); + // Double the number and send it back. + return { num: 2 * num }; + }, + }) + ); + } + + public start(core: CoreStart, plugins: BfetchExplorerStartPlugins) {} + + public stop() {} +} diff --git a/examples/bfetch_explorer/tsconfig.json b/examples/bfetch_explorer/tsconfig.json new file mode 100644 index 0000000000000..d508076b33199 --- /dev/null +++ b/examples/bfetch_explorer/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../typings/**/*", + ], + "exclude": [] +} diff --git a/package.json b/package.json index 7915c0ed32baf..9d2068b02bb93 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,7 @@ "@elastic/charts": "^16.1.0", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "7.6.0", - "@elastic/eui": "18.2.0", + "@elastic/eui": "18.2.1", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "2.3.3", diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index af44991e625a2..488f57b01e168 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -9,7 +9,7 @@ "kbn:watch": "node scripts/build --watch" }, "devDependencies": { - "@elastic/eui": "18.2.0", + "@elastic/eui": "18.2.1", "@elastic/charts": "^16.1.0", "@kbn/dev-utils": "1.0.0", "@yarnpkg/lockfile": "^1.1.0", diff --git a/src/core/server/index.ts b/src/core/server/index.ts index eccf3985fc495..50d291b173640 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -152,7 +152,7 @@ export { SessionCookieValidationResult, SessionStorageFactory, } from './http'; -export { RenderingServiceSetup, IRenderOptions, LegacyRenderOptions } from './rendering'; +export { RenderingServiceSetup, IRenderOptions } from './rendering'; export { Logger, LoggerFactory, LogMeta, LogRecord, LogLevel } from './logging'; export { @@ -216,6 +216,9 @@ export { UiSettingsServiceSetup, UiSettingsServiceStart, UserProvidedValues, + ImageValidation, + DeprecationSettings, + StringValidation, } from './ui_settings'; export { RecursiveReadonly } from '../utils'; diff --git a/src/core/server/legacy/legacy_internals.ts b/src/core/server/legacy/legacy_internals.ts index 3bf54e5f75dce..628ca4ed12f6b 100644 --- a/src/core/server/legacy/legacy_internals.ts +++ b/src/core/server/legacy/legacy_internals.ts @@ -19,7 +19,8 @@ import { Server } from 'hapi'; -import { LegacyRequest } from '../http'; +import { KibanaRequest, LegacyRequest } from '../http'; +import { ensureRawRequest } from '../http/router'; import { mergeVars } from './merge_vars'; import { ILegacyInternals, LegacyVars, VarsInjector, LegacyConfig, LegacyUiExports } from './types'; @@ -51,11 +52,12 @@ export class LegacyInternals implements ILegacyInternals { )); } - private replaceVars(vars: LegacyVars, request: LegacyRequest) { + private replaceVars(vars: LegacyVars, request: KibanaRequest | LegacyRequest) { const { injectedVarsReplacers = [] } = this.uiExports; return injectedVarsReplacers.reduce( - async (injected, replacer) => replacer(await injected, request, this.server), + async (injected, replacer) => + replacer(await injected, ensureRawRequest(request), this.server), Promise.resolve(vars) ); } @@ -78,7 +80,11 @@ export class LegacyInternals implements ILegacyInternals { ); } - public async getVars(id: string, request: LegacyRequest, injected: LegacyVars = {}) { + public async getVars( + id: string, + request: KibanaRequest | LegacyRequest, + injected: LegacyVars = {} + ) { return this.replaceVars( mergeVars(this.defaultVars, await this.getInjectedUiAppVars(id), injected), request diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index 7a03cefc38c1a..ffcbf1662ee85 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -31,6 +31,7 @@ import { PathConfigType } from '../path'; import { findLegacyPluginSpecs } from './plugins'; import { convertLegacyDeprecationProvider } from './config'; import { + ILegacyInternals, LegacyServiceSetupDeps, LegacyServiceStartDeps, LegacyPlugins, @@ -82,6 +83,7 @@ export class LegacyService implements CoreService { private legacyRawConfig?: LegacyConfig; private legacyPlugins?: LegacyPlugins; private settings?: LegacyVars; + public legacyInternals?: ILegacyInternals; constructor(private readonly coreContext: CoreContext) { const { logger, configService } = coreContext; @@ -183,6 +185,11 @@ export class LegacyService implements CoreService { // propagate the instance uuid to the legacy config, as it was the legacy way to access it. this.legacyRawConfig!.set('server.uuid', setupDeps.core.uuid.getInstanceUuid()); this.setupDeps = setupDeps; + this.legacyInternals = new LegacyInternals( + this.legacyPlugins.uiExports, + this.legacyRawConfig!, + setupDeps.core.http.server + ); } public async start(startDeps: LegacyServiceStartDeps) { @@ -317,7 +324,7 @@ export class LegacyService implements CoreService { rendering: setupDeps.core.rendering, uiSettings: setupDeps.core.uiSettings, savedObjectsClientProvider: startDeps.core.savedObjects.clientProvider, - legacy: new LegacyInternals(legacyPlugins.uiExports, config, setupDeps.core.http.server), + legacy: this.legacyInternals, }, logger: this.coreContext.logger, }, diff --git a/src/core/server/legacy/types.ts b/src/core/server/legacy/types.ts index 6ec893be9b310..40b8244a31890 100644 --- a/src/core/server/legacy/types.ts +++ b/src/core/server/legacy/types.ts @@ -20,7 +20,7 @@ import { Server } from 'hapi'; import { ChromeNavLink } from '../../public'; -import { LegacyRequest } from '../http'; +import { KibanaRequest, LegacyRequest } from '../http'; import { InternalCoreSetup, InternalCoreStart } from '../internal_types'; import { PluginsServiceSetup, PluginsServiceStart } from '../plugins'; import { RenderingServiceSetup } from '../rendering'; @@ -198,7 +198,11 @@ export interface ILegacyInternals { /** * Get the metadata vars for a particular plugin */ - getVars(id: string, request: LegacyRequest, injected?: LegacyVars): Promise; + getVars( + id: string, + request: KibanaRequest | LegacyRequest, + injected?: LegacyVars + ): Promise; } /** diff --git a/src/core/server/rendering/rendering_service.tsx b/src/core/server/rendering/rendering_service.tsx index 41810c6a10655..11d1fb271c81d 100644 --- a/src/core/server/rendering/rendering_service.tsx +++ b/src/core/server/rendering/rendering_service.tsx @@ -27,10 +27,10 @@ import { CoreService } from '../../types'; import { CoreContext } from '../core_context'; import { Template } from './views'; import { + IRenderOptions, RenderingSetupDeps, RenderingServiceSetup, RenderingMetadata, - LegacyRenderOptions, } from './types'; /** @internal */ @@ -56,7 +56,7 @@ export class RenderingService implements CoreService { app = { getId: () => 'core' }, includeUserSettings = true, vars = {}, - }: LegacyRenderOptions = {} + }: IRenderOptions = {} ) => { const { env } = this.coreContext; const basePath = http.basePath.get(request); diff --git a/src/core/server/rendering/types.ts b/src/core/server/rendering/types.ts index 31b326bab6c78..3f9f6ff294909 100644 --- a/src/core/server/rendering/types.ts +++ b/src/core/server/rendering/types.ts @@ -84,21 +84,19 @@ export interface IRenderOptions { * `true` by default. */ includeUserSettings?: boolean; -} -/** - * @internal - * @deprecated for legacy use only, remove with ui_render_mixin - */ -export interface LegacyRenderOptions extends IRenderOptions { /** * Render the bootstrapped HTML content for an optional legacy application. * Defaults to `core`. + * @deprecated for legacy use only, remove with ui_render_mixin + * @internal */ app?: { getId(): string }; /** * Inject custom vars into the page metadata. + * @deprecated for legacy use only, remove with ui_render_mixin + * @internal */ vars?: Record; } @@ -123,7 +121,7 @@ export interface IScopedRenderingClient { * ); * ``` */ - render(options?: IRenderOptions): Promise; + render(options?: Pick): Promise; } /** @internal */ @@ -140,6 +138,6 @@ export interface RenderingServiceSetup { render( request: R, uiSettings: IUiSettingsClient, - options?: R extends LegacyRequest ? LegacyRenderOptions : IRenderOptions + options?: IRenderOptions ): Promise; } diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 7f3a960571012..6a58666716f42 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -635,6 +635,12 @@ export interface DeprecationInfo { url: string; } +// @public +export interface DeprecationSettings { + docLinksKey: string; + message: string; +} + // @public export interface DiscoveredPlugin { readonly configPath: ConfigPath; @@ -795,6 +801,17 @@ export interface IKibanaSocket { getPeerCertificate(detailed?: boolean): PeerCertificate | DetailedPeerCertificate | null; } +// Warning: (ae-missing-release-tag) "ImageValidation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface ImageValidation { + // (undocumented) + maxSize: { + length: number; + description: string; + }; +} + // @public (undocumented) export interface IndexSettingsDeprecationInfo { // (undocumented) @@ -803,7 +820,13 @@ export interface IndexSettingsDeprecationInfo { // @public (undocumented) export interface IRenderOptions { + // @internal @deprecated + app?: { + getId(): string; + }; includeUserSettings?: boolean; + // @internal @deprecated + vars?: Record; } // @public @@ -832,7 +855,7 @@ export type IScopedClusterClient = Pick; + render(options?: Pick): Promise; } // @public @@ -932,21 +955,13 @@ export class LegacyInternals implements ILegacyInternals { // (undocumented) getInjectedUiAppVars(id: string): Promise>; // (undocumented) - getVars(id: string, request: LegacyRequest, injected?: LegacyVars): Promise>; + getVars(id: string, request: KibanaRequest | LegacyRequest, injected?: LegacyVars): Promise>; // Warning: (ae-forgotten-export) The symbol "VarsInjector" needs to be exported by the entry point index.d.ts // // (undocumented) injectUiAppVars(id: string, injector: VarsInjector): void; } -// @internal @deprecated (undocumented) -export interface LegacyRenderOptions extends IRenderOptions { - app?: { - getId(): string; - }; - vars?: Record; -} - // @public @deprecated (undocumented) export interface LegacyRequest extends Request { } @@ -1233,7 +1248,7 @@ export type RedirectResponseOptions = HttpResponseOptions & { // @internal (undocumented) export interface RenderingServiceSetup { - render(request: R, uiSettings: IUiSettingsClient, options?: R extends LegacyRequest ? LegacyRenderOptions : IRenderOptions): Promise; + render(request: R, uiSettings: IUiSettingsClient, options?: IRenderOptions): Promise; } // @public @@ -1925,10 +1940,19 @@ export type SharedGlobalConfig = RecursiveReadonly_2<{ path: Pick; }>; +// Warning: (ae-missing-release-tag) "StringValidation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface StringValidation { + // (undocumented) + message: string; + // (undocumented) + regexString: string; +} + // @public export interface UiSettingsParams { category?: string[]; - // Warning: (ae-forgotten-export) The symbol "DeprecationSettings" needs to be exported by the entry point index.d.ts deprecation?: DeprecationSettings; description?: string; name?: string; @@ -1937,9 +1961,6 @@ export interface UiSettingsParams { readonly?: boolean; requiresPageReload?: boolean; type?: UiSettingsType; - // Warning: (ae-forgotten-export) The symbol "ImageValidation" needs to be exported by the entry point index.d.ts - // Warning: (ae-forgotten-export) The symbol "StringValidation" needs to be exported by the entry point index.d.ts - // // (undocumented) validation?: ImageValidation | StringValidation; value?: SavedObjectAttribute; diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 7c3f9f249db13..89a5bdc4802fd 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -220,7 +220,11 @@ export class Server { return { rendering: { - render: rendering.render.bind(rendering, req, uiSettingsClient), + render: async (options = {}) => + rendering.render(req, uiSettingsClient, { + ...options, + vars: await this.legacy.legacyInternals!.getVars('core', req), + }), }, savedObjects: { client: savedObjectsClient, diff --git a/src/core/server/ui_settings/index.ts b/src/core/server/ui_settings/index.ts index f1185474c2160..39eb0046010b3 100644 --- a/src/core/server/ui_settings/index.ts +++ b/src/core/server/ui_settings/index.ts @@ -31,4 +31,7 @@ export { InternalUiSettingsServiceStart, UiSettingsType, UserProvidedValues, + ImageValidation, + DeprecationSettings, + StringValidation, } from './types'; diff --git a/src/core/server/ui_settings/types.ts b/src/core/server/ui_settings/types.ts index 14eb71a22cefc..a74a31bbbd671 100644 --- a/src/core/server/ui_settings/types.ts +++ b/src/core/server/ui_settings/types.ts @@ -78,7 +78,9 @@ export interface UserProvidedValues { * @public * */ export interface DeprecationSettings { + /** Deprecation message */ message: string; + /** Key to documentation links */ docLinksKey: string; } diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 12779efe7f869..e5493df0aecf7 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -120,7 +120,7 @@ export const TEMPORARILY_IGNORED_PATHS = [ 'src/legacy/core_plugins/timelion/server/series_functions/__tests__/fixtures/seriesList.js', 'src/legacy/core_plugins/timelion/server/series_functions/__tests__/fixtures/tlConfig.js', 'src/fixtures/config_upgrade_from_4.0.0_to_4.0.1-snapshot.json', - 'src/fixtures/vislib/mock_data/terms/_seriesMultiple.js', + 'src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_seriesMultiple.js', 'src/legacy/ui/public/angular-bootstrap/bindHtml/bindHtml.js', 'src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-html-unsafe-popup.html', 'src/legacy/ui/public/angular-bootstrap/tooltip/tooltip-popup.html', diff --git a/src/dev/register_git_hook/register_git_hook.js b/src/dev/register_git_hook/register_git_hook.js index 99ae351fe383e..8820327d3adc0 100644 --- a/src/dev/register_git_hook/register_git_hook.js +++ b/src/dev/register_git_hook/register_git_hook.js @@ -65,7 +65,7 @@ function getKbnPrecommitGitHookScript(rootPath, nodeHome, platform) { # The correct exit code on that situation # according the linux documentation project is 130 # https://www.tldp.org/LDP/abs/html/exitcodes.html - trap "exit 130" SIGINT + trap "exit 130" INT has_node() { command -v node >/dev/null 2>&1 diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 8213bdeeea2da..1dce53b6c2a84 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -18,6 +18,7 @@ */ export const storybookAliases = { + apm: 'x-pack/legacy/plugins/apm/scripts/storybook.js', canvas: 'x-pack/legacy/plugins/canvas/scripts/storybook_new.js', embeddable: 'src/plugins/embeddable/scripts/storybook.js', infra: 'x-pack/legacy/plugins/infra/scripts/storybook.js', diff --git a/src/legacy/core_plugins/interpreter/init.ts b/src/legacy/core_plugins/interpreter/init.ts index 768d76fbf744e..46da1539afadb 100644 --- a/src/legacy/core_plugins/interpreter/init.ts +++ b/src/legacy/core_plugins/interpreter/init.ts @@ -22,35 +22,10 @@ // @ts-ignore import { register, registryFactory, Registry, Fn } from '@kbn/interpreter/common'; -// @ts-ignore -import { routes } from './server/routes'; - -import { typeSpecs as types, Type } from '../../../plugins/expressions/common'; import { Legacy } from '../../../../kibana'; -export class TypesRegistry extends Registry { - wrapper(obj: any) { - return new (Type as any)(obj); - } -} - -export class FunctionsRegistry extends Registry { - wrapper(obj: any) { - return new Fn(obj); - } -} - -export const registries = { - types: new TypesRegistry(), - serverFunctions: new FunctionsRegistry(), -}; - export async function init(server: Legacy.Server /* options */) { server.injectUiAppVars('canvas', () => { - register(registries, { - types, - }); - const config = server.config(); const basePath = config.get('server.basePath'); const reportingBrowserType = (() => { @@ -63,7 +38,9 @@ export async function init(server: Legacy.Server /* options */) { return { kbnIndex: config.get('kibana.index'), - serverFunctions: registries.serverFunctions.toArray(), + serverFunctions: (server.newPlatform.setup.plugins.expressions as any).__LEGACY + .registries() + .serverFunctions.toArray(), basePath, reportingBrowserType, }; @@ -71,7 +48,5 @@ export async function init(server: Legacy.Server /* options */) { // Expose server.plugins.interpreter.register(specs) and // server.plugins.interpreter.registries() (a getter). - server.expose(registryFactory(registries)); - - routes(server); + server.expose((server.newPlatform.setup.plugins.expressions as any).__LEGACY); } diff --git a/src/legacy/core_plugins/interpreter/public/canvas/load_legacy_server_function_wrappers.ts b/src/legacy/core_plugins/interpreter/public/canvas/load_legacy_server_function_wrappers.ts index 2c2f79b3d6f51..fed157846a1a1 100644 --- a/src/legacy/core_plugins/interpreter/public/canvas/load_legacy_server_function_wrappers.ts +++ b/src/legacy/core_plugins/interpreter/public/canvas/load_legacy_server_function_wrappers.ts @@ -28,62 +28,6 @@ * server side, it should be respective function's internal implementation detail. */ -import { get, identity } from 'lodash'; -// @ts-ignore -import { npSetup, npStart } from 'ui/new_platform'; -import { FUNCTIONS_URL } from './consts'; -import { batchedFetch } from './batched_fetch'; +import { npSetup } from 'ui/new_platform'; -export function getType(node: any) { - if (node == null) return 'null'; - if (typeof node === 'object') { - if (!node.type) throw new Error('Objects must have a type property'); - return node.type; - } - return typeof node; -} - -export function serializeProvider(types: any) { - return { - serialize: provider('serialize'), - deserialize: provider('deserialize'), - }; - - function provider(key: any) { - return (context: any) => { - const type = getType(context); - const typeDef = types[type]; - const fn: any = get(typeDef, key) || identity; - return fn(context); - }; - } -} - -let cached: Promise | null = null; - -export const loadLegacyServerFunctionWrappers = async () => { - if (!cached) { - cached = (async () => { - const serverFunctionList = await npSetup.core.http.get(FUNCTIONS_URL); - const types = npSetup.plugins.expressions.__LEGACY.types.toJS(); - const { serialize } = serializeProvider(types); - const batch = batchedFetch({ - fetchStreaming: npStart.plugins.bfetch.fetchStreaming, - serialize, - }); - - // For every sever-side function, register a client-side - // function that matches its definition, but which simply - // calls the server-side function endpoint. - Object.keys(serverFunctionList).forEach(functionName => { - const fn = () => ({ - ...serverFunctionList[functionName], - fn: (context: any, args: any) => batch({ functionName, args, context }), - }); - npSetup.plugins.expressions.registerFunction(fn); - }); - })(); - } - - return cached; -}; +export const { loadLegacyServerFunctionWrappers } = npSetup.plugins.expressions.__LEGACY; diff --git a/src/legacy/core_plugins/interpreter/public/registries.karma_mock.ts b/src/legacy/core_plugins/interpreter/public/registries.karma_mock.ts index 66c51167c7b59..0f37f33cc1b13 100644 --- a/src/legacy/core_plugins/interpreter/public/registries.karma_mock.ts +++ b/src/legacy/core_plugins/interpreter/public/registries.karma_mock.ts @@ -26,6 +26,7 @@ export const registries = { browserFunctions: functionsRegistry, renderers: renderersRegistry, types: typesRegistry, + loadLegacyServerFunctionWrappers: () => Promise.resolve(), }; const resetRegistry = (registry: any) => { diff --git a/src/legacy/core_plugins/interpreter/server/lib/__tests__/create_handlers.ts b/src/legacy/core_plugins/interpreter/server/lib/__tests__/create_handlers.ts deleted file mode 100644 index 0088663080774..0000000000000 --- a/src/legacy/core_plugins/interpreter/server/lib/__tests__/create_handlers.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import { createHandlers } from '../create_handlers'; - -const mockRequest = { - headers: 'i can haz headers', -}; - -const mockServer = { - plugins: { - elasticsearch: { - getCluster: () => ({ - callWithRequest: (...args: any) => Promise.resolve(args), - }), - }, - }, - config: () => ({ - has: () => false, - get: (val: any) => val, - }), - info: { - uri: 'serveruri', - }, -}; - -describe('server createHandlers', () => { - it('provides helper methods and properties', () => { - const handlers = createHandlers(mockRequest, mockServer); - - expect(handlers).to.have.property('environment', 'server'); - expect(handlers).to.have.property('serverUri'); - expect(handlers).to.have.property('elasticsearchClient'); - }); - - describe('elasticsearchClient', () => { - it('executes callWithRequest', async () => { - const handlers = createHandlers(mockRequest, mockServer); - const [request, endpoint, payload] = await handlers.elasticsearchClient( - 'endpoint', - 'payload' - ); - expect(request).to.equal(mockRequest); - expect(endpoint).to.equal('endpoint'); - expect(payload).to.equal('payload'); - }); - }); -}); diff --git a/src/legacy/core_plugins/interpreter/server/routes/server_functions.ts b/src/legacy/core_plugins/interpreter/server/routes/server_functions.ts deleted file mode 100644 index e03ad361b5555..0000000000000 --- a/src/legacy/core_plugins/interpreter/server/routes/server_functions.ts +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import Boom from 'boom'; -import Joi from 'joi'; -import { serializeProvider } from '../../../../../plugins/expressions/common'; -import { createHandlers } from '../lib/create_handlers'; - -const API_ROUTE = '/api/interpreter'; - -/** - * Register the Canvas function endopints. - * - * @param {*} server - The Kibana server - */ -export function registerServerFunctions(server: any) { - getServerFunctions(server); - runServerFunctions(server); -} - -/** - * Register the endpoint that executes a batch of functions, and sends the result back as a single response. - * - * @param {*} server - The Kibana server - */ -function runServerFunctions(server: any) { - server.route({ - method: 'POST', - path: `${API_ROUTE}/fns`, - options: { - payload: { - allow: 'application/json', - maxBytes: 26214400, // 25MB payload limit - }, - validate: { - payload: Joi.object({ - functions: Joi.array() - .items( - Joi.object().keys({ - id: Joi.number().required(), - functionName: Joi.string().required(), - args: Joi.object().default({}), - context: Joi.any().default(null), - }) - ) - .required(), - }).required(), - }, - }, - async handler(req: any) { - const handlers = await createHandlers(req, server); - const { functions } = req.payload; - - // Grab the raw Node response object. - const res = req.raw.res; - - // Tell Hapi not to manage the response https://github.com/hapijs/hapi/issues/3884 - req._isReplied = true; - - // Send the initial headers. - res.writeHead(200, { - 'Content-Type': 'application/x-ndjson', - Connection: 'keep-alive', - 'Transfer-Encoding': 'chunked', - 'Cache-Control': 'no-cache', - }); - - // Write a length-delimited response - const streamResult = (result: any) => { - res.write(JSON.stringify(result) + '\n'); - }; - - // Tries to run an interpreter function, and ensures a consistent error payload on failure. - const tryFunction = async (id: any, fnCall: any) => { - try { - const result = await runFunction(server, handlers, fnCall); - - if (typeof result === 'undefined') { - return batchError(id, `Function ${fnCall.functionName} did not return anything.`); - } - - return { id, statusCode: 200, result }; - } catch (err) { - if (Boom.isBoom(err)) { - return batchError(id, err.output.payload, (err as any).statusCode); - } else if (err instanceof Error) { - return batchError(id, err.message); - } - - server.log(['interpreter', 'error'], err); - return batchError(id, 'See server logs for details.'); - } - }; - - // Process each function individually, and stream the responses back to the client - await Promise.all( - functions.map(({ id, ...fnCall }: any) => tryFunction(id, fnCall).then(streamResult)) - ); - - // All of the responses have been written, so we can close the response. - res.end(); - }, - }); -} - -/** - * A helper function for bundling up errors. - */ -function batchError(id: any, message: any, statusCode = 500) { - return { - id, - statusCode, - result: { statusCode, message }, - }; -} - -/** - * Register the endpoint that returns the list of server-only functions. - * @param {*} server - The Kibana server - */ -function getServerFunctions(server: any) { - server.route({ - method: 'GET', - path: `${API_ROUTE}/fns`, - handler() { - return server.plugins.interpreter.registries().serverFunctions.toJS(); - }, - }); -} - -/** - * Run a single Canvas function. - * - * @param {*} server - The Kibana server object - * @param {*} handlers - The Canvas handlers - * @param {*} fnCall - Describes the function being run `{ functionName, args, context }` - */ -async function runFunction(server: any, handlers: any, fnCall: any) { - const registries = server.plugins.interpreter.registries(); - const { functionName, args, context } = fnCall; - const types = registries.types.toJS(); - const { deserialize } = serializeProvider(types); - const fnDef = registries.serverFunctions.toJS()[functionName]; - - if (!fnDef) { - throw Boom.notFound(`Function "${functionName}" could not be found.`); - } - - return fnDef.fn(deserialize(context), args, handlers); -} diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/package.json b/src/legacy/core_plugins/kbn_vislib_vis_types/package.json deleted file mode 100644 index b6df6eb794de8..0000000000000 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "kbn_vislib_vis_types", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/controller.js b/src/legacy/core_plugins/kbn_vislib_vis_types/public/controller.js deleted file mode 100644 index 46f2376cb03eb..0000000000000 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/controller.js +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import $ from 'jquery'; -import React from 'react'; - -import { - CUSTOM_LEGEND_VIS_TYPES, - VisLegend, -} from '../../../ui/public/vis/vis_types/vislib_vis_legend'; -import { VislibVisProvider } from '../../../ui/public/vislib/vis'; -import chrome from '../../../ui/public/chrome'; -import { mountReactNode } from '../../../../core/public/utils'; - -const legendClassName = { - top: 'visLib--legend-top', - bottom: 'visLib--legend-bottom', - left: 'visLib--legend-left', - right: 'visLib--legend-right', -}; - -export class vislibVisController { - constructor(el, vis) { - this.el = el; - this.vis = vis; - this.unmount = null; - this.legendRef = React.createRef(); - - // vis mount point - this.container = document.createElement('div'); - this.container.className = 'visLib'; - this.el.appendChild(this.container); - - // chart mount point - this.chartEl = document.createElement('div'); - this.chartEl.className = 'visLib__chart'; - this.container.appendChild(this.chartEl); - // legend mount point - this.legendEl = document.createElement('div'); - this.legendEl.className = 'visLib__legend'; - this.container.appendChild(this.legendEl); - } - - render(esResponse, visParams) { - if (this.vislibVis) { - this.destroy(); - } - - return new Promise(async resolve => { - if (!this.vislib) { - const $injector = await chrome.dangerouslyGetActiveInjector(); - const Private = $injector.get('Private'); - this.Vislib = Private(VislibVisProvider); - } - - if (this.el.clientWidth === 0 || this.el.clientHeight === 0) { - return resolve(); - } - - this.vislibVis = new this.Vislib(this.chartEl, visParams); - this.vislibVis.on('brush', this.vis.API.events.brush); - this.vislibVis.on('click', this.vis.API.events.filter); - this.vislibVis.on('renderComplete', resolve); - - this.vislibVis.initVisConfig(esResponse, this.vis.getUiState()); - - if (visParams.addLegend) { - $(this.container) - .attr('class', (i, cls) => { - return cls.replace(/visLib--legend-\S+/g, ''); - }) - .addClass(legendClassName[visParams.legendPosition]); - - this.mountLegend(esResponse, visParams.legendPosition); - } - - this.vislibVis.render(esResponse, this.vis.getUiState()); - - // refreshing the legend after the chart is rendered. - // this is necessary because some visualizations - // provide data necessary for the legend only after a render cycle. - if ( - visParams.addLegend && - CUSTOM_LEGEND_VIS_TYPES.includes(this.vislibVis.visConfigArgs.type) - ) { - this.unmountLegend(); - this.mountLegend(esResponse, visParams.legendPosition); - this.vislibVis.render(esResponse, this.vis.getUiState()); - } - }); - } - - mountLegend(visData, position) { - this.unmount = mountReactNode( - - )(this.legendEl); - } - - unmountLegend() { - if (this.unmount) { - this.unmount(); - } - } - - destroy() { - if (this.unmount) { - this.unmount(); - } - - if (this.vislibVis) { - this.vislibVis.off('brush', this.vis.API.events.brush); - this.vislibVis.off('click', this.vis.API.events.filter); - this.vislibVis.destroy(); - delete this.vislibVis; - } - } -} diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/gauge.d.ts b/src/legacy/core_plugins/kbn_vislib_vis_types/public/gauge.d.ts deleted file mode 100644 index ff8345e9a5b25..0000000000000 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/gauge.d.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { RangeValues } from 'ui/vis/editors/default/controls/ranges'; -import { Alignments, GaugeTypes } from './utils/collections'; -import { ColorSchemaVislibParams, Labels, Style } from './types'; - -interface Gauge extends ColorSchemaVislibParams { - backStyle: 'Full'; - gaugeStyle: 'Full'; - orientation: 'vertical'; - type: 'meter'; - alignment: Alignments; - colorsRange: RangeValues[]; - extendRange: boolean; - gaugeType: GaugeTypes; - labels: Labels; - percentageMode: boolean; - outline?: boolean; - scale: { - show: boolean; - labels: false; - color: 'rgba(105,112,125,0.2)'; - }; - style: Style; -} - -export interface GaugeVisParams { - type: 'gauge'; - addTooltip: boolean; - addLegend: boolean; - isDisplayWarning: boolean; - gauge: Gauge; -} diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/heatmap.d.ts b/src/legacy/core_plugins/kbn_vislib_vis_types/public/heatmap.d.ts deleted file mode 100644 index 13c676854ead2..0000000000000 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/heatmap.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { ColorSchemas } from 'ui/vislib/components/color/colormaps'; -import { RangeValues } from 'ui/vis/editors/default/controls/ranges'; -import { TimeMarker } from 'ui/vislib/visualizations/time_marker'; -import { CommonVislibParams, ColorSchemaVislibParams, ValueAxis } from './types'; -import { Positions } from './utils/collections'; - -export interface HeatmapVisParams extends CommonVislibParams, ColorSchemaVislibParams { - type: 'heatmap'; - addLegend: boolean; - enableHover: boolean; - colorsNumber: number | ''; - colorsRange: RangeValues[]; - valueAxes: ValueAxis[]; - setColorRange: boolean; - percentageMode: boolean; - times: TimeMarker[]; -} diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/kbn_vislib_vis_types.js b/src/legacy/core_plugins/kbn_vislib_vis_types/public/kbn_vislib_vis_types.js deleted file mode 100644 index c82073ff582b8..0000000000000 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/kbn_vislib_vis_types.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { setup as visualizations } from '../../visualizations/public/np_ready/public/legacy'; - -import { histogramDefinition } from './histogram'; -import { lineDefinition } from './line'; -import { pieDefinition } from './pie'; -import { areaDefinition } from './area'; -import { heatmapDefinition } from './heatmap'; -import { horizontalBarDefinition } from './horizontal_bar'; -import { gaugeDefinition } from './gauge'; -import { goalDefinition } from './goal'; - -visualizations.types.createBaseVisualization(histogramDefinition); -visualizations.types.createBaseVisualization(lineDefinition); -visualizations.types.createBaseVisualization(pieDefinition); -visualizations.types.createBaseVisualization(areaDefinition); -visualizations.types.createBaseVisualization(heatmapDefinition); -visualizations.types.createBaseVisualization(horizontalBarDefinition); -visualizations.types.createBaseVisualization(gaugeDefinition); -visualizations.types.createBaseVisualization(goalDefinition); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js index 78ac99567d10e..2927565e61dce 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js @@ -826,9 +826,14 @@ function discoverController( }; $scope.updateQueryAndFetch = function({ query, dateRange }) { + const oldDateRange = timefilter.getTime(); timefilter.setTime(dateRange); $state.query = query; - $scope.fetch(); + // storing the updated timerange in the state will trigger a fetch + // call automatically, so only trigger fetch in case this is a refresh call (no changes in parameters). + if (_.isEqual(oldDateRange, dateRange)) { + $scope.fetch(); + } }; function onResults(resp) { diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/ibmmq_metrics/screenshot.png b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/ibmmq_metrics/screenshot.png new file mode 100644 index 0000000000000..c4f202ad13bb7 Binary files /dev/null and b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/ibmmq_metrics/screenshot.png differ diff --git a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/ibmmq.svg b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/ibmmq.svg index ad0cb64b161dd..e474d93359beb 100644 --- a/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/ibmmq.svg +++ b/src/legacy/core_plugins/kibana/public/home/tutorial_resources/logos/ibmmq.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/legacy/core_plugins/kibana/public/index.scss b/src/legacy/core_plugins/kibana/public/index.scss index 9fc25b1fbe61e..324458c0814d9 100644 --- a/src/legacy/core_plugins/kibana/public/index.scss +++ b/src/legacy/core_plugins/kibana/public/index.scss @@ -7,6 +7,9 @@ // Public UI styles @import 'src/legacy/ui/public/index'; +// vis_type_vislib UI styles +@import 'src/legacy/core_plugins/vis_type_vislib/public/index'; + // Dev tools styles @import './dev_tools/index'; diff --git a/src/legacy/core_plugins/kibana/public/kibana.js b/src/legacy/core_plugins/kibana/public/kibana.js index 334397c479a1d..4100ae7205869 100644 --- a/src/legacy/core_plugins/kibana/public/kibana.js +++ b/src/legacy/core_plugins/kibana/public/kibana.js @@ -52,7 +52,7 @@ import './visualize'; import './dashboard'; import './management'; import './dev_tools'; -import 'ui/vislib'; +import 'ui/color_maps'; import 'ui/agg_response'; import 'ui/agg_types'; import { showAppRedirectNotification } from 'ui/notify'; diff --git a/src/legacy/core_plugins/kibana/server/tutorials/ibmmq_metrics/index.js b/src/legacy/core_plugins/kibana/server/tutorials/ibmmq_metrics/index.js new file mode 100644 index 0000000000000..b2824832dc14c --- /dev/null +++ b/src/legacy/core_plugins/kibana/server/tutorials/ibmmq_metrics/index.js @@ -0,0 +1,67 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { TUTORIAL_CATEGORY } from '../../../common/tutorials/tutorial_category'; +import { + onPremInstructions, + cloudInstructions, + onPremCloudInstructions, +} from '../../../common/tutorials/metricbeat_instructions'; + +export function ibmmqMetricsSpecProvider(context) { + const moduleName = 'ibmmq'; + return { + id: 'ibmmqMetrics', + name: i18n.translate('kbn.server.tutorials.ibmmqMetrics.nameTitle', { + defaultMessage: 'IBM MQ metrics', + }), + category: TUTORIAL_CATEGORY.METRICS, + shortDescription: i18n.translate('kbn.server.tutorials.ibmmqMetrics.shortDescription', { + defaultMessage: 'Fetch monitoring metrics from IBM MQ instances.', + }), + longDescription: i18n.translate('kbn.server.tutorials.ibmmqMetrics.longDescription', { + defaultMessage: + 'The `ibmmq` Metricbeat module fetches monitoring metrics from IBM MQ instances \ +[Learn more]({learnMoreLink}).', + values: { + learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-ibmmq.html', + }, + }), + euiIconType: '/plugins/kibana/home/tutorial_resources/logos/ibmmq.svg', + isBeta: true, + artifacts: { + application: { + label: i18n.translate('kbn.server.tutorials.ibmmqMetrics.artifacts.application.label', { + defaultMessage: 'Discover', + }), + path: '/app/kibana#/discover', + }, + dashboards: [], + exportedFields: { + documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-ibmmq.html', + }, + }, + completionTimeMinutes: 10, + previewImagePath: '/plugins/kibana/home/tutorial_resources/ibmmq_metrics/screenshot.png', + onPrem: onPremInstructions(moduleName, null, null, null, context), + elasticCloud: cloudInstructions(moduleName), + onPremElasticCloud: onPremCloudInstructions(moduleName), + }; +} diff --git a/src/legacy/core_plugins/kibana/server/tutorials/register.js b/src/legacy/core_plugins/kibana/server/tutorials/register.js index 69a6ac76e4a8f..2f69e7dbcbc7d 100644 --- a/src/legacy/core_plugins/kibana/server/tutorials/register.js +++ b/src/legacy/core_plugins/kibana/server/tutorials/register.js @@ -84,6 +84,7 @@ import { activemqLogsSpecProvider } from './activemq_logs'; import { activemqMetricsSpecProvider } from './activemq_metrics'; import { azureMetricsSpecProvider } from './azure_metrics'; import { ibmmqLogsSpecProvider } from './ibmmq_logs'; +import { ibmmqMetricsSpecProvider } from './ibmmq_metrics'; import { stanMetricsSpecProvider } from './stan_metrics'; import { envoyproxyMetricsSpecProvider } from './envoyproxy_metrics'; @@ -158,6 +159,7 @@ export function registerTutorials(server) { server.newPlatform.setup.plugins.home.tutorials.registerTutorial(activemqMetricsSpecProvider); server.newPlatform.setup.plugins.home.tutorials.registerTutorial(azureMetricsSpecProvider); server.newPlatform.setup.plugins.home.tutorials.registerTutorial(ibmmqLogsSpecProvider); + server.newPlatform.setup.plugins.home.tutorials.registerTutorial(ibmmqMetricsSpecProvider); server.newPlatform.setup.plugins.home.tutorials.registerTutorial(stanMetricsSpecProvider); server.newPlatform.setup.plugins.home.tutorials.registerTutorial(envoyproxyMetricsSpecProvider); } diff --git a/src/legacy/core_plugins/region_map/public/choropleth_layer.js b/src/legacy/core_plugins/region_map/public/choropleth_layer.js index f4c6bfd6bedf5..8132976fcbc69 100644 --- a/src/legacy/core_plugins/region_map/public/choropleth_layer.js +++ b/src/legacy/core_plugins/region_map/public/choropleth_layer.js @@ -23,7 +23,7 @@ import _ from 'lodash'; import d3 from 'd3'; import { i18n } from '@kbn/i18n'; import { KibanaMapLayer } from 'ui/vis/map/kibana_map_layer'; -import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colormaps'; +import { truncatedColorMaps } from 'ui/color_maps'; import * as topojson from 'topojson-client'; import { toastNotifications } from 'ui/notify'; import * as colorUtil from 'ui/vis/map/color_util'; diff --git a/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx b/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx index 8306b3274a914..73fe07ec60102 100644 --- a/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx +++ b/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx @@ -28,7 +28,7 @@ import { NumberInputOption, SelectOption, SwitchOption, -} from '../../../kbn_vislib_vis_types/public/components'; +} from '../../../vis_type_vislib/public/components'; import { WmsOptions } from '../../../tile_map/public/components/wms_options'; import { RegionMapVisParams } from '../types'; diff --git a/src/legacy/core_plugins/region_map/public/region_map_type.js b/src/legacy/core_plugins/region_map/public/region_map_type.js index 6f83ae912e184..39353a379ce52 100644 --- a/src/legacy/core_plugins/region_map/public/region_map_type.js +++ b/src/legacy/core_plugins/region_map/public/region_map_type.js @@ -19,7 +19,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { Schemas } from 'ui/vis/editors/default/schemas'; -import { colorSchemas } from 'ui/vislib/components/color/truncated_colormaps'; +import { truncatedColorSchemas as colorSchemas } from 'ui/color_maps'; import { mapToLayerWithId } from './util'; import { createRegionMapVisualization } from './region_map_visualization'; import { Status } from '../../visualizations/public'; diff --git a/src/legacy/core_plugins/region_map/public/region_map_visualization.js b/src/legacy/core_plugins/region_map/public/region_map_visualization.js index b58de5d9c6ab7..f9a5793ca8137 100644 --- a/src/legacy/core_plugins/region_map/public/region_map_visualization.js +++ b/src/legacy/core_plugins/region_map/public/region_map_visualization.js @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import ChoroplethLayer from './choropleth_layer'; -import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colormaps'; +import { truncatedColorMaps } from 'ui/color_maps'; import { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities'; import { toastNotifications } from 'ui/notify'; diff --git a/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx b/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx index 15c92f4617497..e57cea8467d12 100644 --- a/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx +++ b/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx @@ -27,7 +27,7 @@ import { RangeOption, SelectOption, SwitchOption, -} from '../../../kbn_vislib_vis_types/public/components'; +} from '../../../vis_type_vislib/public/components'; import { WmsOptions } from './wms_options'; import { TileMapVisParams } from '../types'; import { MapTypes } from '../map_types'; diff --git a/src/legacy/core_plugins/tile_map/public/components/wms_internal_options.tsx b/src/legacy/core_plugins/tile_map/public/components/wms_internal_options.tsx index f2cf69bb63ba4..2989f6ce7ebd5 100644 --- a/src/legacy/core_plugins/tile_map/public/components/wms_internal_options.tsx +++ b/src/legacy/core_plugins/tile_map/public/components/wms_internal_options.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { EuiLink, EuiSpacer, EuiText, EuiScreenReaderOnly } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { TextInputOption } from '../../../kbn_vislib_vis_types/public/components'; +import { TextInputOption } from '../../../vis_type_vislib/public/components'; import { WMSOptions } from '../types'; interface WmsInternalOptions { diff --git a/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx b/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx index c5ccc3acba610..d9dca5afd7377 100644 --- a/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx +++ b/src/legacy/core_plugins/tile_map/public/components/wms_options.tsx @@ -25,7 +25,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { TmsLayer } from 'ui/vis/map/service_settings'; import { Vis } from 'ui/vis'; import { RegionMapVisParams } from '../../../region_map/public/types'; -import { SelectOption, SwitchOption } from '../../../kbn_vislib_vis_types/public/components'; +import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public/components'; import { WmsInternalOptions } from './wms_internal_options'; import { WMSOptions, TileMapVisParams } from '../types'; diff --git a/src/legacy/core_plugins/tile_map/public/markers/scaled_circles.js b/src/legacy/core_plugins/tile_map/public/markers/scaled_circles.js index e9334ff91def9..fe29d9b6aad21 100644 --- a/src/legacy/core_plugins/tile_map/public/markers/scaled_circles.js +++ b/src/legacy/core_plugins/tile_map/public/markers/scaled_circles.js @@ -22,7 +22,7 @@ import _ from 'lodash'; import d3 from 'd3'; import $ from 'jquery'; import { EventEmitter } from 'events'; -import { truncatedColorMaps } from 'ui/vislib/components/color/truncated_colormaps'; +import { truncatedColorMaps } from 'ui/color_maps'; import * as colorUtil from 'ui/vis/map/color_util'; export class ScaledCirclesMarkers extends EventEmitter { diff --git a/src/legacy/core_plugins/tile_map/public/tile_map_type.js b/src/legacy/core_plugins/tile_map/public/tile_map_type.js index f2e6469e768e7..c58c226f0aba0 100644 --- a/src/legacy/core_plugins/tile_map/public/tile_map_type.js +++ b/src/legacy/core_plugins/tile_map/public/tile_map_type.js @@ -21,7 +21,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { Schemas } from 'ui/vis/editors/default/schemas'; -import { colorSchemas } from 'ui/vislib/components/color/truncated_colormaps'; +import { truncatedColorSchemas as colorSchemas } from 'ui/color_maps'; import { convertToGeoJson } from 'ui/vis/map/convert_to_geojson'; import { createTileMapVisualization } from './tile_map_visualization'; diff --git a/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx b/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx index cce92a08f2c2b..125577815c207 100644 --- a/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx +++ b/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx @@ -22,7 +22,7 @@ import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { VisOptionsProps } from 'ui/vis/editors/default'; -import { RangeOption, SwitchOption } from '../../kbn_vislib_vis_types/public/components'; +import { RangeOption, SwitchOption } from '../../vis_type_vislib/public/components'; import { MarkdownVisParams } from './types'; function SettingsOptions({ stateParams, setValue }: VisOptionsProps) { diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_controller.js b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_controller.js index ecaf6b5d70d36..17dad6a4cd8cb 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_controller.js +++ b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_controller.js @@ -20,7 +20,7 @@ import { last, findIndex, isNaN } from 'lodash'; import React, { Component } from 'react'; import { isColorDark } from '@elastic/eui'; -import { getHeatmapColors } from 'ui/vislib/components/color/heatmap_color'; +import { getHeatmapColors } from 'ui/color_maps'; import { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities'; import { MetricVisValue } from './metric_vis_value'; diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx index 9649588976c0d..566618c527019 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx +++ b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx @@ -36,8 +36,8 @@ import { SwitchOption, RangeOption, SetColorSchemaOptionsValue, -} from '../../../kbn_vislib_vis_types/public/components'; -import { ColorModes } from '../../../kbn_vislib_vis_types/public/utils/collections'; +} from '../../../vis_type_vislib/public/components'; +import { ColorModes } from '../../../vis_type_vislib/public/utils/collections'; import { MetricVisParam, VisParams } from '../types'; function MetricVisOptions({ diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.ts index b64361f17c470..04bff5ccb4655 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { vislibColorMaps, ColorSchemas } from 'ui/vislib/components/color/colormaps'; +import { vislibColorMaps, ColorSchemas } from 'ui/color_maps'; import { ExpressionFunction, KibanaDatatable, @@ -27,7 +27,7 @@ import { Render, Style, } from '../../../../plugins/expressions/public'; -import { ColorModes } from '../../kbn_vislib_vis_types/public/utils/collections'; +import { ColorModes } from '../../vis_type_vislib/public/utils/collections'; import { visType, DimensionsVisParam, VisParams } from './types'; type Context = KibanaDatatable; diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts index ceab5dafe1f06..9b22423012b36 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts @@ -23,13 +23,13 @@ import { i18n } from '@kbn/i18n'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { AggGroupNames } from 'ui/vis/editors/default'; -import { colorSchemas, ColorSchemas } from 'ui/vislib/components/color/colormaps'; +import { colorSchemas, ColorSchemas } from 'ui/color_maps'; // @ts-ignore import { MetricVisComponent } from './components/metric_vis_controller'; import { MetricVisOptions } from './components/metric_vis_options'; -import { ColorModes } from '../../kbn_vislib_vis_types/public/utils/collections'; +import { ColorModes } from '../../vis_type_vislib/public/utils/collections'; export const metricVisDefinition = { name: 'metric', diff --git a/src/legacy/core_plugins/vis_type_metric/public/types.ts b/src/legacy/core_plugins/vis_type_metric/public/types.ts index ce0e78140a86a..06ec509f8c4d3 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/types.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/types.ts @@ -17,11 +17,11 @@ * under the License. */ -import { ColorSchemas } from 'ui/vislib/components/color/colormaps'; +import { ColorSchemas } from 'ui/color_maps'; import { RangeValues } from 'ui/vis/editors/default/controls/ranges'; import { SchemaConfig } from '../../visualizations/public'; -import { ColorModes } from '../../kbn_vislib_vis_types/public/utils/collections'; -import { Labels, Style } from '../../kbn_vislib_vis_types/public/types'; +import { ColorModes } from '../../vis_type_vislib/public/utils/collections'; +import { Labels, Style } from '../../vis_type_vislib/public/types'; export const visType = 'metric'; diff --git a/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx b/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx index 4d69af59b0c99..be82b52dee0fc 100644 --- a/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx +++ b/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx @@ -28,7 +28,7 @@ import { NumberInputOption, SwitchOption, SelectOption, -} from '../../../kbn_vislib_vis_types/public/components/common'; +} from '../../../vis_type_vislib/public/components/common'; import { TableVisParams } from '../types'; import { totalAggregations, isAggConfigNumeric } from './utils'; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx index 9e6a2d1a24a85..c500b5d888b05 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { ValidatedDualRange } from 'ui/validated_range'; import { VisOptionsProps } from 'ui/vis/editors/default'; -import { SelectOption, SwitchOption } from '../../../kbn_vislib_vis_types/public/components'; +import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public/components'; import { TagCloudVisParams } from '../types'; function TagCloudOptions({ stateParams, setValue, vis }: VisOptionsProps) { diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/fixture.json b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/fixture.json deleted file mode 100644 index 178704a36372d..0000000000000 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/fixture.json +++ /dev/null @@ -1,984 +0,0 @@ -{ - "_shards": { - "failed": 0, - "successful": 5, - "total": 5 - }, - "aggregations": { - "c9b5f9c0-e403-11e6-be91-6f7688e9fac7": { - "doc_count": 128145, - "timeseries": { - "buckets": [ - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.057 - }, - "doc_count": 368, - "key": 1485549090000, - "key_as_string": "2017-01-27T20:31:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.07466666666666667 - }, - "doc_count": 1106, - "key": 1485549120000, - "key_as_string": "2017-01-27T20:32:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.08033333333333335 - }, - "doc_count": 1107, - "key": 1485549150000, - "key_as_string": "2017-01-27T20:32:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.066 - }, - "doc_count": 1109, - "key": 1485549180000, - "key_as_string": "2017-01-27T20:33:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.05366666666666667 - }, - "doc_count": 1093, - "key": 1485549210000, - "key_as_string": "2017-01-27T20:33:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04533333333333334 - }, - "doc_count": 1086, - "key": 1485549240000, - "key_as_string": "2017-01-27T20:34:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037333333333333336 - }, - "doc_count": 1086, - "key": 1485549270000, - "key_as_string": "2017-01-27T20:34:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1090, - "key": 1485549300000, - "key_as_string": "2017-01-27T20:35:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.036000000000000004 - }, - "doc_count": 1085, - "key": 1485549330000, - "key_as_string": "2017-01-27T20:35:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1082, - "key": 1485549360000, - "key_as_string": "2017-01-27T20:36:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1080, - "key": 1485549390000, - "key_as_string": "2017-01-27T20:36:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037000000000000005 - }, - "doc_count": 1082, - "key": 1485549420000, - "key_as_string": "2017-01-27T20:37:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.036000000000000004 - }, - "doc_count": 1079, - "key": 1485549450000, - "key_as_string": "2017-01-27T20:37:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03233333333333333 - }, - "doc_count": 1080, - "key": 1485549480000, - "key_as_string": "2017-01-27T20:38:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1080, - "key": 1485549510000, - "key_as_string": "2017-01-27T20:38:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1082, - "key": 1485549540000, - "key_as_string": "2017-01-27T20:39:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.042666666666666665 - }, - "doc_count": 1079, - "key": 1485549570000, - "key_as_string": "2017-01-27T20:39:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1077, - "key": 1485549600000, - "key_as_string": "2017-01-27T20:40:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03833333333333334 - }, - "doc_count": 1075, - "key": 1485549630000, - "key_as_string": "2017-01-27T20:40:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1076, - "key": 1485549660000, - "key_as_string": "2017-01-27T20:41:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1076, - "key": 1485549690000, - "key_as_string": "2017-01-27T20:41:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1074, - "key": 1485549720000, - "key_as_string": "2017-01-27T20:42:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1072, - "key": 1485549750000, - "key_as_string": "2017-01-27T20:42:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1067, - "key": 1485549780000, - "key_as_string": "2017-01-27T20:43:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.036000000000000004 - }, - "doc_count": 1065, - "key": 1485549810000, - "key_as_string": "2017-01-27T20:43:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1065, - "key": 1485549840000, - "key_as_string": "2017-01-27T20:44:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.036333333333333336 - }, - "doc_count": 1062, - "key": 1485549870000, - "key_as_string": "2017-01-27T20:44:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1063, - "key": 1485549900000, - "key_as_string": "2017-01-27T20:45:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1065, - "key": 1485549930000, - "key_as_string": "2017-01-27T20:45:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1065, - "key": 1485549960000, - "key_as_string": "2017-01-27T20:46:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1069, - "key": 1485549990000, - "key_as_string": "2017-01-27T20:46:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03333333333333333 - }, - "doc_count": 1068, - "key": 1485550020000, - "key_as_string": "2017-01-27T20:47:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1068, - "key": 1485550050000, - "key_as_string": "2017-01-27T20:47:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1068, - "key": 1485550080000, - "key_as_string": "2017-01-27T20:48:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1074, - "key": 1485550110000, - "key_as_string": "2017-01-27T20:48:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03133333333333333 - }, - "doc_count": 1074, - "key": 1485550140000, - "key_as_string": "2017-01-27T20:49:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1074, - "key": 1485550170000, - "key_as_string": "2017-01-27T20:49:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03133333333333333 - }, - "doc_count": 1073, - "key": 1485550200000, - "key_as_string": "2017-01-27T20:50:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1077, - "key": 1485550230000, - "key_as_string": "2017-01-27T20:50:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03166666666666667 - }, - "doc_count": 1074, - "key": 1485550260000, - "key_as_string": "2017-01-27T20:51:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.031 - }, - "doc_count": 1074, - "key": 1485550290000, - "key_as_string": "2017-01-27T20:51:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1072, - "key": 1485550320000, - "key_as_string": "2017-01-27T20:52:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1073, - "key": 1485550350000, - "key_as_string": "2017-01-27T20:52:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03466666666666667 - }, - "doc_count": 1071, - "key": 1485550380000, - "key_as_string": "2017-01-27T20:53:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1071, - "key": 1485550410000, - "key_as_string": "2017-01-27T20:53:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03166666666666667 - }, - "doc_count": 1069, - "key": 1485550440000, - "key_as_string": "2017-01-27T20:54:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1069, - "key": 1485550470000, - "key_as_string": "2017-01-27T20:54:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.032 - }, - "doc_count": 1068, - "key": 1485550500000, - "key_as_string": "2017-01-27T20:55:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1067, - "key": 1485550530000, - "key_as_string": "2017-01-27T20:55:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03233333333333333 - }, - "doc_count": 1065, - "key": 1485550560000, - "key_as_string": "2017-01-27T20:56:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1069, - "key": 1485550590000, - "key_as_string": "2017-01-27T20:56:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03166666666666667 - }, - "doc_count": 1068, - "key": 1485550620000, - "key_as_string": "2017-01-27T20:57:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03333333333333333 - }, - "doc_count": 1068, - "key": 1485550650000, - "key_as_string": "2017-01-27T20:57:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1068, - "key": 1485550680000, - "key_as_string": "2017-01-27T20:58:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1071, - "key": 1485550710000, - "key_as_string": "2017-01-27T20:58:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03333333333333333 - }, - "doc_count": 1074, - "key": 1485550740000, - "key_as_string": "2017-01-27T20:59:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1074, - "key": 1485550770000, - "key_as_string": "2017-01-27T20:59:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04 - }, - "doc_count": 1074, - "key": 1485550800000, - "key_as_string": "2017-01-27T21:00:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.032 - }, - "doc_count": 1076, - "key": 1485550830000, - "key_as_string": "2017-01-27T21:00:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1078, - "key": 1485550860000, - "key_as_string": "2017-01-27T21:01:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1077, - "key": 1485550890000, - "key_as_string": "2017-01-27T21:01:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03466666666666667 - }, - "doc_count": 1071, - "key": 1485550920000, - "key_as_string": "2017-01-27T21:02:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03466666666666667 - }, - "doc_count": 1071, - "key": 1485550950000, - "key_as_string": "2017-01-27T21:02:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03566666666666667 - }, - "doc_count": 1073, - "key": 1485550980000, - "key_as_string": "2017-01-27T21:03:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1071, - "key": 1485551010000, - "key_as_string": "2017-01-27T21:03:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03466666666666667 - }, - "doc_count": 1069, - "key": 1485551040000, - "key_as_string": "2017-01-27T21:04:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03566666666666667 - }, - "doc_count": 1068, - "key": 1485551070000, - "key_as_string": "2017-01-27T21:04:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1075, - "key": 1485551100000, - "key_as_string": "2017-01-27T21:05:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1074, - "key": 1485551130000, - "key_as_string": "2017-01-27T21:05:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03133333333333333 - }, - "doc_count": 1073, - "key": 1485551160000, - "key_as_string": "2017-01-27T21:06:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1071, - "key": 1485551190000, - "key_as_string": "2017-01-27T21:06:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03333333333333333 - }, - "doc_count": 1075, - "key": 1485551220000, - "key_as_string": "2017-01-27T21:07:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03133333333333333 - }, - "doc_count": 1071, - "key": 1485551250000, - "key_as_string": "2017-01-27T21:07:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04733333333333334 - }, - "doc_count": 1081, - "key": 1485551280000, - "key_as_string": "2017-01-27T21:08:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.044333333333333336 - }, - "doc_count": 1078, - "key": 1485551310000, - "key_as_string": "2017-01-27T21:08:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037000000000000005 - }, - "doc_count": 1079, - "key": 1485551340000, - "key_as_string": "2017-01-27T21:09:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1077, - "key": 1485551370000, - "key_as_string": "2017-01-27T21:09:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03866666666666666 - }, - "doc_count": 1077, - "key": 1485551400000, - "key_as_string": "2017-01-27T21:10:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037666666666666675 - }, - "doc_count": 1075, - "key": 1485551430000, - "key_as_string": "2017-01-27T21:10:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.038000000000000006 - }, - "doc_count": 1078, - "key": 1485551460000, - "key_as_string": "2017-01-27T21:11:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037 - }, - "doc_count": 1074, - "key": 1485551490000, - "key_as_string": "2017-01-27T21:11:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.036666666666666674 - }, - "doc_count": 1074, - "key": 1485551520000, - "key_as_string": "2017-01-27T21:12:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037333333333333336 - }, - "doc_count": 1076, - "key": 1485551550000, - "key_as_string": "2017-01-27T21:12:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03733333333333333 - }, - "doc_count": 1075, - "key": 1485551580000, - "key_as_string": "2017-01-27T21:13:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04533333333333334 - }, - "doc_count": 1077, - "key": 1485551610000, - "key_as_string": "2017-01-27T21:13:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.039 - }, - "doc_count": 1080, - "key": 1485551640000, - "key_as_string": "2017-01-27T21:14:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.057666666666666665 - }, - "doc_count": 1080, - "key": 1485551670000, - "key_as_string": "2017-01-27T21:14:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.045000000000000005 - }, - "doc_count": 1080, - "key": 1485551700000, - "key_as_string": "2017-01-27T21:15:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037666666666666675 - }, - "doc_count": 1080, - "key": 1485551730000, - "key_as_string": "2017-01-27T21:15:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1080, - "key": 1485551760000, - "key_as_string": "2017-01-27T21:16:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.038 - }, - "doc_count": 1080, - "key": 1485551790000, - "key_as_string": "2017-01-27T21:16:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1080, - "key": 1485551820000, - "key_as_string": "2017-01-27T21:17:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04966666666666667 - }, - "doc_count": 1080, - "key": 1485551850000, - "key_as_string": "2017-01-27T21:17:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1080, - "key": 1485551880000, - "key_as_string": "2017-01-27T21:18:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04 - }, - "doc_count": 1080, - "key": 1485551910000, - "key_as_string": "2017-01-27T21:18:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03766666666666666 - }, - "doc_count": 1080, - "key": 1485551940000, - "key_as_string": "2017-01-27T21:19:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1076, - "key": 1485551970000, - "key_as_string": "2017-01-27T21:19:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1077, - "key": 1485552000000, - "key_as_string": "2017-01-27T21:20:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1077, - "key": 1485552030000, - "key_as_string": "2017-01-27T21:20:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.029666666666666664 - }, - "doc_count": 1077, - "key": 1485552060000, - "key_as_string": "2017-01-27T21:21:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.02766666666666667 - }, - "doc_count": 1077, - "key": 1485552090000, - "key_as_string": "2017-01-27T21:21:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.028 - }, - "doc_count": 1077, - "key": 1485552120000, - "key_as_string": "2017-01-27T21:22:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.030666666666666665 - }, - "doc_count": 1077, - "key": 1485552150000, - "key_as_string": "2017-01-27T21:22:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03833333333333334 - }, - "doc_count": 1083, - "key": 1485552180000, - "key_as_string": "2017-01-27T21:23:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04966666666666667 - }, - "doc_count": 1083, - "key": 1485552210000, - "key_as_string": "2017-01-27T21:23:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.041 - }, - "doc_count": 1082, - "key": 1485552240000, - "key_as_string": "2017-01-27T21:24:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037333333333333336 - }, - "doc_count": 1087, - "key": 1485552270000, - "key_as_string": "2017-01-27T21:24:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.039 - }, - "doc_count": 1083, - "key": 1485552300000, - "key_as_string": "2017-01-27T21:25:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03933333333333333 - }, - "doc_count": 1083, - "key": 1485552330000, - "key_as_string": "2017-01-27T21:25:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037666666666666675 - }, - "doc_count": 1083, - "key": 1485552360000, - "key_as_string": "2017-01-27T21:26:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04033333333333333 - }, - "doc_count": 1083, - "key": 1485552390000, - "key_as_string": "2017-01-27T21:26:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03866666666666666 - }, - "doc_count": 1082, - "key": 1485552420000, - "key_as_string": "2017-01-27T21:27:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04033333333333334 - }, - "doc_count": 1083, - "key": 1485552450000, - "key_as_string": "2017-01-27T21:27:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04 - }, - "doc_count": 1083, - "key": 1485552480000, - "key_as_string": "2017-01-27T21:28:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04033333333333333 - }, - "doc_count": 1084, - "key": 1485552510000, - "key_as_string": "2017-01-27T21:28:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03333333333333333 - }, - "doc_count": 1083, - "key": 1485552540000, - "key_as_string": "2017-01-27T21:29:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03566666666666667 - }, - "doc_count": 1083, - "key": 1485552570000, - "key_as_string": "2017-01-27T21:29:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04466666666666667 - }, - "doc_count": 1083, - "key": 1485552600000, - "key_as_string": "2017-01-27T21:30:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03233333333333333 - }, - "doc_count": 1083, - "key": 1485552630000, - "key_as_string": "2017-01-27T21:30:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.0505 - }, - "doc_count": 722, - "key": 1485552660000, - "key_as_string": "2017-01-27T21:31:00.000Z" - } - ] - } - } - }, - "hits": { - "hits": [], - "max_score": 0, - "total": 128145 - }, - "status": 200, - "timed_out": false, - "took": 28 -} diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/fixtures/std_metric_fixture.json b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/fixtures/std_metric_fixture.json deleted file mode 100644 index d587e48b31881..0000000000000 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/fixtures/std_metric_fixture.json +++ /dev/null @@ -1,985 +0,0 @@ -{ - "_shards": { - "failed": 0, - "successful": 5, - "total": 5 - }, - "aggregations": { - "c9b5f9c0-e403-11e6-be91-6f7688e9fac7": { - "doc_count": 128145, - "timeseries": { - "buckets": [ - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.057 - }, - "doc_count": 368, - "key": 1485549090000, - "key_as_string": "2017-01-27T20:31:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.07466666666666667 - }, - "doc_count": 1106, - "key": 1485549120000, - "key_as_string": "2017-01-27T20:32:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.08033333333333335 - }, - "doc_count": 1107, - "key": 1485549150000, - "key_as_string": "2017-01-27T20:32:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.066 - }, - "doc_count": 1109, - "key": 1485549180000, - "key_as_string": "2017-01-27T20:33:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.05366666666666667 - }, - "doc_count": 1093, - "key": 1485549210000, - "key_as_string": "2017-01-27T20:33:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04533333333333334 - }, - "doc_count": 1086, - "key": 1485549240000, - "key_as_string": "2017-01-27T20:34:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037333333333333336 - }, - "doc_count": 1086, - "key": 1485549270000, - "key_as_string": "2017-01-27T20:34:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1090, - "key": 1485549300000, - "key_as_string": "2017-01-27T20:35:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.036000000000000004 - }, - "doc_count": 1085, - "key": 1485549330000, - "key_as_string": "2017-01-27T20:35:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1082, - "key": 1485549360000, - "key_as_string": "2017-01-27T20:36:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1080, - "key": 1485549390000, - "key_as_string": "2017-01-27T20:36:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037000000000000005 - }, - "doc_count": 1082, - "key": 1485549420000, - "key_as_string": "2017-01-27T20:37:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.036000000000000004 - }, - "doc_count": 1079, - "key": 1485549450000, - "key_as_string": "2017-01-27T20:37:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03233333333333333 - }, - "doc_count": 1080, - "key": 1485549480000, - "key_as_string": "2017-01-27T20:38:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1080, - "key": 1485549510000, - "key_as_string": "2017-01-27T20:38:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1082, - "key": 1485549540000, - "key_as_string": "2017-01-27T20:39:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.042666666666666665 - }, - "doc_count": 1079, - "key": 1485549570000, - "key_as_string": "2017-01-27T20:39:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1077, - "key": 1485549600000, - "key_as_string": "2017-01-27T20:40:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03833333333333334 - }, - "doc_count": 1075, - "key": 1485549630000, - "key_as_string": "2017-01-27T20:40:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1076, - "key": 1485549660000, - "key_as_string": "2017-01-27T20:41:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1076, - "key": 1485549690000, - "key_as_string": "2017-01-27T20:41:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1074, - "key": 1485549720000, - "key_as_string": "2017-01-27T20:42:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1072, - "key": 1485549750000, - "key_as_string": "2017-01-27T20:42:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1067, - "key": 1485549780000, - "key_as_string": "2017-01-27T20:43:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.036000000000000004 - }, - "doc_count": 1065, - "key": 1485549810000, - "key_as_string": "2017-01-27T20:43:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1065, - "key": 1485549840000, - "key_as_string": "2017-01-27T20:44:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.036333333333333336 - }, - "doc_count": 1062, - "key": 1485549870000, - "key_as_string": "2017-01-27T20:44:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1063, - "key": 1485549900000, - "key_as_string": "2017-01-27T20:45:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1065, - "key": 1485549930000, - "key_as_string": "2017-01-27T20:45:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1065, - "key": 1485549960000, - "key_as_string": "2017-01-27T20:46:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1069, - "key": 1485549990000, - "key_as_string": "2017-01-27T20:46:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03333333333333333 - }, - "doc_count": 1068, - "key": 1485550020000, - "key_as_string": "2017-01-27T20:47:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1068, - "key": 1485550050000, - "key_as_string": "2017-01-27T20:47:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1068, - "key": 1485550080000, - "key_as_string": "2017-01-27T20:48:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1074, - "key": 1485550110000, - "key_as_string": "2017-01-27T20:48:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03133333333333333 - }, - "doc_count": 1074, - "key": 1485550140000, - "key_as_string": "2017-01-27T20:49:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1074, - "key": 1485550170000, - "key_as_string": "2017-01-27T20:49:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03133333333333333 - }, - "doc_count": 1073, - "key": 1485550200000, - "key_as_string": "2017-01-27T20:50:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1077, - "key": 1485550230000, - "key_as_string": "2017-01-27T20:50:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03166666666666667 - }, - "doc_count": 1074, - "key": 1485550260000, - "key_as_string": "2017-01-27T20:51:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.031 - }, - "doc_count": 1074, - "key": 1485550290000, - "key_as_string": "2017-01-27T20:51:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1072, - "key": 1485550320000, - "key_as_string": "2017-01-27T20:52:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.033 - }, - "doc_count": 1073, - "key": 1485550350000, - "key_as_string": "2017-01-27T20:52:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03466666666666667 - }, - "doc_count": 1071, - "key": 1485550380000, - "key_as_string": "2017-01-27T20:53:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1071, - "key": 1485550410000, - "key_as_string": "2017-01-27T20:53:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03166666666666667 - }, - "doc_count": 1069, - "key": 1485550440000, - "key_as_string": "2017-01-27T20:54:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1069, - "key": 1485550470000, - "key_as_string": "2017-01-27T20:54:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.032 - }, - "doc_count": 1068, - "key": 1485550500000, - "key_as_string": "2017-01-27T20:55:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1067, - "key": 1485550530000, - "key_as_string": "2017-01-27T20:55:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03233333333333333 - }, - "doc_count": 1065, - "key": 1485550560000, - "key_as_string": "2017-01-27T20:56:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1069, - "key": 1485550590000, - "key_as_string": "2017-01-27T20:56:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03166666666666667 - }, - "doc_count": 1068, - "key": 1485550620000, - "key_as_string": "2017-01-27T20:57:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03333333333333333 - }, - "doc_count": 1068, - "key": 1485550650000, - "key_as_string": "2017-01-27T20:57:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1068, - "key": 1485550680000, - "key_as_string": "2017-01-27T20:58:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1071, - "key": 1485550710000, - "key_as_string": "2017-01-27T20:58:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03333333333333333 - }, - "doc_count": 1074, - "key": 1485550740000, - "key_as_string": "2017-01-27T20:59:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1074, - "key": 1485550770000, - "key_as_string": "2017-01-27T20:59:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04 - }, - "doc_count": 1074, - "key": 1485550800000, - "key_as_string": "2017-01-27T21:00:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.032 - }, - "doc_count": 1076, - "key": 1485550830000, - "key_as_string": "2017-01-27T21:00:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1078, - "key": 1485550860000, - "key_as_string": "2017-01-27T21:01:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1077, - "key": 1485550890000, - "key_as_string": "2017-01-27T21:01:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03466666666666667 - }, - "doc_count": 1071, - "key": 1485550920000, - "key_as_string": "2017-01-27T21:02:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03466666666666667 - }, - "doc_count": 1071, - "key": 1485550950000, - "key_as_string": "2017-01-27T21:02:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03566666666666667 - }, - "doc_count": 1073, - "key": 1485550980000, - "key_as_string": "2017-01-27T21:03:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1071, - "key": 1485551010000, - "key_as_string": "2017-01-27T21:03:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03466666666666667 - }, - "doc_count": 1069, - "key": 1485551040000, - "key_as_string": "2017-01-27T21:04:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03566666666666667 - }, - "doc_count": 1068, - "key": 1485551070000, - "key_as_string": "2017-01-27T21:04:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1075, - "key": 1485551100000, - "key_as_string": "2017-01-27T21:05:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1074, - "key": 1485551130000, - "key_as_string": "2017-01-27T21:05:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03133333333333333 - }, - "doc_count": 1073, - "key": 1485551160000, - "key_as_string": "2017-01-27T21:06:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1071, - "key": 1485551190000, - "key_as_string": "2017-01-27T21:06:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03333333333333333 - }, - "doc_count": 1075, - "key": 1485551220000, - "key_as_string": "2017-01-27T21:07:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03133333333333333 - }, - "doc_count": 1071, - "key": 1485551250000, - "key_as_string": "2017-01-27T21:07:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04733333333333334 - }, - "doc_count": 1081, - "key": 1485551280000, - "key_as_string": "2017-01-27T21:08:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.044333333333333336 - }, - "doc_count": 1078, - "key": 1485551310000, - "key_as_string": "2017-01-27T21:08:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037000000000000005 - }, - "doc_count": 1079, - "key": 1485551340000, - "key_as_string": "2017-01-27T21:09:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1077, - "key": 1485551370000, - "key_as_string": "2017-01-27T21:09:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03866666666666666 - }, - "doc_count": 1077, - "key": 1485551400000, - "key_as_string": "2017-01-27T21:10:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037666666666666675 - }, - "doc_count": 1075, - "key": 1485551430000, - "key_as_string": "2017-01-27T21:10:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.038000000000000006 - }, - "doc_count": 1078, - "key": 1485551460000, - "key_as_string": "2017-01-27T21:11:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037 - }, - "doc_count": 1074, - "key": 1485551490000, - "key_as_string": "2017-01-27T21:11:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.036666666666666674 - }, - "doc_count": 1074, - "key": 1485551520000, - "key_as_string": "2017-01-27T21:12:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037333333333333336 - }, - "doc_count": 1076, - "key": 1485551550000, - "key_as_string": "2017-01-27T21:12:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03733333333333333 - }, - "doc_count": 1075, - "key": 1485551580000, - "key_as_string": "2017-01-27T21:13:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04533333333333334 - }, - "doc_count": 1077, - "key": 1485551610000, - "key_as_string": "2017-01-27T21:13:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.039 - }, - "doc_count": 1080, - "key": 1485551640000, - "key_as_string": "2017-01-27T21:14:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.057666666666666665 - }, - "doc_count": 1080, - "key": 1485551670000, - "key_as_string": "2017-01-27T21:14:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.045000000000000005 - }, - "doc_count": 1080, - "key": 1485551700000, - "key_as_string": "2017-01-27T21:15:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037666666666666675 - }, - "doc_count": 1080, - "key": 1485551730000, - "key_as_string": "2017-01-27T21:15:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1080, - "key": 1485551760000, - "key_as_string": "2017-01-27T21:16:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.038 - }, - "doc_count": 1080, - "key": 1485551790000, - "key_as_string": "2017-01-27T21:16:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1080, - "key": 1485551820000, - "key_as_string": "2017-01-27T21:17:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04966666666666667 - }, - "doc_count": 1080, - "key": 1485551850000, - "key_as_string": "2017-01-27T21:17:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03266666666666667 - }, - "doc_count": 1080, - "key": 1485551880000, - "key_as_string": "2017-01-27T21:18:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04 - }, - "doc_count": 1080, - "key": 1485551910000, - "key_as_string": "2017-01-27T21:18:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03766666666666666 - }, - "doc_count": 1080, - "key": 1485551940000, - "key_as_string": "2017-01-27T21:19:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034 - }, - "doc_count": 1076, - "key": 1485551970000, - "key_as_string": "2017-01-27T21:19:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.034333333333333334 - }, - "doc_count": 1077, - "key": 1485552000000, - "key_as_string": "2017-01-27T21:20:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03366666666666667 - }, - "doc_count": 1077, - "key": 1485552030000, - "key_as_string": "2017-01-27T21:20:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.029666666666666664 - }, - "doc_count": 1077, - "key": 1485552060000, - "key_as_string": "2017-01-27T21:21:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.02766666666666667 - }, - "doc_count": 1077, - "key": 1485552090000, - "key_as_string": "2017-01-27T21:21:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.028 - }, - "doc_count": 1077, - "key": 1485552120000, - "key_as_string": "2017-01-27T21:22:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.030666666666666665 - }, - "doc_count": 1077, - "key": 1485552150000, - "key_as_string": "2017-01-27T21:22:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03833333333333334 - }, - "doc_count": 1083, - "key": 1485552180000, - "key_as_string": "2017-01-27T21:23:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04966666666666667 - }, - "doc_count": 1083, - "key": 1485552210000, - "key_as_string": "2017-01-27T21:23:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.041 - }, - "doc_count": 1082, - "key": 1485552240000, - "key_as_string": "2017-01-27T21:24:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037333333333333336 - }, - "doc_count": 1087, - "key": 1485552270000, - "key_as_string": "2017-01-27T21:24:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.039 - }, - "doc_count": 1083, - "key": 1485552300000, - "key_as_string": "2017-01-27T21:25:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03933333333333333 - }, - "doc_count": 1083, - "key": 1485552330000, - "key_as_string": "2017-01-27T21:25:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.037666666666666675 - }, - "doc_count": 1083, - "key": 1485552360000, - "key_as_string": "2017-01-27T21:26:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04033333333333333 - }, - "doc_count": 1083, - "key": 1485552390000, - "key_as_string": "2017-01-27T21:26:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03866666666666666 - }, - "doc_count": 1082, - "key": 1485552420000, - "key_as_string": "2017-01-27T21:27:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04033333333333334 - }, - "doc_count": 1083, - "key": 1485552450000, - "key_as_string": "2017-01-27T21:27:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04 - }, - "doc_count": 1083, - "key": 1485552480000, - "key_as_string": "2017-01-27T21:28:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04033333333333333 - }, - "doc_count": 1084, - "key": 1485552510000, - "key_as_string": "2017-01-27T21:28:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03333333333333333 - }, - "doc_count": 1083, - "key": 1485552540000, - "key_as_string": "2017-01-27T21:29:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03566666666666667 - }, - "doc_count": 1083, - "key": 1485552570000, - "key_as_string": "2017-01-27T21:29:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.04466666666666667 - }, - "doc_count": 1083, - "key": 1485552600000, - "key_as_string": "2017-01-27T21:30:00.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.03233333333333333 - }, - "doc_count": 1083, - "key": 1485552630000, - "key_as_string": "2017-01-27T21:30:30.000Z" - }, - { - "c9b5f9c1-e403-11e6-be91-6f7688e9fac7": { - "value": 0.0505 - }, - "doc_count": 722, - "key": 1485552660000, - "key_as_string": "2017-01-27T21:31:00.000Z" - } - ] - } - } - }, - "hits": { - "hits": [], - "max_score": 0, - "total": 128145 - }, - "status": 200, - "timed_out": false, - "took": 28 -} - diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/bucket_transform.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/bucket_transform.js deleted file mode 100644 index 1414435017c86..0000000000000 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/bucket_transform.js +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { expect } from 'chai'; -import { bucketTransform } from '../../helpers/bucket_transform'; - -describe('bucketTransform', () => { - describe('count', () => { - it('returns count agg', () => { - const metric = { id: 'test', type: 'count' }; - const fn = bucketTransform.count; - expect(fn(metric)).to.eql({ - bucket_script: { - buckets_path: { count: '_count' }, - script: { source: 'count * 1', lang: 'expression' }, - gap_policy: 'skip', - }, - }); - }); - }); - - describe('std metric', () => { - ['avg', 'max', 'min', 'sum', 'cardinality', 'value_count'].forEach(type => { - it(`returns ${type} agg`, () => { - const metric = { id: 'test', type: type, field: 'cpu.pct' }; - const fn = bucketTransform[type]; - const result = {}; - result[type] = { field: 'cpu.pct' }; - expect(fn(metric)).to.eql(result); - }); - }); - - it('throws error if type is missing', () => { - const run = () => bucketTransform.avg({ id: 'test', field: 'cpu.pct' }); - expect(run).to.throw(Error, 'Metric missing type'); - }); - - it('throws error if field is missing', () => { - const run = () => bucketTransform.avg({ id: 'test', type: 'avg' }); - expect(run).to.throw(Error, 'Metric missing field'); - }); - }); - - describe('extended stats', () => { - ['std_deviation', 'variance', 'sum_of_squares'].forEach(type => { - it(`returns ${type} agg`, () => { - const fn = bucketTransform[type]; - const metric = { id: 'test', type: type, field: 'cpu.pct' }; - expect(fn(metric)).to.eql({ extended_stats: { field: 'cpu.pct' } }); - }); - }); - - it('returns std_deviation agg with sigma', () => { - const fn = bucketTransform.std_deviation; - const metric = { - id: 'test', - type: 'std_deviation', - field: 'cpu.pct', - sigma: 2, - }; - expect(fn(metric)).to.eql({ - extended_stats: { field: 'cpu.pct', sigma: 2 }, - }); - }); - - it('throws error if type is missing', () => { - const run = () => bucketTransform.std_deviation({ id: 'test', field: 'cpu.pct' }); - expect(run).to.throw(Error, 'Metric missing type'); - }); - - it('throws error if field is missing', () => { - const run = () => bucketTransform.std_deviation({ id: 'test', type: 'avg' }); - expect(run).to.throw(Error, 'Metric missing field'); - }); - }); - - describe('percentiles', () => { - it('returns percentiles agg', () => { - const metric = { - id: 'test', - type: 'percentile', - field: 'cpu.pct', - percentiles: [ - { value: 50, mode: 'line' }, - { value: 10, mode: 'band', percentile: 90 }, - ], - }; - const fn = bucketTransform.percentile; - expect(fn(metric)).to.eql({ - percentiles: { - field: 'cpu.pct', - percents: [50, 10, 90], - }, - }); - }); - - it('define a default 0 value if it was not provided', () => { - const metric = { - id: 'test', - type: 'percentile', - field: 'cpu.pct', - percentiles: [ - { value: 50, mode: 'line' }, - { mode: 'line' }, - { value: undefined, mode: 'line' }, - { value: '', mode: 'line' }, - { value: null, mode: 'line' }, - { value: 0, mode: 'line' }, - ], - }; - expect(bucketTransform.percentile(metric)).to.eql({ - percentiles: { - field: 'cpu.pct', - percents: [50, 0, 0, 0, 0, 0], - }, - }); - }); - - it('throws error if type is missing', () => { - const run = () => - bucketTransform.percentile({ - id: 'test', - field: 'cpu.pct', - percentiles: [{ value: 50, mode: 'line' }], - }); - expect(run).to.throw(Error, 'Metric missing type'); - }); - - it('throws error if field is missing', () => { - const run = () => - bucketTransform.percentile({ - id: 'test', - type: 'avg', - percentiles: [{ value: 50, mode: 'line' }], - }); - expect(run).to.throw(Error, 'Metric missing field'); - }); - - it('throws error if percentiles is missing', () => { - const run = () => - bucketTransform.percentile({ - id: 'test', - type: 'avg', - field: 'cpu.pct', - }); - expect(run).to.throw(Error, 'Metric missing percentiles'); - }); - }); - - describe('derivative', () => { - it('returns derivative agg with defaults', () => { - const metric = { - id: '2', - type: 'derivative', - field: '1', - }; - const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; - const fn = bucketTransform.derivative; - expect(fn(metric, metrics, '10s')).is.eql({ - derivative: { - buckets_path: '1', - gap_policy: 'skip', - unit: '10s', - }, - }); - }); - - it('returns derivative agg with unit', () => { - const metric = { - id: '2', - type: 'derivative', - field: '1', - unit: '1s', - }; - const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; - const fn = bucketTransform.derivative; - expect(fn(metric, metrics, '10s')).is.eql({ - derivative: { - buckets_path: '1', - gap_policy: 'skip', - unit: '1s', - }, - }); - }); - - it('returns derivative agg with gap_policy', () => { - const metric = { - id: '2', - type: 'derivative', - field: '1', - gap_policy: 'zero_fill', - }; - const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; - const fn = bucketTransform.derivative; - expect(fn(metric, metrics, '10s')).is.eql({ - derivative: { - buckets_path: '1', - gap_policy: 'zero_fill', - unit: '10s', - }, - }); - }); - - it('throws error if type is missing', () => { - const run = () => bucketTransform.derivative({ id: 'test', field: 'cpu.pct' }); - expect(run).to.throw(Error, 'Metric missing type'); - }); - - it('throws error if field is missing', () => { - const run = () => bucketTransform.derivative({ id: 'test', type: 'derivative' }); - expect(run).to.throw(Error, 'Metric missing field'); - }); - }); - - describe('serial_diff', () => { - it('returns serial_diff agg with defaults', () => { - const metric = { - id: '2', - type: 'serial_diff', - field: '1', - }; - const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; - const fn = bucketTransform.serial_diff; - expect(fn(metric, metrics)).is.eql({ - serial_diff: { - buckets_path: '1', - gap_policy: 'skip', - lag: 1, - }, - }); - }); - - it('returns serial_diff agg with lag', () => { - const metric = { - id: '2', - type: 'serial_diff', - field: '1', - lag: 10, - }; - const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; - const fn = bucketTransform.serial_diff; - expect(fn(metric, metrics)).is.eql({ - serial_diff: { - buckets_path: '1', - gap_policy: 'skip', - lag: 10, - }, - }); - }); - - it('returns serial_diff agg with gap_policy', () => { - const metric = { - id: '2', - type: 'serial_diff', - field: '1', - gap_policy: 'zero_fill', - }; - const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; - const fn = bucketTransform.serial_diff; - expect(fn(metric, metrics)).is.eql({ - serial_diff: { - buckets_path: '1', - gap_policy: 'zero_fill', - lag: 1, - }, - }); - }); - - it('throws error if type is missing', () => { - const run = () => bucketTransform.serial_diff({ id: 'test', field: 'cpu.pct' }); - expect(run).to.throw(Error, 'Metric missing type'); - }); - - it('throws error if field is missing', () => { - const run = () => bucketTransform.serial_diff({ id: 'test', type: 'serial_diff' }); - expect(run).to.throw(Error, 'Metric missing field'); - }); - }); - - describe('cumulative_sum', () => { - it('returns cumulative_sum agg', () => { - const metric = { id: '2', type: 'cumulative_sum', field: '1' }; - const metrics = [{ id: '1', type: 'sum', field: 'cpu.pct' }, metric]; - const fn = bucketTransform.cumulative_sum; - expect(fn(metric, metrics, '10s')).is.eql({ - cumulative_sum: { buckets_path: '1' }, - }); - }); - - it('throws error if type is missing', () => { - const run = () => bucketTransform.cumulative_sum({ id: 'test', field: 'cpu.pct' }); - expect(run).to.throw(Error, 'Metric missing type'); - }); - - it('throws error if field is missing', () => { - const run = () => bucketTransform.cumulative_sum({ id: 'test', type: 'cumulative_sum' }); - expect(run).to.throw(Error, 'Metric missing field'); - }); - }); - - describe('calculation', () => { - it('returns calculation(bucket_script)', () => { - const metric = { - id: '2', - type: 'calculation', - script: 'params.idle != null ? 1 - params.idle : 0', - variables: [{ name: 'idle', field: '1' }], - }; - const metrics = [{ id: '1', type: 'avg', field: 'cpu.idle.pct' }, metric]; - const fn = bucketTransform.calculation; - expect(fn(metric, metrics, '10s')).is.eql({ - bucket_script: { - buckets_path: { - idle: '1', - }, - gap_policy: 'skip', - script: { - source: 'params.idle != null ? 1 - params.idle : 0', - lang: 'painless', - params: { - _interval: 10000, - }, - }, - }, - }); - }); - - it('throws error if variables is missing', () => { - const run = () => - bucketTransform.calculation({ - id: 'test', - type: 'calculation', - script: 'params.idle != null ? 1 - params.idle : null', - }); - expect(run).to.throw(Error, 'Metric missing variables'); - }); - - it('throws error if script is missing', () => { - const run = () => - bucketTransform.calculation({ - id: 'test', - type: 'calculation', - variables: [{ field: '1', name: 'idle' }], - }); - expect(run).to.throw(Error, 'Metric missing script'); - }); - }); - - describe('positive_only', () => { - it('returns bucket_script', () => { - const metric = { - id: '2', - type: 'positive_only', - field: '1', - }; - const metrics = [{ id: '1', type: 'avg', field: 'cpu.idle.pct' }, metric]; - const fn = bucketTransform.positive_only; - expect(fn(metric, metrics, '10s')).is.eql({ - bucket_script: { - buckets_path: { - value: '1', - }, - gap_policy: 'skip', - script: { - source: 'params.value > 0.0 ? params.value : 0.0', - lang: 'painless', - }, - }, - }); - }); - }); -}); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/build_processor_function.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.test.ts similarity index 58% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/build_processor_function.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.test.ts index 10ad9e467610e..cf5244f8e5a7d 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/build_processor_function.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.test.ts @@ -17,43 +17,41 @@ * under the License. */ -import sinon from 'sinon'; -import { expect } from 'chai'; -import { buildProcessorFunction } from '../build_processor_function'; +import { buildProcessorFunction } from './build_processor_function'; describe('buildProcessorFunction(chain, ...args)', () => { const req = {}; const panel = {}; const series = {}; - it('should call each processor', () => { - const first = sinon.spy(() => next => doc => next(doc)); - const second = sinon.spy(() => next => doc => next(doc)); + test('should call each processor', () => { + const first = jest.fn(() => (next: any) => (doc: any) => next(doc)); + const second = jest.fn(() => (next: any) => (doc: any) => next(doc)); buildProcessorFunction([first, second], req, panel, series); - expect(first.calledOnce).to.equal(true); - expect(second.calledOnce).to.equal(true); + expect(first.mock.calls.length).toEqual(1); + expect(second.mock.calls.length).toEqual(1); }); - it('should chain each processor', () => { - const first = sinon.spy(next => doc => next(doc)); - const second = sinon.spy(next => doc => next(doc)); + test('should chain each processor', () => { + const first = jest.fn(() => (next: any) => (doc: any) => next(doc)); + const second = jest.fn(() => (next: any) => (doc: any) => next(doc)); buildProcessorFunction([() => first, () => second], req, panel, series); - expect(first.calledOnce).to.equal(true); - expect(second.calledOnce).to.equal(true); + expect(first.mock.calls.length).toEqual(1); + expect(second.mock.calls.length).toEqual(1); }); - it('should next of each processor', () => { - const first = sinon.spy(); - const second = sinon.spy(); + test('should next of each processor', () => { + const first = jest.fn(); + const second = jest.fn(); const fn = buildProcessorFunction( [ - () => next => doc => { + () => (next: any) => (doc: any) => { first(); next(doc); }, - () => next => doc => { + () => (next: any) => (doc: any) => { second(); next(doc); }, @@ -63,7 +61,7 @@ describe('buildProcessorFunction(chain, ...args)', () => { series ); fn({}); - expect(first.calledOnce).to.equal(true); - expect(second.calledOnce).to.equal(true); + expect(first.mock.calls.length).toEqual(1); + expect(second.mock.calls.length).toEqual(1); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.ts similarity index 86% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.ts index 5896a089bafe2..b898f4dbf7a5e 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/build_processor_function.ts @@ -17,11 +17,9 @@ * under the License. */ -export function buildProcessorFunction(chain, ...args) { +export function buildProcessorFunction(chain: any[], ...args: any) { return chain.reduceRight( - (next, fn) => { - return fn(...args)(next); - }, - doc => doc + (next, fn) => fn(...args)(next), + (doc: any) => doc ); } diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/get_interval_and_timefield.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.js similarity index 79% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/get_interval_and_timefield.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.js index dc2c2b2cfb1f2..f3e15f2fc65b6 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/get_interval_and_timefield.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/get_interval_and_timefield.test.js @@ -17,27 +17,26 @@ * under the License. */ -import { expect } from 'chai'; -import { getIntervalAndTimefield } from '../get_interval_and_timefield'; +import { getIntervalAndTimefield } from './get_interval_and_timefield'; describe('getIntervalAndTimefield(panel, series)', () => { - it('returns the panel interval and timefield', () => { + test('returns the panel interval and timefield', () => { const panel = { time_field: '@timestamp', interval: 'auto' }; const series = {}; - expect(getIntervalAndTimefield(panel, series)).to.eql({ + expect(getIntervalAndTimefield(panel, series)).toEqual({ timeField: '@timestamp', interval: 'auto', }); }); - it('returns the series interval and timefield', () => { + test('returns the series interval and timefield', () => { const panel = { time_field: '@timestamp', interval: 'auto' }; const series = { override_index_pattern: true, series_interval: '1m', series_time_field: 'time', }; - expect(getIntervalAndTimefield(panel, series)).to.eql({ + expect(getIntervalAndTimefield(panel, series)).toEqual({ timeField: 'time', interval: '1m', }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.test.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.test.js index 3def18997863e..db0e8fa3d6bb9 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.test.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.test.js @@ -94,4 +94,371 @@ describe('src/legacy/core_plugins/metrics/server/lib/vis_data/helpers/bucket_tra }); }); }); + + describe('bucketTransform additional', () => { + describe('count', () => { + test('returns count agg', () => { + const metric = { id: 'test', type: 'count' }; + const fn = bucketTransform.count; + expect(fn(metric)).toEqual({ + bucket_script: { + buckets_path: { count: '_count' }, + script: { source: 'count * 1', lang: 'expression' }, + gap_policy: 'skip', + }, + }); + }); + }); + + describe('std metric', () => { + ['avg', 'max', 'min', 'sum', 'cardinality', 'value_count'].forEach(type => { + test(`returns ${type} agg`, () => { + const metric = { id: 'test', type: type, field: 'cpu.pct' }; + const fn = bucketTransform[type]; + const result = {}; + result[type] = { field: 'cpu.pct' }; + expect(fn(metric)).toEqual(result); + }); + }); + + test('throws error if type is missing', () => { + const run = () => bucketTransform.avg({ id: 'test', field: 'cpu.pct' }); + expect(run).toThrow(new Error('Metric missing type')); + }); + + test('throws error if field is missing', () => { + const run = () => bucketTransform.avg({ id: 'test', type: 'avg' }); + expect(run).toThrow(new Error('Metric missing field')); + }); + }); + + describe('extended stats', () => { + ['std_deviation', 'variance', 'sum_of_squares'].forEach(type => { + test(`returns ${type} agg`, () => { + const fn = bucketTransform[type]; + const metric = { id: 'test', type: type, field: 'cpu.pct' }; + expect(fn(metric)).toEqual({ extended_stats: { field: 'cpu.pct' } }); + }); + }); + + test('returns std_deviation agg with sigma', () => { + const fn = bucketTransform.std_deviation; + const metric = { + id: 'test', + type: 'std_deviation', + field: 'cpu.pct', + sigma: 2, + }; + expect(fn(metric)).toEqual({ + extended_stats: { field: 'cpu.pct', sigma: 2 }, + }); + }); + + test('throws error if type is missing', () => { + const run = () => bucketTransform.std_deviation({ id: 'test', field: 'cpu.pct' }); + expect(run).toThrow(new Error('Metric missing type')); + }); + + test('throws error if field is missing', () => { + const run = () => bucketTransform.std_deviation({ id: 'test', type: 'avg' }); + expect(run).toThrow(new Error('Metric missing field')); + }); + }); + + describe('percentiles', () => { + test('returns percentiles agg', () => { + const metric = { + id: 'test', + type: 'percentile', + field: 'cpu.pct', + percentiles: [ + { value: 50, mode: 'line' }, + { value: 10, mode: 'band', percentile: 90 }, + ], + }; + const fn = bucketTransform.percentile; + expect(fn(metric)).toEqual({ + percentiles: { + field: 'cpu.pct', + percents: [50, 10, 90], + }, + }); + }); + + test('define a default 0 value if it was not provided', () => { + const metric = { + id: 'test', + type: 'percentile', + field: 'cpu.pct', + percentiles: [ + { value: 50, mode: 'line' }, + { mode: 'line' }, + { value: undefined, mode: 'line' }, + { value: '', mode: 'line' }, + { value: null, mode: 'line' }, + { value: 0, mode: 'line' }, + ], + }; + expect(bucketTransform.percentile(metric)).toEqual({ + percentiles: { + field: 'cpu.pct', + percents: [50, 0, 0, 0, 0, 0], + }, + }); + }); + + test('throws error if type is missing', () => { + const run = () => + bucketTransform.percentile({ + id: 'test', + field: 'cpu.pct', + percentiles: [{ value: 50, mode: 'line' }], + }); + expect(run).toThrow(new Error('Metric missing type')); + }); + + test('throws error if field is missing', () => { + const run = () => + bucketTransform.percentile({ + id: 'test', + type: 'avg', + percentiles: [{ value: 50, mode: 'line' }], + }); + expect(run).toThrow(new Error('Metric missing field')); + }); + + test('throws error if percentiles is missing', () => { + const run = () => + bucketTransform.percentile({ + id: 'test', + type: 'avg', + field: 'cpu.pct', + }); + expect(run).toThrow(new Error('Metric missing percentiles')); + }); + }); + + describe('derivative', () => { + test('returns derivative agg with defaults', () => { + const metric = { + id: '2', + type: 'derivative', + field: '1', + }; + const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; + const fn = bucketTransform.derivative; + expect(fn(metric, metrics, '10s')).toEqual({ + derivative: { + buckets_path: '1', + gap_policy: 'skip', + unit: '10s', + }, + }); + }); + + test('returns derivative agg with unit', () => { + const metric = { + id: '2', + type: 'derivative', + field: '1', + unit: '1s', + }; + const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; + const fn = bucketTransform.derivative; + expect(fn(metric, metrics, '10s')).toEqual({ + derivative: { + buckets_path: '1', + gap_policy: 'skip', + unit: '1s', + }, + }); + }); + + test('returns derivative agg with gap_policy', () => { + const metric = { + id: '2', + type: 'derivative', + field: '1', + gap_policy: 'zero_fill', + }; + const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; + const fn = bucketTransform.derivative; + expect(fn(metric, metrics, '10s')).toEqual({ + derivative: { + buckets_path: '1', + gap_policy: 'zero_fill', + unit: '10s', + }, + }); + }); + + test('throws error if type is missing', () => { + const run = () => bucketTransform.derivative({ id: 'test', field: 'cpu.pct' }); + expect(run).toThrow(new Error('Metric missing type')); + }); + + test('throws error if field is missing', () => { + const run = () => bucketTransform.derivative({ id: 'test', type: 'derivative' }); + expect(run).toThrow(new Error('Metric missing field')); + }); + }); + + describe('serial_diff', () => { + test('returns serial_diff agg with defaults', () => { + const metric = { + id: '2', + type: 'serial_diff', + field: '1', + }; + const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; + const fn = bucketTransform.serial_diff; + expect(fn(metric, metrics)).toEqual({ + serial_diff: { + buckets_path: '1', + gap_policy: 'skip', + lag: 1, + }, + }); + }); + + test('returns serial_diff agg with lag', () => { + const metric = { + id: '2', + type: 'serial_diff', + field: '1', + lag: 10, + }; + const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; + const fn = bucketTransform.serial_diff; + expect(fn(metric, metrics)).toEqual({ + serial_diff: { + buckets_path: '1', + gap_policy: 'skip', + lag: 10, + }, + }); + }); + + test('returns serial_diff agg with gap_policy', () => { + const metric = { + id: '2', + type: 'serial_diff', + field: '1', + gap_policy: 'zero_fill', + }; + const metrics = [{ id: '1', type: 'max', field: 'cpu.pct' }, metric]; + const fn = bucketTransform.serial_diff; + expect(fn(metric, metrics)).toEqual({ + serial_diff: { + buckets_path: '1', + gap_policy: 'zero_fill', + lag: 1, + }, + }); + }); + + test('throws error if type is missing', () => { + const run = () => bucketTransform.serial_diff({ id: 'test', field: 'cpu.pct' }); + expect(run).toThrow(new Error('Metric missing type')); + }); + + test('throws error if field is missing', () => { + const run = () => bucketTransform.serial_diff({ id: 'test', type: 'serial_diff' }); + expect(run).toThrow(new Error('Metric missing field')); + }); + }); + + describe('cumulative_sum', () => { + test('returns cumulative_sum agg', () => { + const metric = { id: '2', type: 'cumulative_sum', field: '1' }; + const metrics = [{ id: '1', type: 'sum', field: 'cpu.pct' }, metric]; + const fn = bucketTransform.cumulative_sum; + expect(fn(metric, metrics, '10s')).toEqual({ + cumulative_sum: { buckets_path: '1' }, + }); + }); + + test('throws error if type is missing', () => { + const run = () => bucketTransform.cumulative_sum({ id: 'test', field: 'cpu.pct' }); + expect(run).toThrow(new Error('Metric missing type')); + }); + + test('throws error if field is missing', () => { + const run = () => bucketTransform.cumulative_sum({ id: 'test', type: 'cumulative_sum' }); + expect(run).toThrow(new Error('Metric missing field')); + }); + }); + + describe('calculation', () => { + test('returns calculation(bucket_script)', () => { + const metric = { + id: '2', + type: 'calculation', + script: 'params.idle != null ? 1 - params.idle : 0', + variables: [{ name: 'idle', field: '1' }], + }; + const metrics = [{ id: '1', type: 'avg', field: 'cpu.idle.pct' }, metric]; + const fn = bucketTransform.calculation; + expect(fn(metric, metrics, '10s')).toEqual({ + bucket_script: { + buckets_path: { + idle: '1', + }, + gap_policy: 'skip', + script: { + source: 'params.idle != null ? 1 - params.idle : 0', + lang: 'painless', + params: { + _interval: 10000, + }, + }, + }, + }); + }); + + test('throws error if variables is missing', () => { + const run = () => + bucketTransform.calculation({ + id: 'test', + type: 'calculation', + script: 'params.idle != null ? 1 - params.idle : null', + }); + expect(run).toThrow(new Error('Metric missing variables')); + }); + + test('throws error if script is missing', () => { + const run = () => + bucketTransform.calculation({ + id: 'test', + type: 'calculation', + variables: [{ field: '1', name: 'idle' }], + }); + expect(run).toThrow(new Error('Metric missing script')); + }); + }); + + describe('positive_only', () => { + test('returns bucket_script', () => { + const metric = { + id: '2', + type: 'positive_only', + field: '1', + }; + const metrics = [{ id: '1', type: 'avg', field: 'cpu.idle.pct' }, metric]; + const fn = bucketTransform.positive_only; + expect(fn(metric, metrics, '10s')).toEqual({ + bucket_script: { + buckets_path: { + value: '1', + }, + gap_policy: 'skip', + script: { + source: 'params.value > 0.0 ? params.value : 0.0', + lang: 'painless', + }, + }, + }); + }); + }); + }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_agg_value.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js similarity index 95% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_agg_value.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js index 234953c8f4617..5f5e5ebafa560 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_agg_value.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_agg_value.test.js @@ -17,16 +17,15 @@ * under the License. */ -import { expect } from 'chai'; -import { getAggValue } from '../../helpers/get_agg_value'; +import { getAggValue } from './get_agg_value'; function testAgg(row, metric, expected) { let name = metric.type; if (metric.mode) name += `(${metric.mode})`; if (metric.percent) name += `(${metric.percent})`; - it(`it should return ${name}(${expected})`, () => { + test(`it should return ${name}(${expected})`, () => { const value = getAggValue(row, metric); - expect(value).to.eql(expected); + expect(value).toEqual(expected); }); } diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_bucket_size.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.test.js similarity index 54% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_bucket_size.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.test.js index e62cfff1083cf..99bef2de6b72d 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_bucket_size.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_bucket_size.test.js @@ -17,8 +17,7 @@ * under the License. */ -import { expect } from 'chai'; -import { getBucketSize } from '../../helpers/get_bucket_size'; +import { getBucketSize } from './get_bucket_size'; describe('getBucketSize', () => { const req = { @@ -30,39 +29,39 @@ describe('getBucketSize', () => { }, }; - it('returns auto calculated buckets', () => { + test('returns auto calculated buckets', () => { const result = getBucketSize(req, 'auto'); - expect(result).to.have.property('bucketSize', 30); - expect(result).to.have.property('intervalString', '30s'); + expect(result).toHaveProperty('bucketSize', 30); + expect(result).toHaveProperty('intervalString', '30s'); }); - it('returns overridden buckets (1s)', () => { + test('returns overridden buckets (1s)', () => { const result = getBucketSize(req, '1s'); - expect(result).to.have.property('bucketSize', 1); - expect(result).to.have.property('intervalString', '1s'); + expect(result).toHaveProperty('bucketSize', 1); + expect(result).toHaveProperty('intervalString', '1s'); }); - it('returns overridden buckets (10m)', () => { + test('returns overridden buckets (10m)', () => { const result = getBucketSize(req, '10m'); - expect(result).to.have.property('bucketSize', 600); - expect(result).to.have.property('intervalString', '10m'); + expect(result).toHaveProperty('bucketSize', 600); + expect(result).toHaveProperty('intervalString', '10m'); }); - it('returns overridden buckets (1d)', () => { + test('returns overridden buckets (1d)', () => { const result = getBucketSize(req, '1d'); - expect(result).to.have.property('bucketSize', 86400); - expect(result).to.have.property('intervalString', '1d'); + expect(result).toHaveProperty('bucketSize', 86400); + expect(result).toHaveProperty('intervalString', '1d'); }); - it('returns overridden buckets (>=2d)', () => { + test('returns overridden buckets (>=2d)', () => { const result = getBucketSize(req, '>=2d'); - expect(result).to.have.property('bucketSize', 86400 * 2); - expect(result).to.have.property('intervalString', '2d'); + expect(result).toHaveProperty('bucketSize', 86400 * 2); + expect(result).toHaveProperty('intervalString', '2d'); }); - it('returns overridden buckets (>=10s)', () => { + test('returns overridden buckets (>=10s)', () => { const result = getBucketSize(req, '>=10s'); - expect(result).to.have.property('bucketSize', 30); - expect(result).to.have.property('intervalString', '30s'); + expect(result).toHaveProperty('bucketSize', 30); + expect(result).toHaveProperty('intervalString', '30s'); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_buckets_path.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.test.js similarity index 53% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_buckets_path.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.test.js index f5c31fa8ba7ce..0a0e18a696588 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_buckets_path.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_buckets_path.test.js @@ -17,8 +17,7 @@ * under the License. */ -import { expect } from 'chai'; -import { getBucketsPath } from '../../helpers/get_buckets_path'; +import { getBucketsPath } from './get_buckets_path'; describe('getBucketsPath', () => { const metrics = [ @@ -33,43 +32,43 @@ describe('getBucketsPath', () => { { id: 9, type: 'max' }, ]; - it('return path for derivative', () => { - expect(getBucketsPath(1, metrics)).to.equal('1[normalized_value]'); + test('return path for derivative', () => { + expect(getBucketsPath(1, metrics)).toEqual('1[normalized_value]'); }); - it('return path for percentile(50)', () => { - expect(getBucketsPath(2, metrics)).to.equal('2[50.0]'); + test('return path for percentile(50)', () => { + expect(getBucketsPath(2, metrics)).toEqual('2[50.0]'); }); - it('return path for percentile(20.0)', () => { - expect(getBucketsPath(3, metrics)).to.equal('3[20.0]'); + test('return path for percentile(20.0)', () => { + expect(getBucketsPath(3, metrics)).toEqual('3[20.0]'); }); - it('return path for percentile(10.0) with alt id', () => { - expect(getBucketsPath('3[10.0]', metrics)).to.equal('3[10.0]'); + test('return path for percentile(10.0) with alt id', () => { + expect(getBucketsPath('3[10.0]', metrics)).toEqual('3[10.0]'); }); - it('return path for std_deviation(raw)', () => { - expect(getBucketsPath(4, metrics)).to.equal('4[std_deviation]'); + test('return path for std_deviation(raw)', () => { + expect(getBucketsPath(4, metrics)).toEqual('4[std_deviation]'); }); - it('return path for std_deviation(upper)', () => { - expect(getBucketsPath(5, metrics)).to.equal('5[std_upper]'); + test('return path for std_deviation(upper)', () => { + expect(getBucketsPath(5, metrics)).toEqual('5[std_upper]'); }); - it('return path for std_deviation(lower)', () => { - expect(getBucketsPath(6, metrics)).to.equal('6[std_lower]'); + test('return path for std_deviation(lower)', () => { + expect(getBucketsPath(6, metrics)).toEqual('6[std_lower]'); }); - it('return path for sum_of_squares', () => { - expect(getBucketsPath(7, metrics)).to.equal('7[sum_of_squares]'); + test('return path for sum_of_squares', () => { + expect(getBucketsPath(7, metrics)).toEqual('7[sum_of_squares]'); }); - it('return path for variance', () => { - expect(getBucketsPath(8, metrics)).to.equal('8[variance]'); + test('return path for variance', () => { + expect(getBucketsPath(8, metrics)).toEqual('8[variance]'); }); - it('return path for basic metric', () => { - expect(getBucketsPath(9, metrics)).to.equal('9'); + test('return path for basic metric', () => { + expect(getBucketsPath(9, metrics)).toEqual('9'); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_default_decoration.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_default_decoration.test.js similarity index 51% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_default_decoration.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_default_decoration.test.js index 5cc94dda6d21a..2529c8c649485 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_default_decoration.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_default_decoration.test.js @@ -17,44 +17,43 @@ * under the License. */ -import { expect } from 'chai'; -import { getDefaultDecoration } from '../../helpers/get_default_decoration'; +import { getDefaultDecoration } from './get_default_decoration'; describe('getDefaultDecoration', () => { describe('stack option', () => { - it('should set a stack option to none', () => { + test('should set a stack option to none', () => { const series = { id: 'test_id', stacked: 'none', }; - expect(getDefaultDecoration(series)).to.have.property('stack', 'none'); + expect(getDefaultDecoration(series)).toHaveProperty('stack', 'none'); }); - it('should set a stack option to stacked/percent', () => { + test('should set a stack option to stacked/percent', () => { const series = { stacked: 'stacked', id: 'test_id', }; - expect(getDefaultDecoration(series)).to.have.property('stack', 'stacked'); + expect(getDefaultDecoration(series)).toHaveProperty('stack', 'stacked'); series.stacked = 'percent'; - expect(getDefaultDecoration(series)).to.have.property('stack', 'percent'); + expect(getDefaultDecoration(series)).toHaveProperty('stack', 'percent'); }); - it('should set a stack option to stacked_within_series', () => { + test('should set a stack option to stacked_within_series', () => { const series = { stacked: 'stacked_within_series', id: 'test_id', }; - expect(getDefaultDecoration(series)).to.have.property('stack', 'stacked_within_series'); + expect(getDefaultDecoration(series)).toHaveProperty('stack', 'stacked_within_series'); }); }); describe('lines', () => { - it('return decoration for lines', () => { + test('return decoration for lines', () => { const series = { point_size: 10, chart_type: 'line', @@ -62,29 +61,29 @@ describe('getDefaultDecoration', () => { fill: 1, }; const result = getDefaultDecoration(series); - expect(result.lines).to.have.property('show', true); - expect(result.lines).to.have.property('fill', 1); - expect(result.lines).to.have.property('lineWidth', 10); - expect(result.points).to.have.property('show', true); - expect(result.points).to.have.property('radius', 1); - expect(result.points).to.have.property('lineWidth', 10); - expect(result.bars).to.have.property('show', false); - expect(result.bars).to.have.property('fill', 1); - expect(result.bars).to.have.property('lineWidth', 10); + expect(result.lines).toHaveProperty('show', true); + expect(result.lines).toHaveProperty('fill', 1); + expect(result.lines).toHaveProperty('lineWidth', 10); + expect(result.points).toHaveProperty('show', true); + expect(result.points).toHaveProperty('radius', 1); + expect(result.points).toHaveProperty('lineWidth', 10); + expect(result.bars).toHaveProperty('show', false); + expect(result.bars).toHaveProperty('fill', 1); + expect(result.bars).toHaveProperty('lineWidth', 10); }); - it('return decoration for lines without points', () => { + test('return decoration for lines without points', () => { const series = { chart_type: 'line', line_width: 10, fill: 1, }; const result = getDefaultDecoration(series); - expect(result.points).to.have.property('show', true); - expect(result.points).to.have.property('lineWidth', 10); + expect(result.points).toHaveProperty('show', true); + expect(result.points).toHaveProperty('lineWidth', 10); }); - it('return decoration for lines with points set to zero (off)', () => { + test('return decoration for lines with points set to zero (off)', () => { const series = { chart_type: 'line', line_width: 10, @@ -92,32 +91,32 @@ describe('getDefaultDecoration', () => { point_size: 0, }; const result = getDefaultDecoration(series); - expect(result.points).to.have.property('show', false); + expect(result.points).toHaveProperty('show', false); }); - it('return decoration for lines (off)', () => { + test('return decoration for lines (off)', () => { const series = { chart_type: 'line', line_width: 0, }; const result = getDefaultDecoration(series); - expect(result.lines).to.have.property('show', false); + expect(result.lines).toHaveProperty('show', false); }); }); describe('bars', () => { - it('return decoration for bars', () => { + test('return decoration for bars', () => { const series = { chart_type: 'bar', line_width: 10, fill: 1, }; const result = getDefaultDecoration(series); - expect(result.lines).to.have.property('show', false); - expect(result.points).to.have.property('show', false); - expect(result.bars).to.have.property('show', true); - expect(result.bars).to.have.property('fill', 1); - expect(result.bars).to.have.property('lineWidth', 10); + expect(result.lines).toHaveProperty('show', false); + expect(result.points).toHaveProperty('show', false); + expect(result.bars).toHaveProperty('show', true); + expect(result.bars).toHaveProperty('fill', 1); + expect(result.bars).toHaveProperty('lineWidth', 10); }); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_es_shard_timeout.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_es_shard_timeout.test.js similarity index 82% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_es_shard_timeout.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_es_shard_timeout.test.js index b19f6a3241597..13f62739a5485 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_es_shard_timeout.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_es_shard_timeout.test.js @@ -17,11 +17,10 @@ * under the License. */ -import { expect } from 'chai'; -import { getEsShardTimeout } from '../../helpers/get_es_shard_timeout'; +import { getEsShardTimeout } from './get_es_shard_timeout'; describe('getEsShardTimeout', () => { - it('should return the elasticsearch.shardTimeout', async () => { + test('should return the elasticsearch.shardTimeout', async () => { const req = { getEsShardTimeout: async () => { return 12345; @@ -30,6 +29,6 @@ describe('getEsShardTimeout', () => { const timeout = await getEsShardTimeout(req); - expect(timeout).to.equal(12345); + expect(timeout).toEqual(12345); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_last_metric.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_last_metric.test.js similarity index 76% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_last_metric.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_last_metric.test.js index be99f81dfccc4..42e6cc2c01836 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_last_metric.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_last_metric.test.js @@ -17,26 +17,25 @@ * under the License. */ -import { expect } from 'chai'; -import { getLastMetric } from '../../helpers/get_last_metric'; +import { getLastMetric } from './get_last_metric'; describe('getLastMetric(series)', () => { - it('returns the last metric', () => { + test('returns the last metric', () => { const series = { metrics: [ { id: 1, type: 'avg' }, { id: 2, type: 'moving_average' }, ], }; - expect(getLastMetric(series)).to.eql({ id: 2, type: 'moving_average' }); + expect(getLastMetric(series)).toEqual({ id: 2, type: 'moving_average' }); }); - it('returns the last metric that not a series_agg', () => { + test('returns the last metric that not a series_agg', () => { const series = { metrics: [ { id: 1, type: 'avg' }, { id: 2, type: 'series_agg' }, ], }; - expect(getLastMetric(series)).to.eql({ id: 1, type: 'avg' }); + expect(getLastMetric(series)).toEqual({ id: 1, type: 'avg' }); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_sibling_agg_value.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.test.js similarity index 69% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_sibling_agg_value.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.test.js index 68bfa77e25ea3..755568546949c 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_sibling_agg_value.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_sibling_agg_value.test.js @@ -17,8 +17,7 @@ * under the License. */ -import { expect } from 'chai'; -import { getSiblingAggValue } from '../../helpers/get_sibling_agg_value'; +import { getSiblingAggValue } from './get_sibling_agg_value'; describe('getSiblingAggValue', () => { const row = { @@ -32,23 +31,23 @@ describe('getSiblingAggValue', () => { }, }; - it('returns the value for std_deviation_bounds.upper', () => { + test('returns the value for std_deviation_bounds.upper', () => { const metric = { id: 'test', type: 'std_deviation_bucket', mode: 'upper' }; - expect(getSiblingAggValue(row, metric)).to.equal(2); + expect(getSiblingAggValue(row, metric)).toEqual(2); }); - it('returns the value for std_deviation_bounds.lower', () => { + test('returns the value for std_deviation_bounds.lower', () => { const metric = { id: 'test', type: 'std_deviation_bucket', mode: 'lower' }; - expect(getSiblingAggValue(row, metric)).to.equal(1); + expect(getSiblingAggValue(row, metric)).toEqual(1); }); - it('returns the value for std_deviation', () => { + test('returns the value for std_deviation', () => { const metric = { id: 'test', type: 'std_deviation_bucket', mode: 'raw' }; - expect(getSiblingAggValue(row, metric)).to.equal(1.5); + expect(getSiblingAggValue(row, metric)).toEqual(1.5); }); - it('returns the value for basic (max)', () => { + test('returns the value for basic (max)', () => { const metric = { id: 'test', type: 'max_bucket' }; - expect(getSiblingAggValue(row, metric)).to.equal(3); + expect(getSiblingAggValue(row, metric)).toEqual(3); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_splits.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js similarity index 90% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_splits.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js index 1057248d2f362..0874d944033f5 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_splits.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js @@ -17,11 +17,10 @@ * under the License. */ -import { expect } from 'chai'; -import { getSplits } from '../../helpers/get_splits'; +import { getSplits } from './get_splits'; describe('getSplits(resp, panel, series)', () => { - it('should return a splits for everything/filter group bys', () => { + test('should return a splits for everything/filter group bys', () => { const resp = { aggregations: { SERIES: { @@ -41,7 +40,7 @@ describe('getSplits(resp, panel, series)', () => { { id: 'SIBAGG', type: 'avg_bucket', field: 'AVG' }, ], }; - expect(getSplits(resp, panel, series)).to.eql([ + expect(getSplits(resp, panel, series)).toEqual([ { id: 'SERIES', label: 'Overall Average of Average of cpu', @@ -53,7 +52,7 @@ describe('getSplits(resp, panel, series)', () => { ]); }); - it('should return a splits for terms group bys for top_n', () => { + test('should return a splits for terms group bys for top_n', () => { const resp = { aggregations: { SERIES: { @@ -85,7 +84,7 @@ describe('getSplits(resp, panel, series)', () => { ], }; const panel = { type: 'top_n' }; - expect(getSplits(resp, panel, series)).to.eql([ + expect(getSplits(resp, panel, series)).toEqual([ { id: 'SERIES:example-01', key: 'example-01', @@ -107,7 +106,7 @@ describe('getSplits(resp, panel, series)', () => { ]); }); - it('should return a splits for terms group bys', () => { + test('should return a splits for terms group bys', () => { const resp = { aggregations: { SERIES: { @@ -139,7 +138,7 @@ describe('getSplits(resp, panel, series)', () => { ], }; const panel = { type: 'timeseries' }; - expect(getSplits(resp, panel, series)).to.eql([ + expect(getSplits(resp, panel, series)).toEqual([ { id: 'SERIES:example-01', key: 'example-01', @@ -161,7 +160,7 @@ describe('getSplits(resp, panel, series)', () => { ]); }); - it('should return a splits for filters group bys', () => { + test('should return a splits for filters group bys', () => { const resp = { aggregations: { SERIES: { @@ -188,7 +187,7 @@ describe('getSplits(resp, panel, series)', () => { metrics: [{ id: 'COUNT', type: 'count' }], }; const panel = { type: 'timeseries' }; - expect(getSplits(resp, panel, series)).to.eql([ + expect(getSplits(resp, panel, series)).toEqual([ { id: 'SERIES:filter-1', key: 'filter-1', diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_timerange.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange.test.js similarity index 72% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_timerange.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange.test.js index f79d1002b8546..1a1b12c651992 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/get_timerange.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_timerange.test.js @@ -17,12 +17,11 @@ * under the License. */ -import { expect } from 'chai'; -import { getTimerange } from '../../helpers/get_timerange'; +import { getTimerange } from './get_timerange'; import moment from 'moment'; describe('getTimerange(req)', () => { - it('should return a moment object for to and from', () => { + test('should return a moment object for to and from', () => { const req = { payload: { timerange: { @@ -32,9 +31,9 @@ describe('getTimerange(req)', () => { }, }; const { from, to } = getTimerange(req); - expect(moment.isMoment(from)).to.equal(true); - expect(moment.isMoment(to)).to.equal(true); - expect(moment.utc('2017-01-01T00:00:00Z').isSame(from)).to.equal(true); - expect(moment.utc('2017-01-01T01:00:00Z').isSame(to)).to.equal(true); + expect(moment.isMoment(from)).toEqual(true); + expect(moment.isMoment(to)).toEqual(true); + expect(moment.utc('2017-01-01T00:00:00Z').isSame(from)).toEqual(true); + expect(moment.utc('2017-01-01T01:00:00Z').isSame(to)).toEqual(true); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/map_bucket.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_bucket.test.js similarity index 72% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/map_bucket.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_bucket.test.js index e3223ef7b618f..0b007416681ee 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/map_bucket.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/map_bucket.test.js @@ -17,40 +17,39 @@ * under the License. */ -import { mapBucket } from '../../helpers/map_bucket'; -import { expect } from 'chai'; +import { mapBucket } from './map_bucket'; describe('mapBucket(metric)', () => { - it('returns bucket key and value for basic metric', () => { + test('returns bucket key and value for basic metric', () => { const metric = { id: 'AVG', type: 'avg' }; const bucket = { key: 1234, AVG: { value: 1 }, }; - expect(mapBucket(metric)(bucket)).to.eql([1234, 1]); + expect(mapBucket(metric)(bucket)).toEqual([1234, 1]); }); - it('returns bucket key and value for std_deviation', () => { + test('returns bucket key and value for std_deviation', () => { const metric = { id: 'STDDEV', type: 'std_deviation' }; const bucket = { key: 1234, STDDEV: { std_deviation: 1 }, }; - expect(mapBucket(metric)(bucket)).to.eql([1234, 1]); + expect(mapBucket(metric)(bucket)).toEqual([1234, 1]); }); - it('returns bucket key and value for percentiles', () => { + test('returns bucket key and value for percentiles', () => { const metric = { id: 'PCT', type: 'percentile', percent: 50 }; const bucket = { key: 1234, PCT: { values: { '50.0': 1 } }, }; - expect(mapBucket(metric)(bucket)).to.eql([1234, 1]); + expect(mapBucket(metric)(bucket)).toEqual([1234, 1]); }); - it('returns bucket key and value for derivative', () => { + test('returns bucket key and value for derivative', () => { const metric = { id: 'DERV', type: 'derivative', field: 'io', unit: '1s' }; const bucket = { key: 1234, DERV: { value: 100, normalized_value: 1 }, }; - expect(mapBucket(metric)(bucket)).to.eql([1234, 1]); + expect(mapBucket(metric)(bucket)).toEqual([1234, 1]); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/parse_settings.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/parse_settings.test.js similarity index 70% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/parse_settings.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/parse_settings.test.js index bf16b6e22e8ef..eab06cdccfa66 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/helpers/parse_settings.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/helpers/parse_settings.test.js @@ -17,41 +17,40 @@ * under the License. */ -import { expect } from 'chai'; -import { parseSettings } from '../../helpers/parse_settings'; +import { parseSettings } from './parse_settings'; describe('parseSettings', () => { - it('returns the true for "true"', () => { + test('returns the true for "true"', () => { const settings = 'pad=true'; - expect(parseSettings(settings)).to.eql({ + expect(parseSettings(settings)).toEqual({ pad: true, }); }); - it('returns the false for "false"', () => { + test('returns the false for "false"', () => { const settings = 'pad=false'; - expect(parseSettings(settings)).to.eql({ + expect(parseSettings(settings)).toEqual({ pad: false, }); }); - it('returns the true for 1', () => { + test('returns the true for 1', () => { const settings = 'pad=1'; - expect(parseSettings(settings)).to.eql({ + expect(parseSettings(settings)).toEqual({ pad: true, }); }); - it('returns the false for 0', () => { + test('returns the false for 0', () => { const settings = 'pad=0'; - expect(parseSettings(settings)).to.eql({ + expect(parseSettings(settings)).toEqual({ pad: false, }); }); - it('returns the settings as an object', () => { + test('returns the settings as an object', () => { const settings = 'alpha=0.9 beta=0.4 gamma=0.2 period=5 pad=false type=add'; - expect(parseSettings(settings)).to.eql({ + expect(parseSettings(settings)).toEqual({ alpha: 0.9, beta: 0.4, gamma: 0.2, diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/offset_time.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/offset_time.test.js similarity index 64% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/offset_time.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/offset_time.test.js index 55e43cf7bd8ce..b9b73002bafb3 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/__tests__/offset_time.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/offset_time.test.js @@ -17,12 +17,11 @@ * under the License. */ -import { expect } from 'chai'; import moment from 'moment'; -import { offsetTime } from '../offset_time'; +import { offsetTime } from './offset_time'; describe('offsetTime(req, by)', () => { - it('should return a moment object for to and from', () => { + test('should return a moment object for to and from', () => { const req = { payload: { timerange: { @@ -32,13 +31,13 @@ describe('offsetTime(req, by)', () => { }, }; const { from, to } = offsetTime(req, ''); - expect(moment.isMoment(from)).to.equal(true); - expect(moment.isMoment(to)).to.equal(true); - expect(moment.utc('2017-01-01T00:00:00Z').isSame(from)).to.equal(true); - expect(moment.utc('2017-01-01T01:00:00Z').isSame(to)).to.equal(true); + expect(moment.isMoment(from)).toBeTruthy(); + expect(moment.isMoment(to)).toBeTruthy(); + expect(moment.utc('2017-01-01T00:00:00Z').isSame(from)).toBeTruthy(); + expect(moment.utc('2017-01-01T01:00:00Z').isSame(to)).toBeTruthy(); }); - it('should return a moment object for to and from offset by 1 hour', () => { + test('should return a moment object for to and from offset by 1 hour', () => { const req = { payload: { timerange: { @@ -48,23 +47,23 @@ describe('offsetTime(req, by)', () => { }, }; const { from, to } = offsetTime(req, '1h'); - expect(moment.isMoment(from)).to.equal(true); - expect(moment.isMoment(to)).to.equal(true); + expect(moment.isMoment(from)).toBeTruthy(); + expect(moment.isMoment(to)).toBeTruthy(); expect( moment .utc('2017-01-01T00:00:00Z') .subtract(1, 'h') .isSame(from) - ).to.equal(true); + ).toBeTruthy(); expect( moment .utc('2017-01-01T01:00:00Z') .subtract(1, 'h') .isSame(to) - ).to.equal(true); + ).toBeTruthy(); }); - it('should return a moment object for to and from offset by -2 minute', () => { + test('should return a moment object for to and from offset by -2 minute', () => { const req = { payload: { timerange: { @@ -74,13 +73,13 @@ describe('offsetTime(req, by)', () => { }, }; const { from, to } = offsetTime(req, '-2m'); - expect(moment.isMoment(from)).to.equal(true); - expect(moment.isMoment(to)).to.equal(true); - expect(moment.utc('2017-01-10T01:02:00Z').isSame(from)).to.equal(true); - expect(moment.utc('2017-01-10T02:02:00Z').isSame(to)).to.equal(true); + expect(moment.isMoment(from)).toBeTruthy(); + expect(moment.isMoment(to)).toBeTruthy(); + expect(moment.utc('2017-01-10T01:02:00Z').isSame(from)).toBeTruthy(); + expect(moment.utc('2017-01-10T02:02:00Z').isSame(to)).toBeTruthy(); }); - it('should work when prefixing positive offsets with the plus sign', () => { + test('should work when prefixing positive offsets with the plus sign', () => { const req = { payload: { timerange: { @@ -92,7 +91,7 @@ describe('offsetTime(req, by)', () => { const { from: fromSigned, to: toSigned } = offsetTime(req, '+1m'); const { from, to } = offsetTime(req, '1m'); - expect(fromSigned.isSame(from)).to.equal(true); - expect(toSigned.isSame(to)).to.equal(true); + expect(fromSigned.isSame(from)).toBeTruthy(); + expect(toSigned.isSame(to)).toBeTruthy(); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/date_histogram.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js similarity index 85% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/date_histogram.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js index 40eaba621aabb..7f309b44d13f4 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/date_histogram.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/date_histogram.test.js @@ -17,10 +17,8 @@ * under the License. */ -import { expect } from 'chai'; -import sinon from 'sinon'; -import { DefaultSearchCapabilities } from '../../../../search_strategies/default_search_capabilities'; -import { dateHistogram } from '../date_histogram'; +import { DefaultSearchCapabilities } from '../../../search_strategies/default_search_capabilities'; +import { dateHistogram } from './date_histogram'; describe('dateHistogram(req, panel, series)', () => { let panel; @@ -54,18 +52,18 @@ describe('dateHistogram(req, panel, series)', () => { capabilities = new DefaultSearchCapabilities(req); }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); dateHistogram(req, panel, series, config, indexPatternObject, capabilities)(next)({}); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('returns valid date histogram', () => { + test('returns valid date histogram', () => { const next = doc => doc; const doc = dateHistogram(req, panel, series, config, indexPatternObject, capabilities)(next)( {} ); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { aggs: { @@ -93,13 +91,13 @@ describe('dateHistogram(req, panel, series)', () => { }); }); - it('returns valid date histogram (offset by 1h)', () => { + test('returns valid date histogram (offset by 1h)', () => { series.offset_time = '1h'; const next = doc => doc; const doc = dateHistogram(req, panel, series, config, indexPatternObject, capabilities)(next)( {} ); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { aggs: { @@ -127,7 +125,7 @@ describe('dateHistogram(req, panel, series)', () => { }); }); - it('returns valid date histogram with overridden index pattern', () => { + test('returns valid date histogram with overridden index pattern', () => { series.override_index_pattern = 1; series.series_index_pattern = '*'; series.series_time_field = 'timestamp'; @@ -136,7 +134,7 @@ describe('dateHistogram(req, panel, series)', () => { const doc = dateHistogram(req, panel, series, config, indexPatternObject, capabilities)(next)( {} ); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { aggs: { @@ -165,7 +163,7 @@ describe('dateHistogram(req, panel, series)', () => { }); describe('dateHistogram for entire time range mode', () => { - it('should ignore entire range mode for timeseries', () => { + test('should ignore entire range mode for timeseries', () => { panel.time_range_mode = 'entire_time_range'; panel.type = 'timeseries'; @@ -174,18 +172,18 @@ describe('dateHistogram(req, panel, series)', () => { {} ); - expect(doc.aggs.test.aggs.timeseries.auto_date_histogram).to.eql(undefined); - expect(doc.aggs.test.aggs.timeseries.date_histogram).to.exist; + expect(doc.aggs.test.aggs.timeseries.auto_date_histogram).toBeUndefined(); + expect(doc.aggs.test.aggs.timeseries.date_histogram).toBeDefined(); }); - it('should returns valid date histogram for entire range mode', () => { + test('should returns valid date histogram for entire range mode', () => { panel.time_range_mode = 'entire_time_range'; const next = doc => doc; const doc = dateHistogram(req, panel, series, config, indexPatternObject, capabilities)(next)( {} ); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { aggs: { diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/filter_ratios.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js similarity index 92% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/filter_ratios.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js index 0449ee440da81..f3d25a8e44ba7 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/filter_ratios.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/filter_ratios.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { ratios } from '../filter_ratios'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { ratios } from './filter_ratios'; describe('ratios(req, panel, series)', () => { let panel; @@ -55,16 +53,16 @@ describe('ratios(req, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); ratios(req, panel, series)(next)({}); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('returns filter ratio aggs', () => { + test('returns filter ratio aggs', () => { const next = doc => doc; const doc = ratios(req, panel, series)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { aggs: { @@ -119,11 +117,11 @@ describe('ratios(req, panel, series)', () => { }); }); - it('returns empty object when field is not set', () => { + test('returns empty object when field is not set', () => { delete series.metrics[0].field; const next = doc => doc; const doc = ratios(req, panel, series)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { aggs: { diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/metric_buckets.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.test.js similarity index 88% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/metric_buckets.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.test.js index 9393882f12b7e..eaf8ee1549f0b 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/metric_buckets.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/metric_buckets.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { metricBuckets } from '../metric_buckets'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { metricBuckets } from './metric_buckets'; describe('metricBuckets(req, panel, series)', () => { let panel; @@ -63,16 +61,16 @@ describe('metricBuckets(req, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); metricBuckets(req, panel, series)(next)({}); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('returns metric aggs', () => { + test('returns metric aggs', () => { const next = doc => doc; const doc = metricBuckets(req, panel, series)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { aggs: { diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/query.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js similarity index 91% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/query.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js index 2f2e5f46f834e..06da636f818d5 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/query.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { query } from '../query'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { query } from './query'; describe('query(req, panel, series)', () => { let panel; @@ -47,16 +45,16 @@ describe('query(req, panel, series)', () => { series = { id: 'test' }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); query(req, panel, series, config)(next)({}); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('returns doc with query for timerange', () => { + test('returns doc with query for timerange', () => { const next = doc => doc; const doc = query(req, panel, series, config)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ size: 0, query: { bool: { @@ -79,11 +77,11 @@ describe('query(req, panel, series)', () => { }); }); - it('returns doc with query for timerange (offset by 1h)', () => { + test('returns doc with query for timerange (offset by 1h)', () => { series.offset_time = '1h'; const next = doc => doc; const doc = query(req, panel, series, config)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ size: 0, query: { bool: { @@ -106,7 +104,7 @@ describe('query(req, panel, series)', () => { }); }); - it('returns doc with global query', () => { + test('returns doc with global query', () => { req.payload.filters = [ { bool: { @@ -122,7 +120,7 @@ describe('query(req, panel, series)', () => { ]; const next = doc => doc; const doc = query(req, panel, series, config)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ size: 0, query: { bool: { @@ -157,11 +155,11 @@ describe('query(req, panel, series)', () => { }); }); - it('returns doc with series filter', () => { + test('returns doc with series filter', () => { series.filter = { query: 'host:web-server', language: 'lucene' }; const next = doc => doc; const doc = query(req, panel, series, config)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ size: 0, query: { bool: { @@ -198,7 +196,7 @@ describe('query(req, panel, series)', () => { }, }); }); - it('returns doc with panel filter and global', () => { + test('returns doc with panel filter and global', () => { req.payload.filters = [ { bool: { @@ -215,7 +213,7 @@ describe('query(req, panel, series)', () => { panel.filter = { query: 'host:web-server', language: 'lucene' }; const next = doc => doc; const doc = query(req, panel, series, config)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ size: 0, query: { bool: { @@ -265,7 +263,7 @@ describe('query(req, panel, series)', () => { }); }); - it('returns doc with panel filter (ignoring globals)', () => { + test('returns doc with panel filter (ignoring globals)', () => { req.payload.filters = [ { bool: { @@ -283,7 +281,7 @@ describe('query(req, panel, series)', () => { panel.ignore_global_filter = true; const next = doc => doc; const doc = query(req, panel, series, config)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ size: 0, query: { bool: { diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/sibling_buckets.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.test.js similarity index 86% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/sibling_buckets.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.test.js index 0f02c755cabcd..8e1c9b0f9ecd9 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/sibling_buckets.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/sibling_buckets.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { siblingBuckets } from '../sibling_buckets'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { siblingBuckets } from './sibling_buckets'; describe('siblingBuckets(req, panel, series)', () => { let panel; @@ -57,16 +55,16 @@ describe('siblingBuckets(req, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); siblingBuckets(req, panel, series)(next)({}); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('returns sibling aggs', () => { + test('returns sibling aggs', () => { const next = doc => doc; const doc = siblingBuckets(req, panel, series)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { aggs: { diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_everything.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_everything.test.js similarity index 83% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_everything.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_everything.test.js index 4e2775d3608e6..5c2468f6b7c35 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_everything.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_everything.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { splitByEverything } from '../split_by_everything'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { splitByEverything } from './split_by_everything'; describe('splitByEverything(req, panel, series)', () => { let panel; @@ -39,15 +37,15 @@ describe('splitByEverything(req, panel, series)', () => { }); it('calls next when finished', () => { - const next = sinon.spy(); + const next = jest.fn(); splitByEverything(req, panel, series)(next)({}); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); it('returns a valid filter with match_all', () => { const next = doc => doc; const doc = splitByEverything(req, panel, series)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { filter: { @@ -61,9 +59,9 @@ describe('splitByEverything(req, panel, series)', () => { it('calls next and does not add a filter', () => { series.split_mode = 'terms'; series.terms_field = 'host'; - const next = sinon.spy(doc => doc); + const next = jest.fn(doc => doc); const doc = splitByEverything(req, panel, series)(next)({}); - expect(next.calledOnce).to.equal(true); - expect(doc).to.eql({}); + expect(next.mock.calls.length).toEqual(1); + expect(doc).toEqual({}); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_filter.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.test.js similarity index 79% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_filter.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.test.js index 782310ba4d2bf..75ca782a6d7e7 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_filter.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { splitByFilter } from '../split_by_filter'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { splitByFilter } from './split_by_filter'; describe('splitByFilter(req, panel, series)', () => { let panel; @@ -42,16 +40,16 @@ describe('splitByFilter(req, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); splitByFilter(req, panel, series)(next)({}); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('returns a valid filter with a query_string', () => { + test('returns a valid filter with a query_string', () => { const next = doc => doc; const doc = splitByFilter(req, panel, series)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { filter: { @@ -73,11 +71,11 @@ describe('splitByFilter(req, panel, series)', () => { }); }); - it('calls next and does not add a filter', () => { + test('calls next and does not add a filter', () => { series.split_mode = 'terms'; - const next = sinon.spy(doc => doc); + const next = jest.fn(doc => doc); const doc = splitByFilter(req, panel, series)(next)({}); - expect(next.calledOnce).to.equal(true); - expect(doc).to.eql({}); + expect(next.mock.calls.length).toEqual(1); + expect(doc).toEqual({}); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_filters.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.test.js similarity index 85% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_filters.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.test.js index 018cb4b003ecf..6108b2bf2cf2b 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_filters.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { splitByFilters } from '../split_by_filters'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { splitByFilters } from './split_by_filters'; describe('splitByFilters(req, panel, series)', () => { let panel; @@ -58,16 +56,16 @@ describe('splitByFilters(req, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); splitByFilters(req, panel, series)(next)({}); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('returns a valid terms agg', () => { + test('returns a valid terms agg', () => { const next = doc => doc; const doc = splitByFilters(req, panel, series)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { filters: { @@ -107,11 +105,11 @@ describe('splitByFilters(req, panel, series)', () => { }); }); - it('calls next and does not add a terms agg', () => { + test('calls next and does not add a terms agg', () => { series.split_mode = 'everything'; - const next = sinon.spy(doc => doc); + const next = jest.fn(doc => doc); const doc = splitByFilters(req, panel, series)(next)({}); - expect(next.calledOnce).to.equal(true); - expect(doc).to.eql({}); + expect(next.mock.calls.length).toEqual(1); + expect(doc).toEqual({}); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_terms.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js similarity index 80% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_terms.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js index eb1a81baa7fc2..ff7139c380af9 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/__tests__/split_by_terms.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_terms.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { splitByTerms } from '../split_by_terms'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { splitByTerms } from './split_by_terms'; describe('splitByTerms(req, panel, series)', () => { let panel; @@ -46,16 +44,16 @@ describe('splitByTerms(req, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); splitByTerms(req, panel, series)(next)({}); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('returns a valid terms agg', () => { + test('returns a valid terms agg', () => { const next = doc => doc; const doc = splitByTerms(req, panel, series)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { terms: { @@ -70,12 +68,12 @@ describe('splitByTerms(req, panel, series)', () => { }); }); - it('returns a valid terms agg sort by terms', () => { + test('returns a valid terms agg sort by terms', () => { const next = doc => doc; series.terms_order_by = '_key'; series.terms_direction = 'asc'; const doc = splitByTerms(req, panel, series)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { terms: { @@ -90,11 +88,11 @@ describe('splitByTerms(req, panel, series)', () => { }); }); - it('returns a valid terms agg with custom sort', () => { + test('returns a valid terms agg with custom sort', () => { series.terms_order_by = 'avgmetric'; const next = doc => doc; const doc = splitByTerms(req, panel, series)(next)({}); - expect(doc).to.eql({ + expect(doc).toEqual({ aggs: { test: { terms: { @@ -116,11 +114,11 @@ describe('splitByTerms(req, panel, series)', () => { }); }); - it('calls next and does not add a terms agg', () => { + test('calls next and does not add a terms agg', () => { series.split_mode = 'everything'; - const next = sinon.spy(doc => doc); + const next = jest.fn(doc => doc); const doc = splitByTerms(req, panel, series)(next)({}); - expect(next.calledOnce).to.equal(true); - expect(doc).to.eql({}); + expect(next.mock.calls.length).toEqual(1); + expect(doc).toEqual({}); }); }); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/_series_agg.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/_series_agg.test.js similarity index 68% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/_series_agg.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/_series_agg.test.js index 582466f8eb9fd..22ea2c852e254 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/_series_agg.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/_series_agg.test.js @@ -17,8 +17,7 @@ * under the License. */ -import { expect } from 'chai'; -import { SeriesAgg as seriesAgg } from '../_series_agg'; +import { SeriesAgg as seriesAgg } from './_series_agg'; describe('seriesAgg', () => { const series = [ @@ -40,8 +39,8 @@ describe('seriesAgg', () => { ]; describe('basic', () => { - it('returns the series sum', () => { - expect(seriesAgg.sum(series)).to.eql([ + test('returns the series sum', () => { + expect(seriesAgg.sum(series)).toEqual([ [ [0, 8], [1, 4], @@ -50,8 +49,8 @@ describe('seriesAgg', () => { ]); }); - it('returns the series max', () => { - expect(seriesAgg.max(series)).to.eql([ + test('returns the series max', () => { + expect(seriesAgg.max(series)).toEqual([ [ [0, 4], [1, 2], @@ -60,8 +59,8 @@ describe('seriesAgg', () => { ]); }); - it('returns the series min', () => { - expect(seriesAgg.min(series)).to.eql([ + test('returns the series min', () => { + expect(seriesAgg.min(series)).toEqual([ [ [0, 2], [1, 1], @@ -70,8 +69,8 @@ describe('seriesAgg', () => { ]); }); - it('returns the series mean', () => { - expect(seriesAgg.mean(series)).to.eql([ + test('returns the series mean', () => { + expect(seriesAgg.mean(series)).toEqual([ [ [0, 8 / 3], [1, 4 / 3], @@ -82,8 +81,8 @@ describe('seriesAgg', () => { }); describe('overall', () => { - it('returns the series overall sum', () => { - expect(seriesAgg.overall_sum(series)).to.eql([ + test('returns the series overall sum', () => { + expect(seriesAgg.overall_sum(series)).toEqual([ [ [0, 21], [1, 21], @@ -92,8 +91,8 @@ describe('seriesAgg', () => { ]); }); - it('returns the series overall max', () => { - expect(seriesAgg.overall_max(series)).to.eql([ + test('returns the series overall max', () => { + expect(seriesAgg.overall_max(series)).toEqual([ [ [0, 4], [1, 4], @@ -102,8 +101,8 @@ describe('seriesAgg', () => { ]); }); - it('returns the series overall min', () => { - expect(seriesAgg.overall_min(series)).to.eql([ + test('returns the series overall min', () => { + expect(seriesAgg.overall_min(series)).toEqual([ [ [0, 1], [1, 1], @@ -112,9 +111,9 @@ describe('seriesAgg', () => { ]); }); - it('returns the series overall mean', () => { + test('returns the series overall mean', () => { const value = (8 + 4 + 9) / 3; - expect(seriesAgg.overall_avg(series)).to.eql([ + expect(seriesAgg.overall_avg(series)).toEqual([ [ [0, value], [1, value], @@ -125,8 +124,8 @@ describe('seriesAgg', () => { }); describe('cumulative sum', () => { - it('returns the series cumulative sum', () => { - expect(seriesAgg.cumulative_sum(series)).to.eql([ + test('returns the series cumulative sum', () => { + expect(seriesAgg.cumulative_sum(series)).toEqual([ [ [0, 8], [1, 12], diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/percentile.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile.test.js similarity index 57% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/percentile.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile.test.js index d40a75c903602..9cb08de8dad23 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/percentile.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { percentile } from '../percentile'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { percentile } from './percentile'; describe('percentile(resp, panel, series)', () => { let panel; @@ -82,72 +80,72 @@ describe('percentile(resp, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); percentile(resp, panel, series)(next)([]); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('creates a series', () => { + test('creates a series', () => { const next = results => results; const results = percentile(resp, panel, series)(next)([]); - expect(results).to.have.length(3); + expect(results).toHaveLength(3); - expect(results[0]).to.have.property('id', 'test:10-90'); - expect(results[0]).to.have.property('color', 'rgb(255, 0, 0)'); - expect(results[0]).to.have.property('fillBetween', 'test:10-90:90'); - expect(results[0]).to.have.property('label', 'Percentile of cpu (10)'); - expect(results[0]).to.have.property('legend', false); - expect(results[0]).to.have.property('lines'); - expect(results[0].lines).to.eql({ + expect(results[0]).toHaveProperty('id', 'test:10-90'); + expect(results[0]).toHaveProperty('color', 'rgb(255, 0, 0)'); + expect(results[0]).toHaveProperty('fillBetween', 'test:10-90:90'); + expect(results[0]).toHaveProperty('label', 'Percentile of cpu (10)'); + expect(results[0]).toHaveProperty('legend', false); + expect(results[0]).toHaveProperty('lines'); + expect(results[0].lines).toEqual({ fill: 0.2, lineWidth: 0, show: true, }); - expect(results[0]).to.have.property('points'); - expect(results[0].points).to.eql({ show: false }); - expect(results[0].data).to.eql([ + expect(results[0]).toHaveProperty('points'); + expect(results[0].points).toEqual({ show: false }); + expect(results[0].data).toEqual([ [1, 1], [2, 1.2], ]); - expect(results[1]).to.have.property('id', 'test:10-90:90'); - expect(results[1]).to.have.property('color', 'rgb(255, 0, 0)'); - expect(results[1]).to.have.property('label', 'Percentile of cpu (10)'); - expect(results[1]).to.have.property('legend', false); - expect(results[1]).to.have.property('lines'); - expect(results[1].lines).to.eql({ + expect(results[1]).toHaveProperty('id', 'test:10-90:90'); + expect(results[1]).toHaveProperty('color', 'rgb(255, 0, 0)'); + expect(results[1]).toHaveProperty('label', 'Percentile of cpu (10)'); + expect(results[1]).toHaveProperty('legend', false); + expect(results[1]).toHaveProperty('lines'); + expect(results[1].lines).toEqual({ fill: false, lineWidth: 0, show: true, }); - expect(results[1]).to.have.property('points'); - expect(results[1].points).to.eql({ show: false }); - expect(results[1].data).to.eql([ + expect(results[1]).toHaveProperty('points'); + expect(results[1].points).toEqual({ show: false }); + expect(results[1].data).toEqual([ [1, 5], [2, 5.3], ]); - expect(results[2]).to.have.property('id', 'test:50'); - expect(results[2]).to.have.property('color', 'rgb(255, 0, 0)'); - expect(results[2]).to.have.property('label', 'Percentile of cpu (50)'); - expect(results[2]).to.have.property('stack', false); - expect(results[2]).to.have.property('lines'); - expect(results[2].lines).to.eql({ + expect(results[2]).toHaveProperty('id', 'test:50'); + expect(results[2]).toHaveProperty('color', 'rgb(255, 0, 0)'); + expect(results[2]).toHaveProperty('label', 'Percentile of cpu (50)'); + expect(results[2]).toHaveProperty('stack', false); + expect(results[2]).toHaveProperty('lines'); + expect(results[2].lines).toEqual({ fill: 0, lineWidth: 1, show: true, steps: false, }); - expect(results[2]).to.have.property('bars'); - expect(results[2].bars).to.eql({ + expect(results[2]).toHaveProperty('bars'); + expect(results[2].bars).toEqual({ fill: 0, lineWidth: 1, show: false, }); - expect(results[2]).to.have.property('points'); - expect(results[2].points).to.eql({ show: true, lineWidth: 1, radius: 1 }); - expect(results[2].data).to.eql([ + expect(results[2]).toHaveProperty('points'); + expect(results[2].points).toEqual({ show: true, lineWidth: 1, radius: 1 }); + expect(results[2].data).toEqual([ [1, 2.5], [2, 2.7], ]); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/series_agg.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/series_agg.test.js similarity index 88% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/series_agg.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/series_agg.test.js index 2391cd3bc7698..3e09c51d9184f 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/series_agg.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/series_agg.test.js @@ -17,10 +17,8 @@ * under the License. */ -import { seriesAgg } from '../series_agg'; -import { stdMetric } from '../std_metric'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { seriesAgg } from './series_agg'; +import { stdMetric } from './std_metric'; describe('seriesAgg(resp, panel, series)', () => { let panel; @@ -93,18 +91,18 @@ describe('seriesAgg(resp, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); seriesAgg(resp, panel, series)(next)([]); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('creates a series', () => { + test('creates a series', () => { const next = seriesAgg(resp, panel, series)(results => results); const results = stdMetric(resp, panel, series)(next)([]); - expect(results).to.have.length(1); + expect(results).toHaveLength(1); - expect(results[0]).to.eql({ + expect(results[0]).toEqual({ id: 'test', color: '#F00', label: 'Total CPU', diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_deviation_bands.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js similarity index 87% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_deviation_bands.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js index 488ae4a8351b7..77949ff94dc4c 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_deviation_bands.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_bands.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { stdDeviationBands } from '../std_deviation_bands'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { stdDeviationBands } from './std_deviation_bands'; describe('stdDeviationBands(resp, panel, series)', () => { let panel; @@ -79,18 +77,18 @@ describe('stdDeviationBands(resp, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); stdDeviationBands(resp, panel, series)(next)([]); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('creates a series', () => { + test('creates a series', () => { const next = results => results; const results = stdDeviationBands(resp, panel, series)(next)([]); - expect(results).to.have.length(2); + expect(results).toHaveLength(2); - expect(results[0]).to.eql({ + expect(results[0]).toEqual({ id: 'test:upper', label: 'Std. Deviation of cpu', color: 'rgb(255, 0, 0)', @@ -103,7 +101,7 @@ describe('stdDeviationBands(resp, panel, series)', () => { ], }); - expect(results[1]).to.eql({ + expect(results[1]).toEqual({ id: 'test:lower', color: 'rgb(255, 0, 0)', lines: { show: true, fill: false, lineWidth: 0 }, diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_deviation_sibling.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js similarity index 87% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_deviation_sibling.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js index 14c2c16ef728f..adc5a3a4a991b 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_deviation_sibling.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_deviation_sibling.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { stdDeviationSibling } from '../std_deviation_sibling'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { stdDeviationSibling } from './std_deviation_sibling'; describe('stdDeviationSibling(resp, panel, series)', () => { let panel; @@ -79,18 +77,18 @@ describe('stdDeviationSibling(resp, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); stdDeviationSibling(resp, panel, series)(next)([]); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('creates a series', () => { + test('creates a series', () => { const next = results => results; const results = stdDeviationSibling(resp, panel, series)(next)([]); - expect(results).to.have.length(2); + expect(results).toHaveLength(2); - expect(results[0]).to.eql({ + expect(results[0]).toEqual({ id: 'test:lower', color: 'rgb(255, 0, 0)', lines: { show: true, fill: false, lineWidth: 0 }, @@ -101,7 +99,7 @@ describe('stdDeviationSibling(resp, panel, series)', () => { ], }); - expect(results[1]).to.eql({ + expect(results[1]).toEqual({ id: 'test:upper', label: 'Overall Std. Deviation of Average of cpu', color: 'rgb(255, 0, 0)', diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_metric.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_metric.test.js similarity index 65% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_metric.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_metric.test.js index 5039a406a6acc..6ddd6f907fa97 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_metric.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_metric.test.js @@ -17,9 +17,7 @@ * under the License. */ -import sinon from 'sinon'; -import { expect } from 'chai'; -import { stdMetric } from '../std_metric'; +import { stdMetric } from './std_metric'; describe('stdMetric(resp, panel, series)', () => { let panel; @@ -60,41 +58,41 @@ describe('stdMetric(resp, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); stdMetric(resp, panel, series)(next)([]); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('calls next when finished (percentile)', () => { + test('calls next when finished (percentile)', () => { series.metrics[0].type = 'percentile'; - const next = sinon.spy(d => d); + const next = jest.fn(d => d); const results = stdMetric(resp, panel, series)(next)([]); - expect(next.calledOnce).to.equal(true); - expect(results).to.have.length(0); + expect(next.mock.calls.length).toEqual(1); + expect(results).toHaveLength(0); }); - it('calls next when finished (std_deviation band)', () => { + test('calls next when finished (std_deviation band)', () => { series.metrics[0].type = 'std_deviation'; series.metrics[0].mode = 'band'; - const next = sinon.spy(d => d); + const next = jest.fn(d => d); const results = stdMetric(resp, panel, series)(next)([]); - expect(next.calledOnce).to.equal(true); - expect(results).to.have.length(0); + expect(next.mock.calls.length).toEqual(1); + expect(results).toHaveLength(0); }); - it('creates a series', () => { + test('creates a series', () => { const next = results => results; const results = stdMetric(resp, panel, series)(next)([]); - expect(results).to.have.length(1); - expect(results[0]).to.have.property('color', 'rgb(255, 0, 0)'); - expect(results[0]).to.have.property('id', 'test'); - expect(results[0]).to.have.property('label', 'Average of cpu'); - expect(results[0]).to.have.property('lines'); - expect(results[0]).to.have.property('stack'); - expect(results[0]).to.have.property('bars'); - expect(results[0]).to.have.property('points'); - expect(results[0].data).to.eql([ + expect(results).toHaveLength(1); + expect(results[0]).toHaveProperty('color', 'rgb(255, 0, 0)'); + expect(results[0]).toHaveProperty('id', 'test'); + expect(results[0]).toHaveProperty('label', 'Average of cpu'); + expect(results[0]).toHaveProperty('lines'); + expect(results[0]).toHaveProperty('stack'); + expect(results[0]).toHaveProperty('bars'); + expect(results[0]).toHaveProperty('points'); + expect(results[0].data).toEqual([ [1, 1], [2, 2], ]); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_sibling.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_sibling.test.js similarity index 82% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_sibling.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_sibling.test.js index a243ad525332c..9ed60a6d16492 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/std_sibling.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/std_sibling.test.js @@ -17,9 +17,7 @@ * under the License. */ -import { stdSibling } from '../std_sibling'; -import { expect } from 'chai'; -import sinon from 'sinon'; +import { stdSibling } from './std_sibling'; describe('stdSibling(resp, panel, series)', () => { let panel; @@ -74,26 +72,26 @@ describe('stdSibling(resp, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); stdSibling(resp, panel, series)(next)([]); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('calls next when std. deviation bands set', () => { + test('calls next when std. deviation bands set', () => { series.metrics[1].mode = 'band'; - const next = sinon.spy(results => results); + const next = jest.fn(results => results); const results = stdSibling(resp, panel, series)(next)([]); - expect(next.calledOnce).to.equal(true); - expect(results).to.have.length(0); + expect(next.mock.calls.length).toEqual(1); + expect(results).toHaveLength(0); }); - it('creates a series', () => { + test('creates a series', () => { const next = results => results; const results = stdSibling(resp, panel, series)(next)([]); - expect(results).to.have.length(1); + expect(results).toHaveLength(1); - expect(results[0]).to.eql({ + expect(results[0]).toEqual({ id: 'test', label: 'Overall Std. Deviation of Average of cpu', color: 'rgb(255, 0, 0)', diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/time_shift.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/time_shift.test.js similarity index 71% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/time_shift.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/time_shift.test.js index 1e6da85f88164..7047c54fa1c0e 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/__tests__/time_shift.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/time_shift.test.js @@ -17,10 +17,8 @@ * under the License. */ -import sinon from 'sinon'; -import { expect } from 'chai'; -import { timeShift } from '../time_shift'; -import { stdMetric } from '../std_metric'; +import { timeShift } from './time_shift'; +import { stdMetric } from './std_metric'; describe('timeShift(resp, panel, series)', () => { let panel; @@ -62,24 +60,24 @@ describe('timeShift(resp, panel, series)', () => { }; }); - it('calls next when finished', () => { - const next = sinon.spy(); + test('calls next when finished', () => { + const next = jest.fn(); timeShift(resp, panel, series)(next)([]); - expect(next.calledOnce).to.equal(true); + expect(next.mock.calls.length).toEqual(1); }); - it('creates a series', () => { + test('creates a series', () => { const next = timeShift(resp, panel, series)(results => results); const results = stdMetric(resp, panel, series)(next)([]); - expect(results).to.have.length(1); - expect(results[0]).to.have.property('color', 'rgb(255, 0, 0)'); - expect(results[0]).to.have.property('id', 'test'); - expect(results[0]).to.have.property('label', 'Average of cpu'); - expect(results[0]).to.have.property('lines'); - expect(results[0]).to.have.property('stack'); - expect(results[0]).to.have.property('bars'); - expect(results[0]).to.have.property('points'); - expect(results[0].data).to.eql([ + expect(results).toHaveLength(1); + expect(results[0]).toHaveProperty('color', 'rgb(255, 0, 0)'); + expect(results[0]).toHaveProperty('id', 'test'); + expect(results[0]).toHaveProperty('label', 'Average of cpu'); + expect(results[0]).toHaveProperty('lines'); + expect(results[0]).toHaveProperty('stack'); + expect(results[0]).toHaveProperty('bars'); + expect(results[0]).toHaveProperty('points'); + expect(results[0].data).toEqual([ [1483225200000 + 3600000, 1], [1483225210000 + 3600000, 2], ]); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/__tests__/build_request_body.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts similarity index 95% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/__tests__/build_request_body.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts index f21bf3d197969..0c75e6ef1c5bd 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/__tests__/build_request_body.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.test.ts @@ -17,6 +17,8 @@ * under the License. */ +import { buildRequestBody } from './build_request_body'; + const body = JSON.parse(` { "filters": [ @@ -75,15 +77,11 @@ const body = JSON.parse(` } `); -import sinon from 'sinon'; -import { expect } from 'chai'; -import { buildRequestBody } from '../build_request_body'; - describe('buildRequestBody(req)', () => { - it('returns a valid body', () => { + test('returns a valid body', () => { const panel = body.panels[0]; const series = panel.series[0]; - const getValidTimeInterval = sinon.spy(() => '10s'); + const getValidTimeInterval = jest.fn(() => '10s'); const capabilities = { searchTimezone: 'UTC', getValidTimeInterval, @@ -102,7 +100,7 @@ describe('buildRequestBody(req)', () => { capabilities ); - expect(doc).to.eql({ + expect(doc).toEqual({ size: 0, query: { bool: { diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.ts similarity index 95% rename from src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.js rename to src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.ts index fe3137a8f86ba..85e1f8f7eb12b 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/series/build_request_body.ts @@ -18,6 +18,7 @@ */ import { buildProcessorFunction } from '../build_processor_function'; +// @ts-ignore import { processors } from '../request_processors/series'; /** @@ -33,7 +34,7 @@ import { processors } from '../request_processors/series'; * ] * @returns {Object} doc - processed body */ -export function buildRequestBody(...args) { +export function buildRequestBody(...args: any[]) { const processor = buildProcessorFunction(processors, ...args); const doc = processor({}); return doc; diff --git a/src/legacy/core_plugins/vis_type_vislib/index.ts b/src/legacy/core_plugins/vis_type_vislib/index.ts new file mode 100644 index 0000000000000..8c24368f43ab1 --- /dev/null +++ b/src/legacy/core_plugins/vis_type_vislib/index.ts @@ -0,0 +1,44 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { resolve } from 'path'; +import { Legacy } from 'kibana'; + +import { LegacyPluginApi, LegacyPluginInitializer } from '../../types'; + +const kbnVislibVisTypesPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => + new Plugin({ + id: 'vis_type_vislib', + require: ['kibana', 'elasticsearch', 'visualizations', 'interpreter', 'data'], + publicDir: resolve(__dirname, 'public'), + styleSheetPaths: resolve(__dirname, 'public/index.scss'), + uiExports: { + hacks: [resolve(__dirname, 'public/legacy')], + injectDefaultVars: server => ({}), + }, + init: (server: Legacy.Server) => ({}), + config(Joi: any) { + return Joi.object({ + enabled: Joi.boolean().default(true), + }).default(); + }, + } as Legacy.PluginSpecOptions); + +// eslint-disable-next-line import/no-default-export +export default kbnVislibVisTypesPluginInitializer; diff --git a/src/legacy/core_plugins/vis_type_vislib/package.json b/src/legacy/core_plugins/vis_type_vislib/package.json new file mode 100644 index 0000000000000..e30a9e2b35834 --- /dev/null +++ b/src/legacy/core_plugins/vis_type_vislib/package.json @@ -0,0 +1,4 @@ +{ + "name": "vis_type_vislib", + "version": "kibana" +} diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/__snapshots__/pie_fn.test.js.snap b/src/legacy/core_plugins/vis_type_vislib/public/__snapshots__/pie_fn.test.ts.snap similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/__snapshots__/pie_fn.test.js.snap rename to src/legacy/core_plugins/vis_type_vislib/public/__snapshots__/pie_fn.test.ts.snap diff --git a/src/legacy/core_plugins/vis_type_vislib/public/_index.scss b/src/legacy/core_plugins/vis_type_vislib/public/_index.scss new file mode 100644 index 0000000000000..64445648ba84a --- /dev/null +++ b/src/legacy/core_plugins/vis_type_vislib/public/_index.scss @@ -0,0 +1 @@ +@import './vislib/index' diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/area.js b/src/legacy/core_plugins/vis_type_vislib/public/area.ts similarity index 89% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/area.js rename to src/legacy/core_plugins/vis_type_vislib/public/area.ts index fd13067c84cc0..9484ddc16fe62 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/area.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/area.ts @@ -18,8 +18,12 @@ */ import { i18n } from '@kbn/i18n'; -import { Schemas } from 'ui/vis/editors/default/schemas'; -import { AggGroupNames } from 'ui/vis/editors/default'; +// @ts-ignore +import { palettes } from '@elastic/eui/lib/services'; +// @ts-ignore +import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; + +import { Schemas, AggGroupNames } from './legacy_imports'; import { Positions, ChartTypes, @@ -33,17 +37,17 @@ import { getConfigCollections, } from './utils/collections'; import { getAreaOptionTabs, countLabel } from './utils/common_config'; -import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; -import { vislibVisController } from './controller'; +import { createVislibVisController } from './vis_controller'; +import { KbnVislibVisTypesDependencies } from './plugin'; -export const areaDefinition = { +export const createAreaVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({ name: 'area', title: i18n.translate('kbnVislibVisTypes.area.areaTitle', { defaultMessage: 'Area' }), icon: 'visArea', description: i18n.translate('kbnVislibVisTypes.area.areaDescription', { defaultMessage: 'Emphasize the quantity beneath a line chart', }), - visualization: vislibVisController, + visualization: createVislibVisController(deps), visConfig: { defaults: { type: 'area', @@ -132,7 +136,9 @@ export const areaDefinition = { { group: AggGroupNames.Metrics, name: 'metric', - title: i18n.translate('kbnVislibVisTypes.area.metricsTitle', { defaultMessage: 'Y-axis' }), + title: i18n.translate('kbnVislibVisTypes.area.metricsTitle', { + defaultMessage: 'Y-axis', + }), aggFilter: ['!geo_centroid', '!geo_bounds'], min: 1, defaults: [{ schema: 'metric', type: 'count' }], @@ -140,7 +146,9 @@ export const areaDefinition = { { group: AggGroupNames.Metrics, name: 'radius', - title: i18n.translate('kbnVislibVisTypes.area.radiusTitle', { defaultMessage: 'Dot size' }), + title: i18n.translate('kbnVislibVisTypes.area.radiusTitle', { + defaultMessage: 'Dot size', + }), min: 0, max: 1, aggFilter: ['count', 'avg', 'sum', 'min', 'max', 'cardinality'], @@ -148,7 +156,9 @@ export const areaDefinition = { { group: AggGroupNames.Buckets, name: 'segment', - title: i18n.translate('kbnVislibVisTypes.area.segmentTitle', { defaultMessage: 'X-axis' }), + title: i18n.translate('kbnVislibVisTypes.area.segmentTitle', { + defaultMessage: 'X-axis', + }), min: 0, max: 1, aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'], @@ -175,4 +185,4 @@ export const areaDefinition = { }, ]), }, -}; +}); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/basic_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/basic_options.tsx similarity index 96% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/basic_options.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/basic_options.tsx index 2bffcb383dde3..81174d63060e5 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/basic_options.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/common/basic_options.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from 'ui/vis/editors/default'; +import { VisOptionsProps } from '../../legacy_imports'; import { SwitchOption } from './switch'; import { SelectOption } from './select'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/color_ranges.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_ranges.tsx similarity index 96% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/color_ranges.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/color_ranges.tsx index 947c7ae7e6e36..2bf58de2f93ed 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/color_ranges.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_ranges.tsx @@ -19,9 +19,10 @@ import React, { useCallback } from 'react'; import { last } from 'lodash'; + import { i18n } from '@kbn/i18n'; -import { RangeValues, RangesParamEditor } from 'ui/vis/editors/default/controls/ranges'; +import { RangeValues, RangesParamEditor } from '../../legacy_imports'; interface ColorRangesProps { 'data-test-subj'?: string; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/color_schema.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_schema.tsx similarity index 96% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/color_schema.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/color_schema.tsx index 44837c514d4cf..7b4679ba25e8f 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/color_schema.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_schema.tsx @@ -22,8 +22,7 @@ import { i18n } from '@kbn/i18n'; import { EuiLink, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; -import { ColorSchema } from 'ui/vislib/components/color/colormaps'; +import { VisOptionsProps, ColorSchema } from '../../legacy_imports'; import { SelectOption } from './select'; import { SwitchOption } from './switch'; import { ColorSchemaVislibParams } from '../../types'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/index.ts b/src/legacy/core_plugins/vis_type_vislib/public/components/common/index.ts similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/index.ts rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/index.ts diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/number_input.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/number_input.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/number_input.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/number_input.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/range.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/range.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/range.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/range.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/required_number_input.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/required_number_input.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/required_number_input.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/required_number_input.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/select.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/select.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/select.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/select.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/switch.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/switch.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/switch.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/switch.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/text_input.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/text_input.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/text_input.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/text_input.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/truncate_labels.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/truncate_labels.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/truncate_labels.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/truncate_labels.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/utils.ts b/src/legacy/core_plugins/vis_type_vislib/public/components/common/utils.ts similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/utils.ts rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/utils.ts diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/validation_wrapper.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/validation_wrapper.tsx similarity index 96% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/validation_wrapper.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/common/validation_wrapper.tsx index 1dd1ab49d9a47..b38c65d086823 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/common/validation_wrapper.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/common/validation_wrapper.tsx @@ -18,7 +18,8 @@ */ import React, { useEffect, useState, useCallback } from 'react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; + +import { VisOptionsProps } from '../../legacy_imports'; export interface ValidationVisOptionsProps extends VisOptionsProps { setMultipleValidity(paramName: string, isValid: boolean): void; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/index.ts b/src/legacy/core_plugins/vis_type_vislib/public/components/index.ts similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/index.ts rename to src/legacy/core_plugins/vis_type_vislib/public/components/index.ts diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/index.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/index.tsx similarity index 97% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/index.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/index.tsx index e3805027d658c..2ba4319a82a95 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/index.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/index.tsx @@ -20,7 +20,7 @@ import React, { useCallback } from 'react'; import { EuiSpacer } from '@elastic/eui'; -import { VisOptionsProps } from 'ui/vis/editors/default'; +import { VisOptionsProps } from '../../../legacy_imports'; import { GaugeVisParams } from '../../../gauge'; import { RangesPanel } from './ranges_panel'; import { StylePanel } from './style_panel'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/labels_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/labels_panel.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/labels_panel.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/labels_panel.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/ranges_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/ranges_panel.tsx similarity index 98% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/ranges_panel.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/ranges_panel.tsx index 1045543512c6b..71ffb22243a0f 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/ranges_panel.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/ranges_panel.tsx @@ -22,7 +22,7 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ColorSchemas } from 'ui/vislib/components/color/colormaps'; +import { ColorSchemas } from '../../../legacy_imports'; import { ColorRanges, ColorSchemaOptions, SwitchOption } from '../../common'; import { GaugeOptionsInternalProps } from '.'; import { ColorSchemaVislibParams } from '../../../types'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/style_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/style_panel.tsx similarity index 97% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/style_panel.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/style_panel.tsx index a76171673d9a8..0b4986ae8ad80 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/gauge/style_panel.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/style_panel.tsx @@ -22,7 +22,7 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { AggGroupNames } from 'ui/vis/editors/default'; +import { AggGroupNames } from '../../../legacy_imports'; import { SelectOption } from '../../common'; import { GaugeOptionsInternalProps } from '.'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/heatmap/index.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/index.tsx similarity index 99% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/heatmap/index.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/index.tsx index 659bcf47f7a0b..7ce38fdcda1c8 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/heatmap/index.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/index.tsx @@ -18,11 +18,12 @@ */ import React, { useCallback, useEffect, useState } from 'react'; + import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; +import { VisOptionsProps } from '../../../legacy_imports'; import { BasicOptions, ColorRanges, diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/heatmap/labels_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx similarity index 98% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/heatmap/labels_panel.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx index c0e9a70e8b11e..6ca0bb26ef535 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/heatmap/labels_panel.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx @@ -18,11 +18,12 @@ */ import React, { useCallback } from 'react'; + import { EuiColorPicker, EuiFormRow, EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; +import { VisOptionsProps } from '../../../legacy_imports'; import { ValueAxis } from '../../../types'; import { HeatmapVisParams } from '../../../heatmap'; import { SwitchOption } from '../../common'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/index.ts b/src/legacy/core_plugins/vis_type_vislib/public/components/options/index.ts similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/index.ts rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/index.ts diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/category_axis_panel.test.tsx.snap diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/chart_options.test.tsx.snap diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/custom_extents_options.test.tsx.snap diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/label_options.test.tsx.snap diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/line_options.test.tsx.snap diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axes_panel.test.tsx.snap diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/value_axis_options.test.tsx.snap diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/y_extents.test.tsx.snap diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/category_axis_panel.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.test.tsx similarity index 98% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/category_axis_panel.test.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.test.tsx index a32e48baf4588..69622bb3666a6 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/category_axis_panel.test.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.test.tsx @@ -25,6 +25,8 @@ import { Positions, getPositions } from '../../../utils/collections'; import { LabelOptions } from './label_options'; import { categoryAxis } from './mocks'; +jest.mock('ui/new_platform'); + const positions = getPositions(); describe('CategoryAxisPanel component', () => { diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/category_axis_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx similarity index 98% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/category_axis_panel.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx index 11946a5a6bccd..b83508f3f0896 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/category_axis_panel.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx @@ -18,11 +18,12 @@ */ import React, { useCallback } from 'react'; + import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; +import { VisOptionsProps } from '../../../legacy_imports'; import { BasicVislibParams, Axis } from '../../../types'; import { SelectOption, SwitchOption } from '../../common'; import { LabelOptions } from './label_options'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/chart_options.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.test.tsx similarity index 99% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/chart_options.test.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.test.tsx index ba1a46ba7d89e..9679728a2a3d1 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/chart_options.test.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.test.tsx @@ -31,6 +31,8 @@ import { } from '../../../utils/collections'; import { valueAxis, seriesParam } from './mocks'; +jest.mock('ui/new_platform'); + const interpolationModes = getInterpolationModes(); const chartTypes = getChartTypes(); const chartModes = getChartModes(); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/chart_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx similarity index 98% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/chart_options.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx index 1c9357c67c2f0..8830c9164c751 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/chart_options.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/chart_options.tsx @@ -18,10 +18,11 @@ */ import React, { useMemo, useCallback } from 'react'; + import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { VisOptionsProps } from 'ui/vis/editors/default'; +import { VisOptionsProps } from '../../../legacy_imports'; import { BasicVislibParams, SeriesParam, ValueAxis } from '../../../types'; import { ChartTypes } from '../../../utils/collections'; import { SelectOption } from '../../common'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/custom_extents_options.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.test.tsx similarity index 99% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/custom_extents_options.test.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.test.tsx index 5f1779ad35304..a112b9a3db708 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/custom_extents_options.test.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.test.tsx @@ -28,6 +28,8 @@ const DEFAULT_Y_EXTENTS = 'defaultYExtents'; const SCALE = 'scale'; const SET_Y_EXTENTS = 'setYExtents'; +jest.mock('ui/new_platform'); + describe('CustomExtentsOptions component', () => { let setValueAxis: jest.Mock; let setValueAxisScale: jest.Mock; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/custom_extents_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/custom_extents_options.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/custom_extents_options.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/index.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx similarity index 99% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/index.test.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx index dc5cf42277603..df1920bd4013c 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/index.test.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx @@ -19,6 +19,7 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; + import { MetricsAxisOptions } from './index'; import { BasicVislibParams, SeriesParam, ValueAxis } from '../../../types'; import { ValidationVisOptionsProps } from '../../common'; @@ -26,10 +27,10 @@ import { Positions } from '../../../utils/collections'; import { ValueAxesPanel } from './value_axes_panel'; import { CategoryAxisPanel } from './category_axis_panel'; import { ChartTypes } from '../../../utils/collections'; -import { AggConfig } from 'ui/vis'; -import { AggType } from 'ui/agg_types'; +import { AggConfig, AggType } from '../../../legacy_imports'; import { defaultValueAxisId, valueAxis, seriesParam, categoryAxis } from './mocks'; +jest.mock('ui/new_platform'); jest.mock('./series_panel', () => ({ SeriesPanel: () => 'SeriesPanel', })); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/index.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx similarity index 99% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/index.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx index 2ca4ed1e2343d..85077ed492331 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/index.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx @@ -21,7 +21,7 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { cloneDeep, uniq, get } from 'lodash'; import { EuiSpacer } from '@elastic/eui'; -import { AggConfig } from 'ui/vis'; +import { AggConfig } from '../../../legacy_imports'; import { BasicVislibParams, ValueAxis, SeriesParam, Axis } from '../../../types'; import { ValidationVisOptionsProps } from '../../common'; import { SeriesPanel } from './series_panel'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/label_options.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.test.tsx similarity index 99% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/label_options.test.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.test.tsx index abb3a2455f9f9..91d9987c77f3b 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/label_options.test.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.test.tsx @@ -23,6 +23,8 @@ import { LabelOptions, LabelOptionsProps } from './label_options'; import { TruncateLabelsOption } from '../../common'; import { valueAxis, categoryAxis } from './mocks'; +jest.mock('ui/new_platform'); + const FILTER = 'filter'; const ROTATE = 'rotate'; const DISABLED = 'disabled'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/label_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.tsx similarity index 98% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/label_options.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.tsx index 427d43e18cca6..6a94eabe25243 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/label_options.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/label_options.tsx @@ -18,11 +18,12 @@ */ import React, { useCallback, useMemo } from 'react'; + import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; +import { VisOptionsProps } from '../../../legacy_imports'; import { BasicVislibParams, Axis } from '../../../types'; import { SelectOption, SwitchOption, TruncateLabelsOption } from '../../common'; import { getRotateOptions } from '../../../utils/collections'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/line_options.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.test.tsx similarity index 98% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/line_options.test.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.test.tsx index 0e603814493fa..98ef8a094a260 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/line_options.test.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.test.tsx @@ -24,6 +24,8 @@ import { NumberInputOption } from '../../common'; import { getInterpolationModes } from '../../../utils/collections'; import { seriesParam } from './mocks'; +jest.mock('ui/new_platform'); + const LINE_WIDTH = 'lineWidth'; const DRAW_LINES = 'drawLinesBetweenPoints'; const interpolationModes = getInterpolationModes(); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/line_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx similarity index 98% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/line_options.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx index 9514b69a20b04..0848b708b9094 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/line_options.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/line_options.tsx @@ -18,10 +18,11 @@ */ import React, { useCallback } from 'react'; + import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { Vis } from 'ui/vis'; +import { Vis } from '../../../legacy_imports'; import { SeriesParam } from '../../../types'; import { NumberInputOption, SelectOption, SwitchOption } from '../../common'; import { SetChart } from './chart_options'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/mocks.ts b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/mocks.ts rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/mocks.ts diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/series_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx similarity index 97% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/series_panel.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx index 5a455f4adde31..4d87cc61797fc 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/series_panel.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/series_panel.tsx @@ -18,11 +18,12 @@ */ import React from 'react'; + import { EuiPanel, EuiTitle, EuiSpacer, EuiAccordion } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; +import { VisOptionsProps } from '../../../legacy_imports'; import { BasicVislibParams } from '../../../types'; import { ChartOptions } from './chart_options'; import { SetParamByIndex, ChangeValueAxis } from './'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/utils.ts b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/utils.ts similarity index 98% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/utils.ts rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/utils.ts index 7144b0ad4902e..7c4f3b3ec8843 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/utils.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/utils.ts @@ -44,7 +44,7 @@ const makeSerie = ( }; const isAxisHorizontal = (position: Positions) => - [Positions.TOP, Positions.BOTTOM].includes(position); + [Positions.TOP, Positions.BOTTOM].includes(position as any); const RADIX = 10; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axes_panel.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx similarity index 99% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axes_panel.test.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx index 080c64db7ff85..7524c7a13435b 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axes_panel.test.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.test.tsx @@ -25,6 +25,8 @@ import { Positions, getScaleTypes, getAxisModes, getPositions } from '../../../u import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { valueAxis, seriesParam } from './mocks'; +jest.mock('ui/new_platform'); + const positions = getPositions(); const axisModes = getAxisModes(); const scaleTypes = getScaleTypes(); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axes_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axes_panel.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axes_panel.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axis_options.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx similarity index 99% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axis_options.test.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx index 8cb476508c78b..bd512e9365783 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axis_options.test.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.test.tsx @@ -32,6 +32,8 @@ import { } from '../../../utils/collections'; import { valueAxis, categoryAxis } from './mocks'; +jest.mock('ui/new_platform'); + const POSITION = 'position'; const positions = getPositions(); const axisModes = getAxisModes(); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axis_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx similarity index 99% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axis_options.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx index 243950b762390..b4ea4cb42ee60 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/value_axis_options.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/value_axis_options.tsx @@ -100,7 +100,7 @@ function ValueAxisOptions(props: ValueAxisOptionsParams) { if (isCategoryAxisHorizontal) { return isAxisHorizontal(position); } - return [Positions.LEFT, Positions.RIGHT].includes(position); + return [Positions.LEFT, Positions.RIGHT].includes(position as any); }, [isCategoryAxisHorizontal] ); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/y_extents.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx similarity index 99% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/y_extents.test.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx index 2df17b6e34985..17c47b35b20dc 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/y_extents.test.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.test.tsx @@ -23,6 +23,8 @@ import { YExtents, YExtentsProps } from './y_extents'; import { ScaleTypes } from '../../../utils/collections'; import { NumberInputOption } from '../../common'; +jest.mock('ui/new_platform'); + describe('YExtents component', () => { let setMultipleValidity: jest.Mock; let setScale: jest.Mock; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/y_extents.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/metrics_axes/y_extents.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/y_extents.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/pie.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/pie.tsx similarity index 98% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/pie.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/pie.tsx index 53dde185ec09f..056eb70cb256b 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/pie.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/pie.tsx @@ -17,11 +17,12 @@ * under the License. */ import React from 'react'; + import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; +import { VisOptionsProps } from '../../legacy_imports'; import { BasicOptions, TruncateLabelsOption, SwitchOption } from '../common'; import { PieVisParams } from '../../pie'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/grid_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx similarity index 98% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/grid_panel.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx index 63b2449b823d5..bdb4d3f39c12b 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/grid_panel.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx @@ -17,11 +17,12 @@ * under the License. */ import React, { useMemo, useEffect, useCallback } from 'react'; + import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; +import { VisOptionsProps } from '../../../legacy_imports'; import { SelectOption, SwitchOption } from '../../common'; import { BasicVislibParams, ValueAxis } from '../../../types'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/index.ts b/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/index.ts similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/index.ts rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/index.ts diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/point_series.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/point_series.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/point_series.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/point_series.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/threshold_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/threshold_panel.tsx similarity index 100% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/components/options/point_series/threshold_panel.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/threshold_panel.tsx diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/gauge.js b/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts similarity index 77% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/gauge.js rename to src/legacy/core_plugins/vis_type_vislib/public/gauge.ts index a472d20e07224..5dcc8ad16918d 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/gauge.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts @@ -18,14 +18,43 @@ */ import { i18n } from '@kbn/i18n'; -import { Schemas } from 'ui/vis/editors/default/schemas'; -import { AggGroupNames } from 'ui/vis/editors/default'; -import { ColorSchemas } from 'ui/vislib/components/color/colormaps'; + +import { Schemas, AggGroupNames, ColorSchemas, RangeValues } from './legacy_imports'; import { GaugeOptions } from './components/options'; import { getGaugeCollections, Alignments, ColorModes, GaugeTypes } from './utils/collections'; -import { vislibVisController } from './controller'; +import { createVislibVisController } from './vis_controller'; +import { ColorSchemaVislibParams, Labels, Style } from './types'; +import { KbnVislibVisTypesDependencies } from './plugin'; + +export interface Gauge extends ColorSchemaVislibParams { + backStyle: 'Full'; + gaugeStyle: 'Full'; + orientation: 'vertical'; + type: 'meter'; + alignment: Alignments; + colorsRange: RangeValues[]; + extendRange: boolean; + gaugeType: GaugeTypes; + labels: Labels; + percentageMode: boolean; + outline?: boolean; + scale: { + show: boolean; + labels: false; + color: 'rgba(105,112,125,0.2)'; + }; + style: Style; +} + +export interface GaugeVisParams { + type: 'gauge'; + addTooltip: boolean; + addLegend: boolean; + isDisplayWarning: boolean; + gauge: Gauge; +} -export const gaugeDefinition = { +export const createGaugeVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({ name: 'gauge', title: i18n.translate('kbnVislibVisTypes.gauge.gaugeTitle', { defaultMessage: 'Gauge' }), icon: 'visGauge', @@ -79,7 +108,7 @@ export const gaugeDefinition = { }, }, }, - visualization: vislibVisController, + visualization: createVislibVisController(deps), editorConfig: { collections: getGaugeCollections(), optionsTemplate: GaugeOptions, @@ -115,4 +144,4 @@ export const gaugeDefinition = { ]), }, useCustomNoDataScreen: true, -}; +}); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/goal.js b/src/legacy/core_plugins/vis_type_vislib/public/goal.ts similarity index 90% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/goal.js rename to src/legacy/core_plugins/vis_type_vislib/public/goal.ts index 71fcd6593e72c..302d5f6393ef9 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/goal.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/goal.ts @@ -18,21 +18,21 @@ */ import { i18n } from '@kbn/i18n'; -import { Schemas } from 'ui/vis/editors/default/schemas'; -import { AggGroupNames } from 'ui/vis/editors/default'; -import { ColorSchemas } from 'ui/vislib/components/color/colormaps'; + +import { Schemas, AggGroupNames, ColorSchemas } from './legacy_imports'; import { GaugeOptions } from './components/options'; import { getGaugeCollections, GaugeTypes, ColorModes } from './utils/collections'; -import { vislibVisController } from './controller'; +import { createVislibVisController } from './vis_controller'; +import { KbnVislibVisTypesDependencies } from './plugin'; -export const goalDefinition = { +export const createGoalVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({ name: 'goal', title: i18n.translate('kbnVislibVisTypes.goal.goalTitle', { defaultMessage: 'Goal' }), icon: 'visGoal', description: i18n.translate('kbnVislibVisTypes.goal.goalDescription', { defaultMessage: 'A goal chart indicates how close you are to your final goal.', }), - visualization: vislibVisController, + visualization: createVislibVisController(deps), visConfig: { defaults: { addTooltip: true, @@ -108,4 +108,4 @@ export const goalDefinition = { ]), }, useCustomNoDataScreen: true, -}; +}); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/heatmap.js b/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts similarity index 81% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/heatmap.js rename to src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts index 292e108053598..eb5f84b409838 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/heatmap.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts @@ -18,21 +18,35 @@ */ import { i18n } from '@kbn/i18n'; -import { Schemas } from 'ui/vis/editors/default/schemas'; -import { AggGroupNames } from 'ui/vis/editors/default'; -import { ColorSchemas } from 'ui/vislib/components/color/colormaps'; + +import { Schemas, AggGroupNames, ColorSchemas, RangeValues } from './legacy_imports'; import { AxisTypes, getHeatmapCollections, Positions, ScaleTypes } from './utils/collections'; import { HeatmapOptions } from './components/options'; -import { vislibVisController } from './controller'; +import { createVislibVisController } from './vis_controller'; +import { TimeMarker } from './vislib/visualizations/time_marker'; +import { CommonVislibParams, ColorSchemaVislibParams, ValueAxis } from './types'; +import { KbnVislibVisTypesDependencies } from './plugin'; + +export interface HeatmapVisParams extends CommonVislibParams, ColorSchemaVislibParams { + type: 'heatmap'; + addLegend: boolean; + enableHover: boolean; + colorsNumber: number | ''; + colorsRange: RangeValues[]; + valueAxes: ValueAxis[]; + setColorRange: boolean; + percentageMode: boolean; + times: TimeMarker[]; +} -export const heatmapDefinition = { +export const createHeatmapVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({ name: 'heatmap', title: i18n.translate('kbnVislibVisTypes.heatmap.heatmapTitle', { defaultMessage: 'Heat Map' }), icon: 'visHeatmap', description: i18n.translate('kbnVislibVisTypes.heatmap.heatmapDescription', { defaultMessage: 'Shade cells within a matrix', }), - visualization: vislibVisController, + visualization: createVislibVisController(deps), visConfig: { defaults: { type: 'heatmap', @@ -122,4 +136,4 @@ export const heatmapDefinition = { }, ]), }, -}; +}); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/histogram.js b/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts similarity index 92% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/histogram.js rename to src/legacy/core_plugins/vis_type_vislib/public/histogram.ts index bc017b5a1a871..f92875a62cfd7 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/histogram.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts @@ -18,8 +18,13 @@ */ import { i18n } from '@kbn/i18n'; -import { Schemas } from 'ui/vis/editors/default/schemas'; -import { AggGroupNames } from 'ui/vis/editors/default'; +// @ts-ignore +import { palettes } from '@elastic/eui/lib/services'; +// @ts-ignore +import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; + +import { Schemas, AggGroupNames } from './legacy_imports'; + import { Positions, ChartTypes, @@ -32,10 +37,10 @@ import { getConfigCollections, } from './utils/collections'; import { getAreaOptionTabs, countLabel } from './utils/common_config'; -import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; -import { vislibVisController } from './controller'; +import { createVislibVisController } from './vis_controller'; +import { KbnVislibVisTypesDependencies } from './plugin'; -export const histogramDefinition = { +export const createHistogramVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({ name: 'histogram', title: i18n.translate('kbnVislibVisTypes.histogram.histogramTitle', { defaultMessage: 'Vertical Bar', @@ -44,7 +49,7 @@ export const histogramDefinition = { description: i18n.translate('kbnVislibVisTypes.histogram.histogramDescription', { defaultMessage: 'Assign a continuous variable to each axis', }), - visualization: vislibVisController, + visualization: createVislibVisController(deps), visConfig: { defaults: { type: 'histogram', @@ -183,4 +188,4 @@ export const histogramDefinition = { }, ]), }, -}; +}); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/horizontal_bar.js b/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts similarity index 92% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/horizontal_bar.js rename to src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts index ee3570314618a..ada0c6b44ff70 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/horizontal_bar.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts @@ -18,8 +18,13 @@ */ import { i18n } from '@kbn/i18n'; -import { Schemas } from 'ui/vis/editors/default/schemas'; -import { AggGroupNames } from 'ui/vis/editors/default'; +// @ts-ignore +import { palettes } from '@elastic/eui/lib/services'; +// @ts-ignore +import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; + +import { Schemas, AggGroupNames } from './legacy_imports'; + import { Positions, ChartTypes, @@ -32,10 +37,10 @@ import { getConfigCollections, } from './utils/collections'; import { getAreaOptionTabs, countLabel } from './utils/common_config'; -import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; -import { vislibVisController } from './controller'; +import { createVislibVisController } from './vis_controller'; +import { KbnVislibVisTypesDependencies } from './plugin'; -export const horizontalBarDefinition = { +export const createHorizontalBarVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({ name: 'horizontal_bar', title: i18n.translate('kbnVislibVisTypes.horizontalBar.horizontalBarTitle', { defaultMessage: 'Horizontal Bar', @@ -44,7 +49,7 @@ export const horizontalBarDefinition = { description: i18n.translate('kbnVislibVisTypes.horizontalBar.horizontalBarDescription', { defaultMessage: 'Assign a continuous variable to each axis', }), - visualization: vislibVisController, + visualization: createVislibVisController(deps), visConfig: { defaults: { type: 'histogram', @@ -182,4 +187,4 @@ export const horizontalBarDefinition = { }, ]), }, -}; +}); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/pie.d.ts b/src/legacy/core_plugins/vis_type_vislib/public/index.ts similarity index 74% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/pie.d.ts rename to src/legacy/core_plugins/vis_type_vislib/public/index.ts index bf55abfe8161a..5abe79daf11fa 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/pie.d.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/index.ts @@ -17,16 +17,9 @@ * under the License. */ -import { CommonVislibParams } from './types'; +import { PluginInitializerContext } from '../../../../core/public'; +import { KbnVislibVisTypesPlugin as Plugin } from './plugin'; -export interface PieVisParams extends CommonVislibParams { - type: 'pie'; - addLegend: boolean; - isDonut: boolean; - labels: { - show: boolean; - values: boolean; - last_level: boolean; - truncate: number | null; - }; +export function plugin(initializerContext: PluginInitializerContext) { + return new Plugin(initializerContext); } diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts new file mode 100644 index 0000000000000..3d4cf55adc5e0 --- /dev/null +++ b/src/legacy/core_plugins/vis_type_vislib/public/legacy.ts @@ -0,0 +1,74 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { npSetup, npStart } from 'ui/new_platform'; +import { PluginInitializerContext } from 'kibana/public'; + +/* eslint-disable prettier/prettier */ +import { + initializeHierarchicalTooltipFormatter, + getHierarchicalTooltipFormatter, + // @ts-ignore +} from 'ui/vis/components/tooltip/_hierarchical_tooltip_formatter'; +import { + initializePointSeriesTooltipFormatter, + getPointSeriesTooltipFormatter, + // @ts-ignore +} from 'ui/vis/components/tooltip/_pointseries_tooltip_formatter'; +import { + vislibSeriesResponseHandlerProvider, + vislibSlicesResponseHandlerProvider, + // @ts-ignore +} from 'ui/vis/response_handlers/vislib'; +// @ts-ignore +import { vislibColor } from 'ui/vis/components/color/color'; + +import { plugin } from '.'; +import { + KbnVislibVisTypesPluginSetupDependencies, + KbnVislibVisTypesPluginStartDependencies, +} from './plugin'; +import { + setup as visualizationsSetup, + start as visualizationsStart, +} from '../../visualizations/public/np_ready/public/legacy'; + +const setupPlugins: Readonly = { + expressions: npSetup.plugins.expressions, + visualizations: visualizationsSetup, + __LEGACY: { + initializeHierarchicalTooltipFormatter, + getHierarchicalTooltipFormatter, + initializePointSeriesTooltipFormatter, + getPointSeriesTooltipFormatter, + vislibSeriesResponseHandlerProvider, + vislibSlicesResponseHandlerProvider, + vislibColor, + }, +}; + +const startPlugins: Readonly = { + expressions: npStart.plugins.expressions, + visualizations: visualizationsStart, +}; + +const pluginInstance = plugin({} as PluginInitializerContext); + +export const setup = pluginInstance.setup(npSetup.core, setupPlugins); +export const start = pluginInstance.start(npStart.core, startPlugins); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts new file mode 100644 index 0000000000000..2970942f221e8 --- /dev/null +++ b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts @@ -0,0 +1,35 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { AggGroupNames, VisOptionsProps } from 'ui/vis/editors/default'; +export { Schemas } from 'ui/vis/editors/default/schemas'; +export { RangeValues, RangesParamEditor } from 'ui/vis/editors/default/controls/ranges'; +export { ColorSchema, ColorSchemas, colorSchemas, getHeatmapColors } from 'ui/color_maps'; +export { AggConfig, Vis, VisParams } from 'ui/vis'; +export { AggType } from 'ui/agg_types'; +export { CUSTOM_LEGEND_VIS_TYPES, VisLegend } from 'ui/vis/vis_types/vislib_vis_legend'; +// @ts-ignore +export { Tooltip } from 'ui/vis/components/tooltip'; +// @ts-ignore +export { SimpleEmitter } from 'ui/utils/simple_emitter'; +// @ts-ignore +export { Binder } from 'ui/binder'; +export { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities'; +// @ts-ignore +export { tabifyAggResponse } from 'ui/agg_response/tabify'; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/line.js b/src/legacy/core_plugins/vis_type_vislib/public/line.ts similarity index 92% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/line.js rename to src/legacy/core_plugins/vis_type_vislib/public/line.ts index d6d075f452fed..35a059fadddcb 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/line.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/line.ts @@ -18,8 +18,12 @@ */ import { i18n } from '@kbn/i18n'; -import { Schemas } from 'ui/vis/editors/default/schemas'; -import { AggGroupNames } from 'ui/vis/editors/default'; +// @ts-ignore +import { palettes } from '@elastic/eui/lib/services'; +// @ts-ignore +import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; + +import { Schemas, AggGroupNames } from './legacy_imports'; import { Positions, ChartTypes, @@ -32,18 +36,18 @@ import { InterpolationModes, getConfigCollections, } from './utils/collections'; -import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { getAreaOptionTabs, countLabel } from './utils/common_config'; -import { vislibVisController } from './controller'; +import { createVislibVisController } from './vis_controller'; +import { KbnVislibVisTypesDependencies } from './plugin'; -export const lineDefinition = { +export const createLineVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({ name: 'line', title: i18n.translate('kbnVislibVisTypes.line.lineTitle', { defaultMessage: 'Line' }), icon: 'visLine', description: i18n.translate('kbnVislibVisTypes.line.lineDescription', { defaultMessage: 'Emphasize trends', }), - visualization: vislibVisController, + visualization: createVislibVisController(deps), visConfig: { defaults: { type: 'line', @@ -175,4 +179,4 @@ export const lineDefinition = { }, ]), }, -}; +}); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/pie.js b/src/legacy/core_plugins/vis_type_vislib/public/pie.ts similarity index 81% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/pie.js rename to src/legacy/core_plugins/vis_type_vislib/public/pie.ts index 1cef3114f047d..32307b7a117a1 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/pie.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/pie.ts @@ -18,20 +18,34 @@ */ import { i18n } from '@kbn/i18n'; -import { Schemas } from 'ui/vis/editors/default/schemas'; -import { AggGroupNames } from 'ui/vis/editors/default'; + +import { Schemas, AggGroupNames } from './legacy_imports'; import { PieOptions } from './components/options'; import { getPositions, Positions } from './utils/collections'; -import { vislibVisController } from './controller'; +import { createVislibVisController } from './vis_controller'; +import { CommonVislibParams } from './types'; +import { KbnVislibVisTypesDependencies } from './plugin'; + +export interface PieVisParams extends CommonVislibParams { + type: 'pie'; + addLegend: boolean; + isDonut: boolean; + labels: { + show: boolean; + values: boolean; + last_level: boolean; + truncate: number | null; + }; +} -export const pieDefinition = { +export const createPieVisTypeDefinition = (deps: KbnVislibVisTypesDependencies) => ({ name: 'pie', title: i18n.translate('kbnVislibVisTypes.pie.pieTitle', { defaultMessage: 'Pie' }), icon: 'visPie', description: i18n.translate('kbnVislibVisTypes.pie.pieDescription', { defaultMessage: 'Compare parts of a whole', }), - visualization: vislibVisController, + visualization: createVislibVisController(deps), visConfig: { defaults: { type: 'pie', @@ -89,4 +103,4 @@ export const pieDefinition = { }, hierarchicalData: true, responseHandler: 'vislib_slices', -}; +}); diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/pie_fn.test.js b/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.test.ts similarity index 92% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/pie_fn.test.js rename to src/legacy/core_plugins/vis_type_vislib/public/pie_fn.test.ts index 4d932e6e9518e..786de0cc79b86 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/pie_fn.test.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.test.ts @@ -19,10 +19,14 @@ // eslint-disable-next-line import { functionWrapper } from '../../../../plugins/expressions/public/functions/tests/utils'; -import { kibanaPie } from './pie_fn'; +import { createPieVisFn } from './pie_fn'; +import { KbnVislibVisTypesDependencies } from './plugin'; jest.mock('ui/new_platform'); +const deps: KbnVislibVisTypesDependencies = { + vislibSlicesResponseHandlerProvider: () => ({ handler: mockResponseHandler }), +} as any; const mockResponseHandler = jest.fn().mockReturnValue( Promise.resolve({ hits: 1, @@ -39,20 +43,15 @@ const mockResponseHandler = jest.fn().mockReturnValue( }, }) ); -jest.mock('ui/vis/response_handlers/vislib', () => ({ - vislibSlicesResponseHandlerProvider: () => ({ handler: mockResponseHandler }), -})); describe('interpreter/functions#pie', () => { - const fn = functionWrapper(kibanaPie); + const fn = functionWrapper(createPieVisFn(deps)); const context = { type: 'kibana_datatable', rows: [{ 'col-0-1': 0 }], columns: [{ id: 'col-0-1', name: 'Count' }], }; const visConfig = { - addTooltip: true, - addLegend: false, type: 'pie', addTooltip: true, addLegend: true, diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/pie_fn.js b/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.ts similarity index 68% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/pie_fn.js rename to src/legacy/core_plugins/vis_type_vislib/public/pie_fn.ts index f66fd53e59efe..4b536caedb121 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/pie_fn.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/pie_fn.ts @@ -17,11 +17,37 @@ * under the License. */ -import { functionsRegistry } from 'plugins/interpreter/registries'; -import { vislibSlicesResponseHandlerProvider as vislibSlicesResponseHandler } from 'ui/vis/response_handlers/vislib'; import { i18n } from '@kbn/i18n'; -export const kibanaPie = () => ({ +import { + ExpressionFunction, + KibanaDatatable, + Render, +} from '../../../../plugins/expressions/public'; +import { KbnVislibVisTypesDependencies } from './plugin'; + +const name = 'kibana_pie'; + +type Context = KibanaDatatable; + +interface Arguments { + visConfig: string; +} + +type VisParams = Required; + +interface RenderValue { + visConfig: VisParams; +} + +type Return = Promise>; + +export const createPieVisFn = (deps: KbnVislibVisTypesDependencies) => (): ExpressionFunction< + typeof name, + Context, + Arguments, + Return +> => ({ name: 'kibana_pie', type: 'render', context: { @@ -32,14 +58,15 @@ export const kibanaPie = () => ({ }), args: { visConfig: { - types: ['string', 'null'], + types: ['string'], default: '"{}"', + help: '', }, }, async fn(context, args) { const visConfig = JSON.parse(args.visConfig); - const responseHandler = vislibSlicesResponseHandler().handler; + const responseHandler = deps.vislibSlicesResponseHandlerProvider().handler; const convertedData = await responseHandler(context, visConfig.dimensions); return { @@ -56,5 +83,3 @@ export const kibanaPie = () => ({ }; }, }); - -functionsRegistry.register(kibanaPie); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts b/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts new file mode 100644 index 0000000000000..a2e8512b2201b --- /dev/null +++ b/src/legacy/core_plugins/vis_type_vislib/public/plugin.ts @@ -0,0 +1,106 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { + CoreSetup, + CoreStart, + Plugin, + IUiSettingsClient, + PluginInitializerContext, +} from 'kibana/public'; + +import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; +import { VisualizationsSetup, VisualizationsStart } from '../../visualizations/public'; +import { createKbnVislibVisTypesFn } from './vis_type_vislib_vis_fn'; +import { createPieVisFn } from './pie_fn'; +import { + createHistogramVisTypeDefinition, + createLineVisTypeDefinition, + createPieVisTypeDefinition, + createAreaVisTypeDefinition, + createHeatmapVisTypeDefinition, + createHorizontalBarVisTypeDefinition, + createGaugeVisTypeDefinition, + createGoalVisTypeDefinition, +} from './vis_type_vislib_vis_types'; + +type ResponseHandlerProvider = () => { + name: string; + handler: (response: any, dimensions: any) => Promise; +}; +type KbnVislibVisTypesCoreSetup = CoreSetup; + +export interface LegacyDependencies { + initializeHierarchicalTooltipFormatter: () => Promise; + getHierarchicalTooltipFormatter: () => Promise; + initializePointSeriesTooltipFormatter: () => void; + getPointSeriesTooltipFormatter: () => void; + vislibSeriesResponseHandlerProvider: ResponseHandlerProvider; + vislibSlicesResponseHandlerProvider: ResponseHandlerProvider; + vislibColor: (colors: Array, mappings: any) => (value: any) => any; +} + +export type KbnVislibVisTypesDependencies = LegacyDependencies & { + uiSettings: IUiSettingsClient; +}; + +/** @internal */ +export interface KbnVislibVisTypesPluginSetupDependencies { + expressions: ReturnType; + visualizations: VisualizationsSetup; + __LEGACY: LegacyDependencies; +} + +/** @internal */ +export interface KbnVislibVisTypesPluginStartDependencies { + expressions: ReturnType; + visualizations: VisualizationsStart; +} + +/** @internal */ +export class KbnVislibVisTypesPlugin implements Plugin, void> { + constructor(public initializerContext: PluginInitializerContext) {} + + public async setup( + core: KbnVislibVisTypesCoreSetup, + { expressions, visualizations, __LEGACY }: KbnVislibVisTypesPluginSetupDependencies + ) { + const visualizationDependencies: Readonly = { + ...__LEGACY, + uiSettings: core.uiSettings, + }; + + expressions.registerFunction(createKbnVislibVisTypesFn(visualizationDependencies)); + expressions.registerFunction(createPieVisFn(visualizationDependencies)); + + [ + createHistogramVisTypeDefinition, + createLineVisTypeDefinition, + createPieVisTypeDefinition, + createAreaVisTypeDefinition, + createHeatmapVisTypeDefinition, + createHorizontalBarVisTypeDefinition, + createGaugeVisTypeDefinition, + createGoalVisTypeDefinition, + ].forEach(vis => visualizations.types.createBaseVisualization(vis(visualizationDependencies))); + } + + public start(core: CoreStart, deps: KbnVislibVisTypesPluginStartDependencies) { + // nothing to do here + } +} diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/types.ts b/src/legacy/core_plugins/vis_type_vislib/public/types.ts similarity index 95% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/types.ts rename to src/legacy/core_plugins/vis_type_vislib/public/types.ts index b023eb54f7295..b6928be89f648 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/types.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/types.ts @@ -17,8 +17,8 @@ * under the License. */ -import { ColorSchemas } from 'ui/vislib/components/color/colormaps'; -import { TimeMarker } from 'ui/vislib/visualizations/time_marker'; +import { ColorSchemas } from './legacy_imports'; +import { TimeMarker } from './vislib/visualizations/time_marker'; import { Positions, ChartModes, diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/utils/collections.ts b/src/legacy/core_plugins/vis_type_vislib/public/utils/collections.ts similarity index 73% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/utils/collections.ts rename to src/legacy/core_plugins/vis_type_vislib/public/utils/collections.ts index 6fe30483a32e8..810ddeea73834 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/utils/collections.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/utils/collections.ts @@ -18,14 +18,16 @@ */ import { i18n } from '@kbn/i18n'; -import { colorSchemas } from 'ui/vislib/components/color/colormaps'; +import { $Values } from '@kbn/utility-types'; +import { colorSchemas } from '../legacy_imports'; -export enum Positions { - RIGHT = 'right', - LEFT = 'left', - TOP = 'top', - BOTTOM = 'bottom', -} +export const Positions = Object.freeze({ + RIGHT: 'right' as 'right', + LEFT: 'left' as 'left', + TOP: 'top' as 'top', + BOTTOM: 'bottom' as 'bottom', +}); +export type Positions = $Values; const getPositions = () => [ { @@ -54,11 +56,12 @@ const getPositions = () => [ }, ]; -export enum ChartTypes { - LINE = 'line', - AREA = 'area', - HISTOGRAM = 'histogram', -} +export const ChartTypes = Object.freeze({ + LINE: 'line' as 'line', + AREA: 'area' as 'area', + HISTOGRAM: 'histogram' as 'histogram', +}); +export type ChartTypes = $Values; const getChartTypes = () => [ { @@ -81,10 +84,11 @@ const getChartTypes = () => [ }, ]; -export enum ChartModes { - NORMAL = 'normal', - STACKED = 'stacked', -} +export const ChartModes = Object.freeze({ + NORMAL: 'normal' as 'normal', + STACKED: 'stacked' as 'stacked', +}); +export type ChartModes = $Values; const getChartModes = () => [ { @@ -101,11 +105,12 @@ const getChartModes = () => [ }, ]; -export enum InterpolationModes { - LINEAR = 'linear', - CARDINAL = 'cardinal', - STEP_AFTER = 'step-after', -} +export const InterpolationModes = Object.freeze({ + LINEAR: 'linear' as 'linear', + CARDINAL: 'cardinal' as 'cardinal', + STEP_AFTER: 'step-after' as 'step-after', +}); +export type InterpolationModes = $Values; const getInterpolationModes = () => [ { @@ -128,16 +133,18 @@ const getInterpolationModes = () => [ }, ]; -export enum AxisTypes { - CATEGORY = 'category', - VALUE = 'value', -} +export const AxisTypes = Object.freeze({ + CATEGORY: 'category' as 'category', + VALUE: 'value' as 'value', +}); +export type AxisTypes = $Values; -export enum ScaleTypes { - LINEAR = 'linear', - LOG = 'log', - SQUARE_ROOT = 'square root', -} +export const ScaleTypes = Object.freeze({ + LINEAR: 'linear' as 'linear', + LOG: 'log' as 'log', + SQUARE_ROOT: 'square root' as 'square root', +}); +export type ScaleTypes = $Values; const getScaleTypes = () => [ { @@ -160,12 +167,13 @@ const getScaleTypes = () => [ }, ]; -export enum AxisModes { - NORMAL = 'normal', - PERCENTAGE = 'percentage', - WIGGLE = 'wiggle', - SILHOUETTE = 'silhouette', -} +export const AxisModes = Object.freeze({ + NORMAL: 'normal' as 'normal', + PERCENTAGE: 'percentage' as 'percentage', + WIGGLE: 'wiggle' as 'wiggle', + SILHOUETTE: 'silhouette' as 'silhouette', +}); +export type AxisModes = $Values; const getAxisModes = () => [ { @@ -194,17 +202,19 @@ const getAxisModes = () => [ }, ]; -export enum Rotates { - HORIZONTAL = 0, - VERTICAL = 90, - ANGLED = 75, -} +export const Rotates = Object.freeze({ + HORIZONTAL: 0, + VERTICAL: 90, + ANGLED: 75, +}); +export type Rotates = $Values; -export enum ThresholdLineStyles { - FULL = 'full', - DASHED = 'dashed', - DOT_DASHED = 'dot-dashed', -} +export const ThresholdLineStyles = Object.freeze({ + FULL: 'full' as 'full', + DASHED: 'dashed' as 'dashed', + DOT_DASHED: 'dot-dashed' as 'dot-dashed', +}); +export type ThresholdLineStyles = $Values; const getThresholdLineStyles = () => [ { @@ -248,16 +258,18 @@ const getRotateOptions = () => [ }, ]; -export enum GaugeTypes { - ARC = 'Arc', - CIRCLE = 'Circle', -} +export const GaugeTypes = Object.freeze({ + ARC: 'Arc' as 'Arc', + CIRCLE: 'Circle' as 'Circle', +}); +export type GaugeTypes = $Values; -export enum ColorModes { - BACKGROUND = 'Background', - LABELS = 'Labels', - NONE = 'None', -} +export const ColorModes = Object.freeze({ + BACKGROUND: 'Background' as 'Background', + LABELS: 'Labels' as 'Labels', + NONE: 'None' as 'None', +}); +export type ColorModes = $Values; const getGaugeTypes = () => [ { @@ -274,11 +286,12 @@ const getGaugeTypes = () => [ }, ]; -export enum Alignments { - AUTOMATIC = 'automatic', - HORIZONTAL = 'horizontal', - VERTICAL = 'vertical', -} +export const Alignments = Object.freeze({ + AUTOMATIC: 'automatic' as 'automatic', + HORIZONTAL: 'horizontal' as 'horizontal', + VERTICAL: 'vertical' as 'vertical', +}); +export type Alignments = $Values; const getAlignments = () => [ { diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/utils/common_config.tsx b/src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx similarity index 96% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/utils/common_config.tsx rename to src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx index 7e4140606762f..adb93ca8011b2 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/utils/common_config.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx @@ -18,8 +18,9 @@ */ import React from 'react'; -import { VisOptionsProps } from 'ui/vis/editors/default'; import { i18n } from '@kbn/i18n'; + +import { VisOptionsProps } from '../legacy_imports'; import { PointSeriesOptions, MetricsAxisOptions } from '../components/options'; import { ValidationWrapper } from '../components/common'; import { BasicVislibParams } from '../types'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx b/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx new file mode 100644 index 0000000000000..cff9a0a2e8551 --- /dev/null +++ b/src/legacy/core_plugins/vis_type_vislib/public/vis_controller.tsx @@ -0,0 +1,147 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import $ from 'jquery'; +import React, { RefObject } from 'react'; + +import { CUSTOM_LEGEND_VIS_TYPES, VisLegend, Vis, VisParams } from './legacy_imports'; +// @ts-ignore +import { Vis as Vislib } from './vislib/vis'; +import { Positions } from './utils/collections'; +import { KbnVislibVisTypesDependencies } from './plugin'; +import { mountReactNode } from '../../../../core/public/utils'; + +const legendClassName = { + top: 'visLib--legend-top', + bottom: 'visLib--legend-bottom', + left: 'visLib--legend-left', + right: 'visLib--legend-right', +}; + +export const createVislibVisController = (deps: KbnVislibVisTypesDependencies) => { + return class VislibVisController { + unmount: (() => void) | null = null; + visParams?: VisParams; + legendRef: RefObject; + container: HTMLDivElement; + chartEl: HTMLDivElement; + legendEl: HTMLDivElement; + vislibVis: any; + + constructor(public el: Element, public vis: Vis) { + this.el = el; + this.vis = vis; + this.unmount = null; + this.legendRef = React.createRef(); + + // vis mount point + this.container = document.createElement('div'); + this.container.className = 'visLib'; + this.el.appendChild(this.container); + + // chart mount point + this.chartEl = document.createElement('div'); + this.chartEl.className = 'visLib__chart'; + this.container.appendChild(this.chartEl); + + // legend mount point + this.legendEl = document.createElement('div'); + this.legendEl.className = 'visLib__legend'; + this.container.appendChild(this.legendEl); + } + + render(esResponse: any, visParams: VisParams) { + if (this.vislibVis) { + this.destroy(); + } + + return new Promise(async resolve => { + if (this.el.clientWidth === 0 || this.el.clientHeight === 0) { + return resolve(); + } + + await deps.initializeHierarchicalTooltipFormatter(); + await deps.initializePointSeriesTooltipFormatter(); + + this.vislibVis = new Vislib(this.chartEl, visParams, deps); + this.vislibVis.on('brush', this.vis.API.events.brush); + this.vislibVis.on('click', this.vis.API.events.filter); + this.vislibVis.on('renderComplete', resolve); + + this.vislibVis.initVisConfig(esResponse, this.vis.getUiState()); + + if (visParams.addLegend) { + $(this.container) + .attr('class', (i, cls) => { + return cls.replace(/visLib--legend-\S+/g, ''); + }) + .addClass((legendClassName as any)[visParams.legendPosition]); + + this.mountLegend(esResponse, visParams.legendPosition); + } + + this.vislibVis.render(esResponse, this.vis.getUiState()); + + // refreshing the legend after the chart is rendered. + // this is necessary because some visualizations + // provide data necessary for the legend only after a render cycle. + if ( + visParams.addLegend && + CUSTOM_LEGEND_VIS_TYPES.includes(this.vislibVis.visConfigArgs.type) + ) { + this.unmountLegend(); + this.mountLegend(esResponse, visParams.legendPosition); + this.vislibVis.render(esResponse, this.vis.getUiState()); + } + }); + } + + mountLegend(visData: any, position: Positions) { + this.unmount = mountReactNode( + + )(this.legendEl); + } + + unmountLegend() { + if (this.unmount) { + this.unmount(); + } + } + + destroy() { + if (this.unmount) { + this.unmount(); + } + + if (this.vislibVis) { + this.vislibVis.off('brush', this.vis.API.events.brush); + this.vislibVis.off('click', this.vis.API.events.filter); + this.vislibVis.destroy(); + delete this.vislibVis; + } + } + }; +}; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/public/vislib_fn.js b/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts similarity index 68% rename from src/legacy/core_plugins/kbn_vislib_vis_types/public/vislib_fn.js rename to src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts index 85ea5994548d6..0a685cd70e089 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/public/vislib_fn.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts @@ -17,11 +17,36 @@ * under the License. */ -import { functionsRegistry } from 'plugins/interpreter/registries'; import { i18n } from '@kbn/i18n'; -import { vislibSeriesResponseHandlerProvider } from 'ui/vis/response_handlers/vislib'; -export const vislib = () => ({ +import { + ExpressionFunction, + KibanaDatatable, + Render, +} from '../../../../plugins/expressions/public'; +import { KbnVislibVisTypesDependencies } from './plugin'; + +const name = 'vislib'; + +type Context = KibanaDatatable; + +interface Arguments { + type: string; + visConfig: string; +} + +type VisParams = Required; + +interface RenderValue { + visType: string; + visConfig: VisParams; +} + +type Return = Promise>; + +export const createKbnVislibVisTypesFn = ( + deps: KbnVislibVisTypesDependencies +) => (): ExpressionFunction => ({ name: 'vislib', type: 'render', context: { @@ -34,14 +59,16 @@ export const vislib = () => ({ type: { types: ['string'], default: '""', + help: 'vislib vis type', }, visConfig: { - types: ['string', 'null'], + types: ['string'], default: '"{}"', + help: '', }, }, async fn(context, args) { - const responseHandler = vislibSeriesResponseHandlerProvider().handler; + const responseHandler = deps.vislibSeriesResponseHandlerProvider().handler; const visConfigParams = JSON.parse(args.visConfig); const convertedData = await responseHandler(context, visConfigParams.dimensions); @@ -60,5 +87,3 @@ export const vislib = () => ({ }; }, }); - -functionsRegistry.register(vislib); diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts b/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts new file mode 100644 index 0000000000000..f44d503895483 --- /dev/null +++ b/src/legacy/core_plugins/vis_type_vislib/public/vis_type_vislib_vis_types.ts @@ -0,0 +1,27 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { createHistogramVisTypeDefinition } from './histogram'; +export { createLineVisTypeDefinition } from './line'; +export { createPieVisTypeDefinition } from './pie'; +export { createAreaVisTypeDefinition } from './area'; +export { createHeatmapVisTypeDefinition } from './heatmap'; +export { createHorizontalBarVisTypeDefinition } from './horizontal_bar'; +export { createGaugeVisTypeDefinition } from './gauge'; +export { createGoalVisTypeDefinition } from './goal'; diff --git a/src/legacy/ui/public/vislib/VISLIB.md b/src/legacy/core_plugins/vis_type_vislib/public/vislib/VISLIB.md similarity index 100% rename from src/legacy/ui/public/vislib/VISLIB.md rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/VISLIB.md diff --git a/src/legacy/ui/public/vislib/__tests__/components/heatmap_color.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/heatmap_color.js similarity index 97% rename from src/legacy/ui/public/vislib/__tests__/components/heatmap_color.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/heatmap_color.js index 1cafbadcff655..36c5b60abf5c6 100644 --- a/src/legacy/ui/public/vislib/__tests__/components/heatmap_color.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/heatmap_color.js @@ -19,7 +19,8 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import { getHeatmapColors } from '../../components/color/heatmap_color'; + +import { getHeatmapColors } from '../../../legacy_imports'; describe('Vislib Heatmap Color Module Test Suite', function() { const emptyObject = {}; diff --git a/src/legacy/ui/public/vislib/__tests__/components/labels.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/labels.js similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/components/labels.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/components/labels.js diff --git a/src/legacy/ui/public/vislib/__tests__/index.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/index.js similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/index.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/index.js diff --git a/src/legacy/ui/public/vislib/__tests__/lib/__snapshots__/dispatch_heatmap.test.js.snap b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/__snapshots__/dispatch_heatmap.test.js.snap similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/__snapshots__/dispatch_heatmap.test.js.snap rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/__snapshots__/dispatch_heatmap.test.js.snap diff --git a/src/legacy/ui/public/vislib/__tests__/lib/axis/axis.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis/axis.js similarity index 98% rename from src/legacy/ui/public/vislib/__tests__/lib/axis/axis.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis/axis.js index 8b32943b64ea8..3081c12415076 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/axis/axis.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis/axis.js @@ -20,9 +20,11 @@ import d3 from 'd3'; import _ from 'lodash'; import ngMock from 'ng_mock'; +import 'ui/persisted_state'; + import expect from '@kbn/expect'; import $ from 'jquery'; -import '../../../../persisted_state'; + import { Axis } from '../../../lib/axis'; import { VisConfig } from '../../../lib/vis_config'; @@ -119,7 +121,8 @@ describe('Vislib Axis Class Test Suite', function() { }, data, persistedState, - $('.x-axis-div')[0] + $('.x-axis-div')[0], + () => undefined ); yAxis = new Axis(visConfig, { type: 'value', diff --git a/src/legacy/ui/public/vislib/__tests__/lib/axis_title.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis_title.js similarity index 97% rename from src/legacy/ui/public/vislib/__tests__/lib/axis_title.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis_title.js index d71184fdc1223..cbb294c3b44e4 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/axis_title.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/axis_title.js @@ -21,12 +21,14 @@ import d3 from 'd3'; import _ from 'lodash'; import $ from 'jquery'; import ngMock from 'ng_mock'; + import expect from '@kbn/expect'; +import 'ui/persisted_state'; + import { AxisTitle } from '../../lib/axis/axis_title'; import { AxisConfig } from '../../lib/axis/axis_config'; import { VisConfig } from '../../lib/vis_config'; import { Data } from '../../lib/data'; -import '../../../persisted_state'; describe('Vislib AxisTitle Class Test Suite', function() { let PersistedState; @@ -118,14 +120,15 @@ describe('Vislib AxisTitle Class Test Suite', function() { .style('height', '20px') .style('width', '20px'); - dataObj = new Data(data, new PersistedState()); + dataObj = new Data(data, new PersistedState(), () => undefined); visConfig = new VisConfig( { type: 'histogram', }, data, new PersistedState(), - el.node() + el.node(), + () => undefined ); const xAxisConfig = new AxisConfig(visConfig, { position: 'bottom', diff --git a/src/legacy/ui/public/vislib/__tests__/lib/chart_title.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/chart_title.js similarity index 98% rename from src/legacy/ui/public/vislib/__tests__/lib/chart_title.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/chart_title.js index e64999e7bd329..b2086d0749a41 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/chart_title.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/chart_title.js @@ -21,9 +21,9 @@ import d3 from 'd3'; import _ from 'lodash'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; + import { ChartTitle } from '../../lib/chart_title'; import { VisConfig } from '../../lib/vis_config'; -import '../../../persisted_state'; describe('Vislib ChartTitle Class Test Suite', function() { let persistedState; @@ -112,7 +112,8 @@ describe('Vislib ChartTitle Class Test Suite', function() { }, data, persistedState, - el.node() + el.node(), + () => undefined ); chartTitle = new ChartTitle(visConfig); }) diff --git a/src/legacy/ui/public/vislib/__tests__/lib/data.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/data.js similarity index 92% rename from src/legacy/ui/public/vislib/__tests__/lib/data.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/data.js index c12259ee55a5a..5811b1d238163 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/data.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/data.js @@ -20,9 +20,9 @@ import _ from 'lodash'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; +import 'ui/persisted_state'; import { Data } from '../../lib/data'; -import '../../../persisted_state'; const seriesData = { label: '', @@ -168,7 +168,7 @@ describe('Vislib Data Class Test Suite', function() { }); it('should return an object', function() { - const rowIn = new Data(rowsData, persistedState); + const rowIn = new Data(rowsData, persistedState, () => undefined); expect(_.isObject(rowIn)).to.be(true); }); }); @@ -182,7 +182,7 @@ describe('Vislib Data Class Test Suite', function() { }; beforeEach(function() { - data = new Data(pieData, persistedState); + data = new Data(pieData, persistedState, () => undefined); }); it('should remove zero values', function() { @@ -196,7 +196,7 @@ describe('Vislib Data Class Test Suite', function() { let serOut; beforeEach(function() { - serIn = new Data(seriesData, persistedState); + serIn = new Data(seriesData, persistedState, () => undefined); serOut = serIn.flatten(); }); @@ -210,7 +210,7 @@ describe('Vislib Data Class Test Suite', function() { function testLength(inputData) { return function() { - const data = new Data(inputData, persistedState); + const data = new Data(inputData, persistedState, () => undefined); const len = _.reduce( data.chartData(), function(sum, chart) { @@ -266,7 +266,7 @@ describe('Vislib Data Class Test Suite', function() { }; beforeEach(function() { - data = new Data(geohashGridData, persistedState); + data = new Data(geohashGridData, persistedState, () => undefined); }); describe('getVisData', function() { @@ -287,7 +287,7 @@ describe('Vislib Data Class Test Suite', function() { describe('null value check', function() { it('should return false', function() { - const data = new Data(rowsData, persistedState); + const data = new Data(rowsData, persistedState, () => undefined); expect(data.hasNullValues()).to.be(false); }); @@ -307,7 +307,7 @@ describe('Vislib Data Class Test Suite', function() { ], }); - const data = new Data(nullRowData, persistedState); + const data = new Data(nullRowData, persistedState, () => undefined); expect(data.hasNullValues()).to.be(true); }); }); diff --git a/src/legacy/ui/public/vislib/__tests__/lib/dispatch.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch.js similarity index 90% rename from src/legacy/ui/public/vislib/__tests__/lib/dispatch.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch.js index f2d2e7d05a34d..a93db5637c89d 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/dispatch.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch.js @@ -20,22 +20,23 @@ import _ from 'lodash'; import d3 from 'd3'; import ngMock from 'ng_mock'; + import expect from '@kbn/expect'; +import 'ui/persisted_state'; // Data -import data from 'fixtures/vislib/mock_data/date_histogram/_series'; -import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; -import '../../../persisted_state'; -import { SimpleEmitter } from '../../../utils/simple_emitter'; +import data from './fixtures/mock_data/date_histogram/_series'; +import getFixturesVislibVisFixtureProvider from './fixtures/_vis_fixture'; +import { SimpleEmitter } from '../../../legacy_imports'; describe('Vislib Dispatch Class Test Suite', function() { function destroyVis(vis) { vis.destroy(); } - function getEls(el, n, type) { + function getEls(element, n, type) { return d3 - .select(el) + .select(element) .data(new Array(n)) .enter() .append(type); @@ -48,7 +49,8 @@ describe('Vislib Dispatch Class Test Suite', function() { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(); persistedState = new ($injector.get('PersistedState'))(); vis.render(data, persistedState); }) @@ -74,8 +76,9 @@ describe('Vislib Dispatch Class Test Suite', function() { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(); + const getVis = getFixturesVislibVisFixtureProvider(Private); persistedState = new ($injector.get('PersistedState'))(); + vis = getVis(); vis.on('brush', _.noop); vis.render(data, persistedState); }) @@ -91,7 +94,7 @@ describe('Vislib Dispatch Class Test Suite', function() { const apply = chart.events.addEvent('event', _.noop); expect(apply).to.be.a('function'); - const els = getEls(vis.el, 3, 'div'); + const els = getEls(vis.element, 3, 'div'); apply(els); els.each(function() { expect(d3.select(this).on('event')).to.be(_.noop); @@ -114,7 +117,7 @@ describe('Vislib Dispatch Class Test Suite', function() { const apply = chart.events[name](chart.series[0].chartEl); expect(apply).to.be.a('function'); - const els = getEls(vis.el, 3, 'div'); + const els = getEls(vis.element, 3, 'div'); apply(els); els.each(function() { expect(d3.select(this).on(event)).to.be.a('function'); @@ -188,7 +191,8 @@ describe('Vislib Dispatch Class Test Suite', function() { let persistedState; ngMock.module('kibana'); ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(); persistedState = new ($injector.get('PersistedState'))(); vis.on('someEvent', _.noop); vis.render(data, persistedState); @@ -207,7 +211,8 @@ describe('Vislib Dispatch Class Test Suite', function() { let persistedState; ngMock.module('kibana'); ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(); persistedState = new ($injector.get('PersistedState'))(); vis.render(data, persistedState); vis.on('someEvent', _.noop); diff --git a/src/legacy/ui/public/vislib/__tests__/lib/dispatch_heatmap.test.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_heatmap.test.js similarity index 88% rename from src/legacy/ui/public/vislib/__tests__/lib/dispatch_heatmap.test.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_heatmap.test.js index 19e78ef4f30c2..e22f19ea643fd 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/dispatch_heatmap.test.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_heatmap.test.js @@ -16,7 +16,13 @@ * specific language governing permissions and limitations * under the License. */ + import mockDispatchDataD3 from './fixtures/dispatch_heatmap_d3.json'; +import { Dispatch } from '../../lib/dispatch'; +import mockdataPoint from './fixtures/dispatch_heatmap_data_point.json'; +import mockConfigPercentage from './fixtures/dispatch_heatmap_config.json'; + +jest.mock('ui/new_platform'); jest.mock('d3', () => ({ event: { target: { @@ -26,16 +32,14 @@ jest.mock('d3', () => ({ }, }, })); - -import { Dispatch } from '../../lib/dispatch'; -import mockdataPoint from './fixtures/dispatch_heatmap_data_point.json'; -import mockConfigPercentage from './fixtures/dispatch_heatmap_config.json'; - -jest.mock('ui/chrome', () => ({ - getUiSettingsClient: () => ({ - get: () => '', - }), - addBasePath: () => {}, +jest.mock('../../../legacy_imports.ts', () => ({ + ...jest.requireActual('../../../legacy_imports.ts'), + chrome: { + getUiSettingsClient: () => ({ + get: () => '', + }), + addBasePath: () => {}, + }, })); function getHandlerMock(config = {}, data = {}) { diff --git a/src/legacy/ui/public/vislib/__tests__/lib/dispatch_vertical_bar_chart.test.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_vertical_bar_chart.test.js similarity index 90% rename from src/legacy/ui/public/vislib/__tests__/lib/dispatch_vertical_bar_chart.test.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_vertical_bar_chart.test.js index e196c1bde0737..8fe9ac24db77b 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/dispatch_vertical_bar_chart.test.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/dispatch_vertical_bar_chart.test.js @@ -16,7 +16,14 @@ * specific language governing permissions and limitations * under the License. */ + import mockDispatchDataD3 from './fixtures/dispatch_bar_chart_d3.json'; +import { Dispatch } from '../../lib/dispatch'; +import mockdataPoint from './fixtures/dispatch_bar_chart_data_point.json'; +import mockConfigPercentage from './fixtures/dispatch_bar_chart_config_percentage.json'; +import mockConfigNormal from './fixtures/dispatch_bar_chart_config_normal.json'; + +jest.mock('ui/new_platform'); jest.mock('d3', () => ({ event: { target: { @@ -26,17 +33,14 @@ jest.mock('d3', () => ({ }, }, })); - -import { Dispatch } from '../../lib/dispatch'; -import mockdataPoint from './fixtures/dispatch_bar_chart_data_point.json'; -import mockConfigPercentage from './fixtures/dispatch_bar_chart_config_percentage.json'; -import mockConfigNormal from './fixtures/dispatch_bar_chart_config_normal.json'; - -jest.mock('ui/chrome', () => ({ - getUiSettingsClient: () => ({ - get: () => '', - }), - addBasePath: () => {}, +jest.mock('../../../legacy_imports.ts', () => ({ + ...jest.requireActual('../../../legacy_imports.ts'), + chrome: { + getUiSettingsClient: () => ({ + get: () => '', + }), + addBasePath: () => {}, + }, })); function getHandlerMock(config = {}, data = {}) { diff --git a/src/legacy/ui/public/vislib/__tests__/lib/error_handler.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/error_handler.js similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/error_handler.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/error_handler.js diff --git a/src/fixtures/vislib/_vis_fixture.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/_vis_fixture.js similarity index 61% rename from src/fixtures/vislib/_vis_fixture.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/_vis_fixture.js index d71c76a1fd99e..c49ca732f0915 100644 --- a/src/fixtures/vislib/_vis_fixture.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/_vis_fixture.js @@ -19,7 +19,23 @@ import _ from 'lodash'; import $ from 'jquery'; -import { VislibVisProvider } from 'ui/vislib/vis'; + +import { Vis } from '../../../vis'; + +// TODO: remove legacy imports when/of converting tests to jest +import { + setHierarchicalTooltipFormatter, + getHierarchicalTooltipFormatter, +} from 'ui/vis/components/tooltip/_hierarchical_tooltip_formatter'; +import { + setPointSeriesTooltipFormatter, + getPointSeriesTooltipFormatter, +} from 'ui/vis/components/tooltip/_pointseries_tooltip_formatter'; +import { + vislibSeriesResponseHandlerProvider, + vislibSlicesResponseHandlerProvider, +} from 'ui/vis/response_handlers/vislib'; +import { vislibColor } from 'ui/vis/components/color/color'; const $visCanvas = $('
') .attr('id', 'vislib-vis-fixtures') @@ -52,11 +68,25 @@ afterEach(function() { count = 0; }); -export default function VislibFixtures(Private) { - return function(visLibParams) { - const Vis = Private(VislibVisProvider); +const getDeps = () => { + const uiSettings = new Map(); + return { + uiSettings, + vislibColor, + getHierarchicalTooltipFormatter, + getPointSeriesTooltipFormatter, + vislibSeriesResponseHandlerProvider, + vislibSlicesResponseHandlerProvider, + }; +}; + +export default function getVislibFixtures(Private) { + setHierarchicalTooltipFormatter(Private); + setPointSeriesTooltipFormatter(Private); + + return function(visLibParams, element) { return new Vis( - $visCanvas.new(), + element || $visCanvas.new(), _.defaults({}, visLibParams || {}, { addTooltip: true, addLegend: true, @@ -64,7 +94,8 @@ export default function VislibFixtures(Private) { setYExtents: false, yAxis: {}, type: 'histogram', - }) + }), + getDeps() ); }; } diff --git a/src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_normal.json b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_normal.json similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_normal.json rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_normal.json diff --git a/src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_percentage.json b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_percentage.json similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_percentage.json rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_config_percentage.json diff --git a/src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_d3.json b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_d3.json similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_d3.json rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_d3.json diff --git a/src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_data_point.json b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_data_point.json similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_data_point.json rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_bar_chart_data_point.json diff --git a/src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_config.json b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_config.json similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_config.json rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_config.json diff --git a/src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_d3.json b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_d3.json similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_d3.json rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_d3.json diff --git a/src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_data_point.json b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_data_point.json similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_data_point.json rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/dispatch_heatmap_data_point.json diff --git a/src/fixtures/vislib/mock_data/date_histogram/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_columns.js similarity index 100% rename from src/fixtures/vislib/mock_data/date_histogram/_columns.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_columns.js diff --git a/src/fixtures/vislib/mock_data/date_histogram/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_rows.js similarity index 100% rename from src/fixtures/vislib/mock_data/date_histogram/_rows.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_rows.js diff --git a/src/fixtures/vislib/mock_data/date_histogram/_rows_series_with_holes.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_rows_series_with_holes.js similarity index 100% rename from src/fixtures/vislib/mock_data/date_histogram/_rows_series_with_holes.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_rows_series_with_holes.js diff --git a/src/fixtures/vislib/mock_data/date_histogram/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series.js similarity index 100% rename from src/fixtures/vislib/mock_data/date_histogram/_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series.js diff --git a/src/fixtures/vislib/mock_data/date_histogram/_series_monthly_interval.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_monthly_interval.js similarity index 100% rename from src/fixtures/vislib/mock_data/date_histogram/_series_monthly_interval.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_monthly_interval.js diff --git a/src/fixtures/vislib/mock_data/date_histogram/_series_neg.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_neg.js similarity index 100% rename from src/fixtures/vislib/mock_data/date_histogram/_series_neg.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_neg.js diff --git a/src/fixtures/vislib/mock_data/date_histogram/_series_pos_neg.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_pos_neg.js similarity index 100% rename from src/fixtures/vislib/mock_data/date_histogram/_series_pos_neg.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_series_pos_neg.js diff --git a/src/fixtures/vislib/mock_data/date_histogram/_stacked_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_stacked_series.js similarity index 100% rename from src/fixtures/vislib/mock_data/date_histogram/_stacked_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/date_histogram/_stacked_series.js diff --git a/src/fixtures/vislib/mock_data/filters/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_columns.js similarity index 100% rename from src/fixtures/vislib/mock_data/filters/_columns.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_columns.js diff --git a/src/fixtures/vislib/mock_data/filters/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_rows.js similarity index 100% rename from src/fixtures/vislib/mock_data/filters/_rows.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_rows.js diff --git a/src/fixtures/vislib/mock_data/filters/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_series.js similarity index 100% rename from src/fixtures/vislib/mock_data/filters/_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/filters/_series.js diff --git a/src/fixtures/vislib/mock_data/geohash/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_columns.js similarity index 100% rename from src/fixtures/vislib/mock_data/geohash/_columns.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_columns.js diff --git a/src/fixtures/vislib/mock_data/geohash/_geo_json.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_geo_json.js similarity index 100% rename from src/fixtures/vislib/mock_data/geohash/_geo_json.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_geo_json.js diff --git a/src/fixtures/vislib/mock_data/geohash/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_rows.js similarity index 100% rename from src/fixtures/vislib/mock_data/geohash/_rows.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/geohash/_rows.js diff --git a/src/fixtures/vislib/mock_data/histogram/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_columns.js similarity index 100% rename from src/fixtures/vislib/mock_data/histogram/_columns.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_columns.js diff --git a/src/fixtures/vislib/mock_data/histogram/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_rows.js similarity index 100% rename from src/fixtures/vislib/mock_data/histogram/_rows.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_rows.js diff --git a/src/fixtures/vislib/mock_data/histogram/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_series.js similarity index 100% rename from src/fixtures/vislib/mock_data/histogram/_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_series.js diff --git a/src/fixtures/vislib/mock_data/histogram/_slices.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_slices.js similarity index 100% rename from src/fixtures/vislib/mock_data/histogram/_slices.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/histogram/_slices.js diff --git a/src/fixtures/vislib/mock_data/not_enough_data/_one_point.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/not_enough_data/_one_point.js similarity index 100% rename from src/fixtures/vislib/mock_data/not_enough_data/_one_point.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/not_enough_data/_one_point.js diff --git a/src/fixtures/vislib/mock_data/range/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_columns.js similarity index 100% rename from src/fixtures/vislib/mock_data/range/_columns.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_columns.js diff --git a/src/fixtures/vislib/mock_data/range/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_rows.js similarity index 100% rename from src/fixtures/vislib/mock_data/range/_rows.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_rows.js diff --git a/src/fixtures/vislib/mock_data/range/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_series.js similarity index 100% rename from src/fixtures/vislib/mock_data/range/_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/range/_series.js diff --git a/src/fixtures/vislib/mock_data/significant_terms/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_columns.js similarity index 100% rename from src/fixtures/vislib/mock_data/significant_terms/_columns.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_columns.js diff --git a/src/fixtures/vislib/mock_data/significant_terms/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_rows.js similarity index 100% rename from src/fixtures/vislib/mock_data/significant_terms/_rows.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_rows.js diff --git a/src/fixtures/vislib/mock_data/significant_terms/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_series.js similarity index 100% rename from src/fixtures/vislib/mock_data/significant_terms/_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/significant_terms/_series.js diff --git a/src/fixtures/vislib/mock_data/stacked/_stacked.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/stacked/_stacked.js similarity index 100% rename from src/fixtures/vislib/mock_data/stacked/_stacked.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/stacked/_stacked.js diff --git a/src/fixtures/vislib/mock_data/terms/_columns.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_columns.js similarity index 100% rename from src/fixtures/vislib/mock_data/terms/_columns.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_columns.js diff --git a/src/fixtures/vislib/mock_data/terms/_rows.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_rows.js similarity index 100% rename from src/fixtures/vislib/mock_data/terms/_rows.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_rows.js diff --git a/src/fixtures/vislib/mock_data/terms/_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_series.js similarity index 100% rename from src/fixtures/vislib/mock_data/terms/_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_series.js diff --git a/src/fixtures/vislib/mock_data/terms/_seriesMultiple.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_seriesMultiple.js similarity index 100% rename from src/fixtures/vislib/mock_data/terms/_seriesMultiple.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/fixtures/mock_data/terms/_seriesMultiple.js diff --git a/src/legacy/ui/public/vislib/__tests__/lib/handler/handler.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/handler/handler.js similarity index 87% rename from src/legacy/ui/public/vislib/__tests__/lib/handler/handler.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/handler/handler.js index d37d67567e8a5..b309c97d24000 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/handler/handler.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/handler/handler.js @@ -21,13 +21,14 @@ import ngMock from 'ng_mock'; import expect from '@kbn/expect'; // Data -import series from 'fixtures/vislib/mock_data/date_histogram/_series'; -import columns from 'fixtures/vislib/mock_data/date_histogram/_columns'; -import rows from 'fixtures/vislib/mock_data/date_histogram/_rows'; -import stackedSeries from 'fixtures/vislib/mock_data/date_histogram/_stacked_series'; +import series from '../fixtures/mock_data/date_histogram/_series'; +import columns from '../fixtures/mock_data/date_histogram/_columns'; +import rows from '../fixtures/mock_data/date_histogram/_rows'; +import stackedSeries from '../fixtures/mock_data/date_histogram/_stacked_series'; import $ from 'jquery'; -import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; -import '../../../../persisted_state'; +import 'ui/persisted_state'; + +import getFixturesVislibVisFixtureProvider from '../fixtures/_vis_fixture'; const dateHistogramArray = [series, columns, rows, stackedSeries]; const names = ['series', 'columns', 'rows', 'stackedSeries']; @@ -40,7 +41,8 @@ dateHistogramArray.forEach(function(data, i) { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(); persistedState = new ($injector.get('PersistedState'))(); vis.render(data, persistedState); }) @@ -106,12 +108,12 @@ dateHistogramArray.forEach(function(data, i) { describe('removeAll Method', function() { beforeEach(function() { ngMock.inject(function() { - vis.handler.removeAll(vis.el); + vis.handler.removeAll(vis.element); }); }); it('should remove all DOM elements from the el', function() { - expect($(vis.el).children().length).to.be(0); + expect($(vis.element).children().length).to.be(0); }); }); @@ -121,7 +123,7 @@ dateHistogramArray.forEach(function(data, i) { }); it('should return an error classed DOM element with a text message', function() { - expect($(vis.el).find('.error').length).to.be(1); + expect($(vis.element).find('.error').length).to.be(1); expect($('.error h4').html()).to.be('This is an error!'); }); }); diff --git a/src/legacy/ui/public/vislib/__tests__/lib/layout/layout.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout.js similarity index 65% rename from src/legacy/ui/public/vislib/__tests__/lib/layout/layout.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout.js index 853a00d035a07..c8636f34ce6f8 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/layout/layout.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout.js @@ -20,16 +20,16 @@ import d3 from 'd3'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; +import 'ui/persisted_state'; // Data -import series from 'fixtures/vislib/mock_data/date_histogram/_series'; -import columns from 'fixtures/vislib/mock_data/date_histogram/_columns'; -import rows from 'fixtures/vislib/mock_data/date_histogram/_rows'; -import stackedSeries from 'fixtures/vislib/mock_data/date_histogram/_stacked_series'; +import series from '../fixtures/mock_data/date_histogram/_series'; +import columns from '../fixtures/mock_data/date_histogram/_columns'; +import rows from '../fixtures/mock_data/date_histogram/_rows'; +import stackedSeries from '../fixtures/mock_data/date_histogram/_stacked_series'; import $ from 'jquery'; import { Layout } from '../../../lib/layout/layout'; -import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; -import '../../../../persisted_state'; +import getFixturesVislibVisFixtureProvider from '../fixtures/_vis_fixture'; import { VisConfig } from '../../../lib/vis_config'; const dateHistogramArray = [series, columns, rows, stackedSeries]; @@ -46,7 +46,8 @@ dateHistogramArray.forEach(function(data, i) { beforeEach(function() { ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(); persistedState = new ($injector.get('PersistedState'))(); vis.render(data, persistedState); numberOfCharts = vis.handler.charts.length; @@ -59,17 +60,17 @@ dateHistogramArray.forEach(function(data, i) { describe('createLayout Method', function() { it('should append all the divs', function() { - expect($(vis.el).find('.visWrapper').length).to.be(1); - expect($(vis.el).find('.visAxis--y').length).to.be(2); - expect($(vis.el).find('.visWrapper__column').length).to.be(1); - expect($(vis.el).find('.visAxis__column--y').length).to.be(2); - expect($(vis.el).find('.y-axis-title').length).to.be.above(0); - expect($(vis.el).find('.visAxis__splitAxes--y').length).to.be(2); - expect($(vis.el).find('.visAxis__spacer--y').length).to.be(4); - expect($(vis.el).find('.visWrapper__chart').length).to.be(numberOfCharts); - expect($(vis.el).find('.visAxis--x').length).to.be(2); - expect($(vis.el).find('.visAxis__splitAxes--x').length).to.be(2); - expect($(vis.el).find('.x-axis-title').length).to.be.above(0); + expect($(vis.element).find('.visWrapper').length).to.be(1); + expect($(vis.element).find('.visAxis--y').length).to.be(2); + expect($(vis.element).find('.visWrapper__column').length).to.be(1); + expect($(vis.element).find('.visAxis__column--y').length).to.be(2); + expect($(vis.element).find('.y-axis-title').length).to.be.above(0); + expect($(vis.element).find('.visAxis__splitAxes--y').length).to.be(2); + expect($(vis.element).find('.visAxis__spacer--y').length).to.be(4); + expect($(vis.element).find('.visWrapper__chart').length).to.be(numberOfCharts); + expect($(vis.element).find('.visAxis--x').length).to.be(2); + expect($(vis.element).find('.visAxis__splitAxes--x').length).to.be(2); + expect($(vis.element).find('.x-axis-title').length).to.be.above(0); }); }); @@ -81,35 +82,36 @@ dateHistogramArray.forEach(function(data, i) { }, data, persistedState, - vis.el + vis.element, + () => undefined ); testLayout = new Layout(visConfig); }); it('should append a div with the correct class name', function() { - expect($(vis.el).find('.chart').length).to.be(numberOfCharts); + expect($(vis.element).find('.chart').length).to.be(numberOfCharts); }); it('should bind data to the DOM element', function() { expect( - !!$(vis.el) + !!$(vis.element) .find('.chart') .data() ).to.be(true); }); it('should create children', function() { - expect(typeof $(vis.el).find('.x-axis-div')).to.be('object'); + expect(typeof $(vis.element).find('.x-axis-div')).to.be('object'); }); it('should call split function when provided', function() { - expect(typeof $(vis.el).find('.x-axis-div')).to.be('object'); + expect(typeof $(vis.element).find('.x-axis-div')).to.be('object'); }); it('should throw errors when incorrect arguments provided', function() { expect(function() { testLayout.layout({ - parent: vis.el, + parent: vis.element, type: undefined, class: 'chart', }); @@ -131,7 +133,7 @@ dateHistogramArray.forEach(function(data, i) { expect(function() { testLayout.layout({ - parent: vis.el, + parent: vis.element, type: function(d) { return d; }, @@ -143,26 +145,26 @@ dateHistogramArray.forEach(function(data, i) { describe('appendElem Method', function() { beforeEach(function() { - vis.handler.layout.appendElem(vis.el, 'svg', 'column'); + vis.handler.layout.appendElem(vis.element, 'svg', 'column'); vis.handler.layout.appendElem('.visChart', 'div', 'test'); }); it('should append DOM element to el with a class name', function() { - expect(typeof $(vis.el).find('.column')).to.be('object'); - expect(typeof $(vis.el).find('.test')).to.be('object'); + expect(typeof $(vis.element).find('.column')).to.be('object'); + expect(typeof $(vis.element).find('.test')).to.be('object'); }); }); describe('removeAll Method', function() { beforeEach(function() { - d3.select(vis.el) + d3.select(vis.element) .append('div') .attr('class', 'visualize'); - vis.handler.layout.removeAll(vis.el); + vis.handler.layout.removeAll(vis.element); }); it('should remove all DOM elements from the el', function() { - expect($(vis.el).children().length).to.be(0); + expect($(vis.element).children().length).to.be(0); }); }); }); diff --git a/src/legacy/ui/public/vislib/__tests__/lib/layout/layout_types.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout_types.js similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/layout/layout_types.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/layout_types.js diff --git a/src/legacy/ui/public/vislib/__tests__/lib/layout/splits/column_chart/splits.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/column_chart/splits.js similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/layout/splits/column_chart/splits.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/column_chart/splits.js diff --git a/src/legacy/ui/public/vislib/__tests__/lib/layout/splits/gauge_chart/splits.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/gauge_chart/splits.js similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/layout/splits/gauge_chart/splits.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/splits/gauge_chart/splits.js diff --git a/src/legacy/ui/public/vislib/__tests__/lib/layout/types/column_layout.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/types/column_layout.js similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/layout/types/column_layout.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/layout/types/column_layout.js diff --git a/src/legacy/ui/public/vislib/__tests__/lib/types/point_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/point_series.js similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/types/point_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/point_series.js diff --git a/src/legacy/ui/public/vislib/__tests__/lib/types/testdata_linechart_percentile.json b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/testdata_linechart_percentile.json similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/types/testdata_linechart_percentile.json rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/testdata_linechart_percentile.json diff --git a/src/legacy/ui/public/vislib/__tests__/lib/types/testdata_linechart_percentile_result.json b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/testdata_linechart_percentile_result.json similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/lib/types/testdata_linechart_percentile_result.json rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/types/testdata_linechart_percentile_result.json diff --git a/src/legacy/ui/public/vislib/__tests__/lib/vis_config.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/vis_config.js similarity index 98% rename from src/legacy/ui/public/vislib/__tests__/lib/vis_config.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/vis_config.js index c9dcd4737b51a..3f0253b4a4670 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/vis_config.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/vis_config.js @@ -20,8 +20,8 @@ import d3 from 'd3'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; +import 'ui/persisted_state'; import { VisConfig } from '../../lib/vis_config'; -import '../../../persisted_state'; describe('Vislib VisConfig Class Test Suite', function() { let el; @@ -101,7 +101,8 @@ describe('Vislib VisConfig Class Test Suite', function() { }, data, new PersistedState(), - el + el, + () => undefined ); }) ); diff --git a/src/legacy/ui/public/vislib/__tests__/lib/x_axis.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/x_axis.js similarity index 98% rename from src/legacy/ui/public/vislib/__tests__/lib/x_axis.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/x_axis.js index 2fb7763756975..09fe067537c7f 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/x_axis.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/x_axis.js @@ -21,8 +21,8 @@ import d3 from 'd3'; import _ from 'lodash'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; +import 'ui/persisted_state'; import $ from 'jquery'; -import '../../../persisted_state'; import { Axis } from '../../lib/axis'; import { VisConfig } from '../../lib/vis_config'; @@ -124,7 +124,8 @@ describe('Vislib xAxis Class Test Suite', function() { }, data, persistedState, - $('.x-axis-div')[0] + $('.x-axis-div')[0], + () => undefined ); xAxis = new Axis(visConfig, { type: 'category', diff --git a/src/legacy/ui/public/vislib/__tests__/lib/y_axis.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/y_axis.js similarity index 99% rename from src/legacy/ui/public/vislib/__tests__/lib/y_axis.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/y_axis.js index 0189b8b960d32..e857aca3bf3ed 100644 --- a/src/legacy/ui/public/vislib/__tests__/lib/y_axis.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/lib/y_axis.js @@ -21,8 +21,8 @@ import _ from 'lodash'; import d3 from 'd3'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; +import 'ui/persisted_state'; import $ from 'jquery'; -import '../../../persisted_state'; import { Axis } from '../../lib/axis'; import { VisConfig } from '../../lib/vis_config'; @@ -97,7 +97,8 @@ function createData(seriesData) { }, data, persistedState, - node + node, + () => undefined ); return new YAxis( visConfig, diff --git a/src/legacy/ui/public/vislib/__tests__/vis.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/vis.js similarity index 92% rename from src/legacy/ui/public/vislib/__tests__/vis.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/vis.js index f10f44b7100d8..a6d1c4daf5d2c 100644 --- a/src/legacy/ui/public/vislib/__tests__/vis.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/vis.js @@ -18,16 +18,17 @@ */ import _ from 'lodash'; -import expect from '@kbn/expect'; +import $ from 'jquery'; import ngMock from 'ng_mock'; -import series from 'fixtures/vislib/mock_data/date_histogram/_series'; -import columns from 'fixtures/vislib/mock_data/date_histogram/_columns'; -import rows from 'fixtures/vislib/mock_data/date_histogram/_rows'; -import stackedSeries from 'fixtures/vislib/mock_data/date_histogram/_stacked_series'; -import $ from 'jquery'; -import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; -import '../../persisted_state'; +import expect from '@kbn/expect'; +import 'ui/persisted_state'; + +import series from './lib/fixtures/mock_data/date_histogram/_series'; +import columns from './lib/fixtures/mock_data/date_histogram/_columns'; +import rows from './lib/fixtures/mock_data/date_histogram/_rows'; +import stackedSeries from './lib/fixtures/mock_data/date_histogram/_stacked_series'; +import getFixturesVislibVisFixtureProvider from './lib/fixtures/_vis_fixture'; const dataArray = [series, columns, rows, stackedSeries]; @@ -45,9 +46,10 @@ dataArray.forEach(function(data, i) { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(); persistedState = new ($injector.get('PersistedState'))(); - secondVis = Private(FixturesVislibVisFixtureProvider)(); + secondVis = getVis(); }) ); @@ -99,7 +101,7 @@ dataArray.forEach(function(data, i) { }); it('should not remove visualizations that have not been destroyed', function() { - expect($(vis.el).find('.visWrapper').length).to.be(1); + expect($(vis.element).find('.visWrapper').length).to.be(1); }); }); diff --git a/src/legacy/ui/public/vislib/__tests__/visualizations/area_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/area_chart.js similarity index 93% rename from src/legacy/ui/public/vislib/__tests__/visualizations/area_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/area_chart.js index d81fd66f1e111..7fe350bd85e05 100644 --- a/src/legacy/ui/public/vislib/__tests__/visualizations/area_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/area_chart.js @@ -18,20 +18,22 @@ */ import d3 from 'd3'; -import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import _ from 'lodash'; - import $ from 'jquery'; -import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; -import '../../../persisted_state'; + +import expect from '@kbn/expect'; +import 'ui/persisted_state'; + +import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture'; + const dataTypesArray = { - 'series pos': require('fixtures/vislib/mock_data/date_histogram/_series'), - 'series pos neg': require('fixtures/vislib/mock_data/date_histogram/_series_pos_neg'), - 'series neg': require('fixtures/vislib/mock_data/date_histogram/_series_neg'), - 'term columns': require('fixtures/vislib/mock_data/terms/_columns'), - 'range rows': require('fixtures/vislib/mock_data/range/_rows'), - stackedSeries: require('fixtures/vislib/mock_data/date_histogram/_stacked_series'), + 'series pos': require('../lib/fixtures/mock_data/date_histogram/_series'), + 'series pos neg': require('../lib/fixtures/mock_data/date_histogram/_series_pos_neg'), + 'series neg': require('../lib/fixtures/mock_data/date_histogram/_series_neg'), + 'term columns': require('../lib/fixtures/mock_data/terms/_columns'), + 'range rows': require('../lib/fixtures/mock_data/range/_rows'), + stackedSeries: require('../lib/fixtures/mock_data/date_histogram/_stacked_series'), }; const visLibParams = { @@ -49,7 +51,8 @@ _.forOwn(dataTypesArray, function(dataType, dataTypeName) { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(visLibParams); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(visLibParams); persistedState = new ($injector.get('PersistedState'))(); vis.on('brush', _.noop); vis.render(dataType, persistedState); diff --git a/src/legacy/ui/public/vislib/__tests__/visualizations/chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/chart.js similarity index 93% rename from src/legacy/ui/public/vislib/__tests__/visualizations/chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/chart.js index f876db0aa6b22..088d3377af4dd 100644 --- a/src/legacy/ui/public/vislib/__tests__/visualizations/chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/chart.js @@ -18,15 +18,16 @@ */ import d3 from 'd3'; -import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import { VislibVisProvider } from '../../vis'; -import '../../../persisted_state'; + +import expect from '@kbn/expect'; +import 'ui/persisted_state'; + import { Chart } from '../../visualizations/_chart'; +import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture'; describe('Vislib _chart Test Suite', function() { let persistedState; - let Vis; let vis; let el; let myChart; @@ -111,7 +112,7 @@ describe('Vislib _chart Test Suite', function() { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - Vis = Private(VislibVisProvider); + const getVis = getFixturesVislibVisFixtureProvider(Private); persistedState = new ($injector.get('PersistedState'))(); el = d3 @@ -126,7 +127,7 @@ describe('Vislib _chart Test Suite', function() { zeroFill: true, }; - vis = new Vis(el[0][0], config); + vis = getVis(config, el[0][0]); vis.render(data, persistedState); myChart = vis.handler.charts[0]; diff --git a/src/legacy/ui/public/vislib/__tests__/visualizations/column_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/column_chart.js similarity index 91% rename from src/legacy/ui/public/vislib/__tests__/visualizations/column_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/column_chart.js index 2253783e7e644..d02060ef29bdd 100644 --- a/src/legacy/ui/public/vislib/__tests__/visualizations/column_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/column_chart.js @@ -17,24 +17,25 @@ * under the License. */ -import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import _ from 'lodash'; import d3 from 'd3'; +import expect from '@kbn/expect'; +import 'ui/persisted_state'; + // Data -import series from 'fixtures/vislib/mock_data/date_histogram/_series'; -import seriesPosNeg from 'fixtures/vislib/mock_data/date_histogram/_series_pos_neg'; -import seriesNeg from 'fixtures/vislib/mock_data/date_histogram/_series_neg'; -import termsColumns from 'fixtures/vislib/mock_data/terms/_columns'; -import histogramRows from 'fixtures/vislib/mock_data/histogram/_rows'; -import stackedSeries from 'fixtures/vislib/mock_data/date_histogram/_stacked_series'; -import { seriesMonthlyInterval } from 'fixtures/vislib/mock_data/date_histogram/_series_monthly_interval'; -import { rowsSeriesWithHoles } from 'fixtures/vislib/mock_data/date_histogram/_rows_series_with_holes'; -import rowsWithZeros from 'fixtures/vislib/mock_data/date_histogram/_rows'; +import series from '../lib/fixtures/mock_data/date_histogram/_series'; +import seriesPosNeg from '../lib/fixtures/mock_data/date_histogram/_series_pos_neg'; +import seriesNeg from '../lib/fixtures/mock_data/date_histogram/_series_neg'; +import termsColumns from '../lib/fixtures/mock_data/terms/_columns'; +import histogramRows from '../lib/fixtures/mock_data/histogram/_rows'; +import stackedSeries from '../lib/fixtures/mock_data/date_histogram/_stacked_series'; +import { seriesMonthlyInterval } from '../lib/fixtures/mock_data/date_histogram/_series_monthly_interval'; +import { rowsSeriesWithHoles } from '../lib/fixtures/mock_data/date_histogram/_rows_series_with_holes'; +import rowsWithZeros from '../lib/fixtures/mock_data/date_histogram/_rows'; import $ from 'jquery'; -import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; -import '../../../persisted_state'; +import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture'; // tuple, with the format [description, mode, data] const dataTypesArray = [ @@ -69,7 +70,8 @@ dataTypesArray.forEach(function(dataType) { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(visLibParams); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(visLibParams); persistedState = new ($injector.get('PersistedState'))(); vis.on('brush', _.noop); vis.render(data, persistedState); @@ -257,7 +259,8 @@ describe('stackData method - data set with zeros in percentage mode', function() beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(visLibParams); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(visLibParams); persistedState = new ($injector.get('PersistedState'))(); vis.on('brush', _.noop); }) @@ -307,7 +310,8 @@ describe('datumWidth - split chart data set with holes', function() { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(visLibParams); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(visLibParams); persistedState = new ($injector.get('PersistedState'))(); vis.on('brush', _.noop); vis.render(rowsSeriesWithHoles, persistedState); @@ -344,7 +348,8 @@ describe('datumWidth - monthly interval', function() { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(visLibParams); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(visLibParams); persistedState = new ($injector.get('PersistedState'))(); vis.on('brush', _.noop); vis.render(seriesMonthlyInterval, persistedState); diff --git a/src/legacy/ui/public/vislib/__tests__/visualizations/gauge_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/gauge_chart.js similarity index 94% rename from src/legacy/ui/public/vislib/__tests__/visualizations/gauge_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/gauge_chart.js index 45d9d4a2e374b..074b34e1c03c4 100644 --- a/src/legacy/ui/public/vislib/__tests__/visualizations/gauge_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/gauge_chart.js @@ -17,13 +17,15 @@ * under the License. */ -import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import $ from 'jquery'; import _ from 'lodash'; -import data from 'fixtures/vislib/mock_data/terms/_seriesMultiple'; -import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; -import '../../../persisted_state'; + +import expect from '@kbn/expect'; +import 'ui/persisted_state'; + +import data from '../lib/fixtures/mock_data/terms/_seriesMultiple'; +import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture'; describe('Vislib Gauge Chart Test Suite', function() { let PersistedState; @@ -89,7 +91,7 @@ describe('Vislib Gauge Chart Test Suite', function() { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vislibVis = Private(FixturesVislibVisFixtureProvider); + vislibVis = getFixturesVislibVisFixtureProvider(Private); PersistedState = $injector.get('PersistedState'); generateVis(); }) diff --git a/src/legacy/ui/public/vislib/__tests__/visualizations/heatmap_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/heatmap_chart.js similarity index 92% rename from src/legacy/ui/public/vislib/__tests__/visualizations/heatmap_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/heatmap_chart.js index f1dc4bf07ee31..bf1dbad0b44cf 100644 --- a/src/legacy/ui/public/vislib/__tests__/visualizations/heatmap_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/heatmap_chart.js @@ -17,20 +17,21 @@ * under the License. */ -import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import _ from 'lodash'; import d3 from 'd3'; +import expect from '@kbn/expect'; +import 'ui/persisted_state'; + // Data -import series from 'fixtures/vislib/mock_data/date_histogram/_series'; -import seriesPosNeg from 'fixtures/vislib/mock_data/date_histogram/_series_pos_neg'; -import seriesNeg from 'fixtures/vislib/mock_data/date_histogram/_series_neg'; -import termsColumns from 'fixtures/vislib/mock_data/terms/_columns'; -import stackedSeries from 'fixtures/vislib/mock_data/date_histogram/_stacked_series'; +import series from '../lib/fixtures/mock_data/date_histogram/_series'; +import seriesPosNeg from '../lib/fixtures/mock_data/date_histogram/_series_pos_neg'; +import seriesNeg from '../lib/fixtures/mock_data/date_histogram/_series_neg'; +import termsColumns from '../lib/fixtures/mock_data/terms/_columns'; +import stackedSeries from '../lib/fixtures/mock_data/date_histogram/_stacked_series'; import $ from 'jquery'; -import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; -import '../../../persisted_state'; +import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture'; // tuple, with the format [description, mode, data] const dataTypesArray = [ @@ -74,7 +75,7 @@ describe('Vislib Heatmap Chart Test Suite', function() { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vislibVis = Private(FixturesVislibVisFixtureProvider); + vislibVis = getFixturesVislibVisFixtureProvider(Private); PersistedState = $injector.get('PersistedState'); generateVis(); }) diff --git a/src/legacy/ui/public/vislib/__tests__/visualizations/line_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/line_chart.js similarity index 92% rename from src/legacy/ui/public/vislib/__tests__/visualizations/line_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/line_chart.js index 354be1f0ced0f..d010944a19e47 100644 --- a/src/legacy/ui/public/vislib/__tests__/visualizations/line_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/line_chart.js @@ -20,18 +20,19 @@ import d3 from 'd3'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; +import $ from 'jquery'; import _ from 'lodash'; +import 'ui/persisted_state'; + // Data -import seriesPos from 'fixtures/vislib/mock_data/date_histogram/_series'; -import seriesPosNeg from 'fixtures/vislib/mock_data/date_histogram/_series_pos_neg'; -import seriesNeg from 'fixtures/vislib/mock_data/date_histogram/_series_neg'; -import histogramColumns from 'fixtures/vislib/mock_data/histogram/_columns'; -import rangeRows from 'fixtures/vislib/mock_data/range/_rows'; -import termSeries from 'fixtures/vislib/mock_data/terms/_series'; -import $ from 'jquery'; -import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; -import '../../../persisted_state'; +import seriesPos from '../lib/fixtures/mock_data/date_histogram/_series'; +import seriesPosNeg from '../lib/fixtures/mock_data/date_histogram/_series_pos_neg'; +import seriesNeg from '../lib/fixtures/mock_data/date_histogram/_series_neg'; +import histogramColumns from '../lib/fixtures/mock_data/histogram/_columns'; +import rangeRows from '../lib/fixtures/mock_data/range/_rows'; +import termSeries from '../lib/fixtures/mock_data/terms/_series'; +import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture'; const dataTypes = [ ['series pos', seriesPos], @@ -54,6 +55,7 @@ describe('Vislib Line Chart', function() { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { + const getVis = getFixturesVislibVisFixtureProvider(Private); const visLibParams = { type: 'line', addLegend: true, @@ -61,10 +63,10 @@ describe('Vislib Line Chart', function() { drawLinesBetweenPoints: true, }; - vis = Private(FixturesVislibVisFixtureProvider)(visLibParams); + vis = getVis(visLibParams); persistedState = new ($injector.get('PersistedState'))(); - vis.on('brush', _.noop); vis.render(data, persistedState); + vis.on('brush', _.noop); }) ); diff --git a/src/legacy/ui/public/vislib/__tests__/visualizations/pie_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js similarity index 94% rename from src/legacy/ui/public/vislib/__tests__/visualizations/pie_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js index 9d299c4d3a5df..381dfcd387cc2 100644 --- a/src/legacy/ui/public/vislib/__tests__/visualizations/pie_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js @@ -18,17 +18,20 @@ */ import d3 from 'd3'; -import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import _ from 'lodash'; -import fixtures from 'fixtures/fake_hierarchical_data'; import $ from 'jquery'; -import FixturesVislibVisFixtureProvider from 'fixtures/vislib/_vis_fixture'; -import { Vis } from '../../../vis'; -import '../../../persisted_state'; + +import expect from '@kbn/expect'; +// TODO: Remove ui imports once converting to jest +import 'ui/persisted_state'; +import { vislibSlicesResponseHandlerProvider } from 'ui/vis/response_handlers/vislib'; + +import fixtures from 'fixtures/fake_hierarchical_data'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { vislibSlicesResponseHandlerProvider } from '../../../vis/response_handlers/vislib'; -import { tabifyAggResponse } from '../../../agg_response/tabify'; + +import getFixturesVislibVisFixtureProvider from '../lib/fixtures/_vis_fixture'; +import { Vis, tabifyAggResponse } from '../../../legacy_imports'; const rowAgg = [ { type: 'avg', schema: 'metric', params: { field: 'bytes' } }, @@ -129,7 +132,8 @@ describe('No global chart settings', function() { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - chart1 = Private(FixturesVislibVisFixtureProvider)(visLibParams1); + const getVis = getFixturesVislibVisFixtureProvider(Private); + chart1 = getVis(visLibParams1); persistedState = new ($injector.get('PersistedState'))(); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); responseHandler = vislibSlicesResponseHandlerProvider().handler; @@ -163,7 +167,7 @@ describe('No global chart settings', function() { }); it('should render chart titles for all charts', function() { - expect($(chart1.el).find('.visAxis__splitTitles--y').length).to.be(1); + expect($(chart1.element).find('.visAxis__splitTitles--y').length).to.be(1); }); describe('_validatePieData method', function() { @@ -221,7 +225,8 @@ describe('Vislib PieChart Class Test Suite', function() { beforeEach(ngMock.module('kibana')); beforeEach( ngMock.inject(function(Private, $injector) { - vis = Private(FixturesVislibVisFixtureProvider)(visLibParams); + const getVis = getFixturesVislibVisFixtureProvider(Private); + vis = getVis(visLibParams); persistedState = new ($injector.get('PersistedState'))(); indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); responseHandler = vislibSlicesResponseHandlerProvider().handler; diff --git a/src/legacy/ui/public/vislib/__tests__/visualizations/time_marker.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/time_marker.js similarity index 97% rename from src/legacy/ui/public/vislib/__tests__/visualizations/time_marker.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/time_marker.js index cfe61d6f35198..ec22d43c08cb2 100644 --- a/src/legacy/ui/public/vislib/__tests__/visualizations/time_marker.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/time_marker.js @@ -20,8 +20,8 @@ import d3 from 'd3'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import series from 'fixtures/vislib/mock_data/date_histogram/_series'; -import terms from 'fixtures/vislib/mock_data/terms/_columns'; +import series from '../lib/fixtures/mock_data/date_histogram/_series'; +import terms from '../lib/fixtures/mock_data/terms/_columns'; import $ from 'jquery'; import { TimeMarker } from '../../visualizations/time_marker'; diff --git a/src/legacy/ui/public/vislib/__tests__/visualizations/vis_types.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/vis_types.js similarity index 100% rename from src/legacy/ui/public/vislib/__tests__/visualizations/vis_types.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/vis_types.js diff --git a/src/legacy/ui/public/vislib/_index.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/_index.scss similarity index 100% rename from src/legacy/ui/public/vislib/_index.scss rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/_index.scss diff --git a/src/legacy/ui/public/vislib/_variables.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/_variables.scss similarity index 100% rename from src/legacy/ui/public/vislib/_variables.scss rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/_variables.scss diff --git a/src/legacy/ui/public/vislib/components/labels/data_array.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/data_array.js similarity index 100% rename from src/legacy/ui/public/vislib/components/labels/data_array.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/data_array.js diff --git a/src/legacy/ui/public/vislib/components/labels/flatten_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/flatten_series.js similarity index 100% rename from src/legacy/ui/public/vislib/components/labels/flatten_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/flatten_series.js diff --git a/src/legacy/ui/public/vislib/components/labels/index.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/index.js similarity index 100% rename from src/legacy/ui/public/vislib/components/labels/index.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/index.js diff --git a/src/legacy/ui/public/vislib/components/labels/labels.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/labels.js similarity index 100% rename from src/legacy/ui/public/vislib/components/labels/labels.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/labels.js diff --git a/src/legacy/ui/public/vislib/components/labels/truncate_labels.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/truncate_labels.js similarity index 100% rename from src/legacy/ui/public/vislib/components/labels/truncate_labels.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/truncate_labels.js diff --git a/src/legacy/ui/public/vislib/components/labels/uniq_labels.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/uniq_labels.js similarity index 100% rename from src/legacy/ui/public/vislib/components/labels/uniq_labels.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/labels/uniq_labels.js diff --git a/src/legacy/ui/public/vislib/components/zero_injection/flatten_data.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/flatten_data.js similarity index 100% rename from src/legacy/ui/public/vislib/components/zero_injection/flatten_data.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/flatten_data.js diff --git a/src/legacy/ui/public/vislib/components/zero_injection/inject_zeros.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/inject_zeros.js similarity index 100% rename from src/legacy/ui/public/vislib/components/zero_injection/inject_zeros.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/inject_zeros.js diff --git a/src/legacy/ui/public/vislib/components/zero_injection/ordered_x_keys.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/ordered_x_keys.js similarity index 100% rename from src/legacy/ui/public/vislib/components/zero_injection/ordered_x_keys.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/ordered_x_keys.js diff --git a/src/legacy/ui/public/vislib/components/zero_injection/uniq_keys.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/uniq_keys.js similarity index 100% rename from src/legacy/ui/public/vislib/components/zero_injection/uniq_keys.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/uniq_keys.js diff --git a/src/legacy/ui/public/vislib/components/zero_injection/zero_fill_data_array.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_fill_data_array.js similarity index 100% rename from src/legacy/ui/public/vislib/components/zero_injection/zero_fill_data_array.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_fill_data_array.js diff --git a/src/legacy/ui/public/vislib/components/zero_injection/zero_filled_array.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_filled_array.js similarity index 100% rename from src/legacy/ui/public/vislib/components/zero_injection/zero_filled_array.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_filled_array.js diff --git a/src/legacy/ui/public/vislib/components/zero_injection/zero_injection.test.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_injection.test.js similarity index 100% rename from src/legacy/ui/public/vislib/components/zero_injection/zero_injection.test.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/components/zero_injection/zero_injection.test.js diff --git a/src/legacy/ui/public/vislib/errors.ts b/src/legacy/core_plugins/vis_type_vislib/public/vislib/errors.ts similarity index 95% rename from src/legacy/ui/public/vislib/errors.ts rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/errors.ts index 0c6e720f0ef32..9014349c38d25 100644 --- a/src/legacy/ui/public/vislib/errors.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/errors.ts @@ -19,7 +19,7 @@ /* eslint-disable max-classes-per-file */ -import { KbnError } from '../../../../plugins/kibana_utils/public'; +import { KbnError } from '../../../../../plugins/kibana_utils/public'; export class VislibError extends KbnError { constructor(message: string) { diff --git a/src/legacy/ui/public/vislib/index.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/index.js similarity index 100% rename from src/legacy/ui/public/vislib/index.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/index.js diff --git a/src/legacy/ui/public/vislib/lib/_alerts.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_alerts.scss similarity index 100% rename from src/legacy/ui/public/vislib/lib/_alerts.scss rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_alerts.scss diff --git a/src/legacy/ui/public/vislib/lib/_data_label.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_data_label.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/_data_label.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_data_label.js diff --git a/src/legacy/ui/public/vislib/lib/_error_handler.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_error_handler.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/_error_handler.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_error_handler.js diff --git a/src/legacy/ui/public/vislib/lib/_handler.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_handler.scss similarity index 100% rename from src/legacy/ui/public/vislib/lib/_handler.scss rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_handler.scss diff --git a/src/legacy/ui/public/vislib/lib/_index.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_index.scss similarity index 100% rename from src/legacy/ui/public/vislib/lib/_index.scss rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/_index.scss diff --git a/src/legacy/ui/public/vislib/lib/alerts.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/alerts.js similarity index 97% rename from src/legacy/ui/public/vislib/lib/alerts.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/alerts.js index cf79dabf1b07e..086b4e31be1a3 100644 --- a/src/legacy/ui/public/vislib/lib/alerts.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/alerts.js @@ -71,11 +71,11 @@ export class Alerts { const alerts = this.alerts; const vis = this.vis; - $(vis.el) + $(vis.element) .find('.visWrapper__alerts') .append($('
').addClass('visAlerts__tray')); if (!alerts.size()) return; - $(vis.el) + $(vis.element) .find('.visAlerts__tray') .append(alerts.value()); } @@ -89,13 +89,13 @@ export class Alerts { }; if (this.alertDefs.find(alertDef => alertDef.msg === alert.msg)) return; this.alertDefs.push(alert); - $(vis.el) + $(vis.element) .find('.visAlerts__tray') .append(this._addAlert(alert)); } destroy() { - $(this.vis.el) + $(this.vis.element) .find('.visWrapper__alerts') .remove(); } diff --git a/src/legacy/ui/public/vislib/lib/axis/axis.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/axis/axis.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis.js diff --git a/src/legacy/ui/public/vislib/lib/axis/axis_config.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_config.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/axis/axis_config.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_config.js diff --git a/src/legacy/ui/public/vislib/lib/axis/axis_labels.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/axis/axis_labels.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js diff --git a/src/legacy/ui/public/vislib/lib/axis/axis_scale.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/axis/axis_scale.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js diff --git a/src/legacy/ui/public/vislib/lib/axis/axis_title.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/axis/axis_title.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_title.js diff --git a/src/legacy/ui/public/vislib/lib/axis/index.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/index.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/axis/index.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/index.js diff --git a/src/legacy/ui/public/vislib/lib/axis/scale_modes.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/scale_modes.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/axis/scale_modes.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/scale_modes.js diff --git a/src/legacy/ui/public/vislib/lib/axis/time_ticks.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/axis/time_ticks.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.js diff --git a/src/legacy/ui/public/vislib/lib/axis/time_ticks.test.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.test.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/axis/time_ticks.test.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/time_ticks.test.js diff --git a/src/legacy/ui/public/vislib/lib/chart_grid.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_grid.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/chart_grid.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_grid.js diff --git a/src/legacy/ui/public/vislib/lib/chart_title.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_title.js similarity index 98% rename from src/legacy/ui/public/vislib/lib/chart_title.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_title.js index f79d14a3deaa6..1c84f98614b05 100644 --- a/src/legacy/ui/public/vislib/lib/chart_title.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/chart_title.js @@ -19,8 +19,9 @@ import d3 from 'd3'; import _ from 'lodash'; + import { ErrorHandler } from './_error_handler'; -import { Tooltip } from '../../vis/components/tooltip'; +import { Tooltip } from '../../legacy_imports'; export class ChartTitle extends ErrorHandler { constructor(visConfig) { diff --git a/src/legacy/ui/public/vislib/lib/data.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/data.js similarity index 98% rename from src/legacy/ui/public/vislib/lib/data.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/data.js index c10449c681a3c..c7824c43eeec5 100644 --- a/src/legacy/ui/public/vislib/lib/data.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/data.js @@ -19,11 +19,11 @@ import d3 from 'd3'; import _ from 'lodash'; + import { injectZeros } from '../components/zero_injection/inject_zeros'; import { orderXValues } from '../components/zero_injection/ordered_x_keys'; import { labels } from '../components/labels/labels'; -import { vislibColor } from '../../vis/components/color/color'; -import { getFormat } from '../../visualize/loader/pipeline_helpers/utilities'; +import { getFormat } from '../../legacy_imports'; /** * Provides an API for pulling values off the data @@ -35,8 +35,9 @@ import { getFormat } from '../../visualize/loader/pipeline_helpers/utilities'; * @param attr {Object|*} Visualization options */ export class Data { - constructor(data, uiState) { + constructor(data, uiState, vislibColor) { this.uiState = uiState; + this.vislibColor = vislibColor; this.data = this.copyDataObj(data); this.type = this.getDataType(); this._cleanVisData(); @@ -472,7 +473,7 @@ export class Data { const defaultColors = this.uiState.get('vis.defaultColors'); const overwriteColors = this.uiState.get('vis.colors'); const colors = defaultColors ? _.defaults({}, overwriteColors, defaultColors) : overwriteColors; - return vislibColor(this.getLabels(), colors); + return this.vislibColor(this.getLabels(), colors); } /** @@ -482,7 +483,7 @@ export class Data { * @returns {Function} Performs lookup on string and returns hex color */ getPieColorFunc() { - return vislibColor( + return this.vislibColor( this.pieNames(this.getVisData()).map(function(d) { return d.label; }), diff --git a/src/legacy/ui/public/vislib/lib/dispatch.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/dispatch.js similarity index 92% rename from src/legacy/ui/public/vislib/lib/dispatch.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/dispatch.js index fb5cc127c27fd..404f7ef82d97f 100644 --- a/src/legacy/ui/public/vislib/lib/dispatch.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/dispatch.js @@ -20,10 +20,8 @@ import d3 from 'd3'; import { get } from 'lodash'; import $ from 'jquery'; -import { SimpleEmitter } from '../../utils/simple_emitter'; -import chrome from 'ui/chrome'; -const config = chrome.getUiSettingsClient(); +import { SimpleEmitter } from '../../legacy_imports'; /** * Handles event responses @@ -33,9 +31,10 @@ const config = chrome.getUiSettingsClient(); * @param handler {Object} Reference to Handler Class Object */ export class Dispatch extends SimpleEmitter { - constructor(handler) { + constructor(handler, uiSettings) { super(); this.handler = handler; + this.uiSettings = uiSettings; this._listeners = {}; } @@ -196,7 +195,7 @@ export class Dispatch extends SimpleEmitter { const addEvent = this.addEvent; const $el = this.handler.el; if (!this.handler.highlight) { - this.handler.highlight = self.highlight; + this.handler.highlight = self.getHighlighter(self.uiSettings); } function hover(d, i) { @@ -289,21 +288,23 @@ export class Dispatch extends SimpleEmitter { } /** - * Highlight the element that is under the cursor + * return function to Highlight the element that is under the cursor * by reducing the opacity of all the elements on the graph. - * @param element {d3.Selection} - * @method highlight + * @param uiSettings + * @method getHighlighter */ - highlight(element) { - const label = this.getAttribute('data-label'); - if (!label) return; - const dimming = config.get('visualization:dimmingOpacity'); - $(element) - .parent() - .find('[data-label]') - .css('opacity', 1) //Opacity 1 is needed to avoid the css application - .not((els, el) => String($(el).data('label')) === label) - .css('opacity', justifyOpacity(dimming)); + getHighlighter(uiSettings) { + return function highlight(element) { + const label = this.getAttribute('data-label'); + if (!label) return; + const dimming = uiSettings.get('visualization:dimmingOpacity'); + $(element) + .parent() + .find('[data-label]') + .css('opacity', 1) //Opacity 1 is needed to avoid the css application + .not((els, el) => String($(el).data('label')) === label) + .css('opacity', justifyOpacity(dimming)); + }; } /** diff --git a/src/legacy/ui/public/vislib/lib/handler.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/handler.js similarity index 96% rename from src/legacy/ui/public/vislib/lib/handler.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/handler.js index 6047faa0c6b15..b887b61578cc4 100644 --- a/src/legacy/ui/public/vislib/lib/handler.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/handler.js @@ -20,15 +20,16 @@ import d3 from 'd3'; import _ from 'lodash'; import MarkdownIt from 'markdown-it'; + import { NoResults } from '../errors'; -import { Binder } from '../../binder'; import { Layout } from './layout/layout'; import { ChartTitle } from './chart_title'; import { Alerts } from './alerts'; import { Axis } from './axis/axis'; import { ChartGrid as Grid } from './chart_grid'; import { visTypes as chartTypes } from '../visualizations/vis_types'; -import { dispatchRenderComplete } from '../../../../../plugins/kibana_utils/public'; +import { Binder } from '../../legacy_imports'; +import { dispatchRenderComplete } from '../../../../../../plugins/kibana_utils/public'; const markdownIt = new MarkdownIt({ html: false, @@ -45,9 +46,10 @@ const markdownIt = new MarkdownIt({ * create the visualization */ export class Handler { - constructor(vis, visConfig) { + constructor(vis, visConfig, deps) { this.el = visConfig.get('el'); this.ChartClass = chartTypes[visConfig.get('type')]; + this.deps = deps; this.charts = []; this.vis = vis; @@ -151,7 +153,7 @@ export class Handler { let loadedCount = 0; const chartSelection = selection.selectAll('.chart'); chartSelection.each(function(chartData) { - const chart = new self.ChartClass(self, this, chartData); + const chart = new self.ChartClass(self, this, chartData, self.deps); self.vis.eventNames().forEach(function(event) { self.enable(event, chart); diff --git a/src/legacy/ui/public/vislib/lib/layout/_index.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/_index.scss similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/_index.scss rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/_index.scss diff --git a/src/legacy/ui/public/vislib/lib/layout/_layout.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/_layout.scss rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/_layout.scss diff --git a/src/legacy/ui/public/vislib/lib/layout/index.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/index.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/index.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/index.js diff --git a/src/legacy/ui/public/vislib/lib/layout/layout.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/layout.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/layout.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/layout.js diff --git a/src/legacy/ui/public/vislib/lib/layout/layout_types.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/layout_types.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/layout_types.js diff --git a/src/legacy/ui/public/vislib/lib/layout/splits/column_chart/chart_split.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_split.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/splits/column_chart/chart_split.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_split.js diff --git a/src/legacy/ui/public/vislib/lib/layout/splits/column_chart/chart_title_split.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_title_split.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/splits/column_chart/chart_title_split.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/chart_title_split.js diff --git a/src/legacy/ui/public/vislib/lib/layout/splits/column_chart/x_axis_split.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/x_axis_split.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/splits/column_chart/x_axis_split.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/x_axis_split.js diff --git a/src/legacy/ui/public/vislib/lib/layout/splits/column_chart/y_axis_split.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/y_axis_split.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/splits/column_chart/y_axis_split.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/column_chart/y_axis_split.js diff --git a/src/legacy/ui/public/vislib/lib/layout/splits/gauge_chart/chart_split.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_split.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/splits/gauge_chart/chart_split.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_split.js diff --git a/src/legacy/ui/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/gauge_chart/chart_title_split.js diff --git a/src/legacy/ui/public/vislib/lib/layout/splits/pie_chart/chart_split.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_split.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/splits/pie_chart/chart_split.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_split.js diff --git a/src/legacy/ui/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/splits/pie_chart/chart_title_split.js diff --git a/src/legacy/ui/public/vislib/lib/layout/types/column_layout.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/types/column_layout.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/column_layout.js diff --git a/src/legacy/ui/public/vislib/lib/layout/types/gauge_layout.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/gauge_layout.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/types/gauge_layout.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/gauge_layout.js diff --git a/src/legacy/ui/public/vislib/lib/layout/types/pie_layout.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/pie_layout.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/layout/types/pie_layout.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/layout/types/pie_layout.js diff --git a/src/legacy/ui/public/vislib/lib/types/gauge.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/gauge.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/types/gauge.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/gauge.js diff --git a/src/legacy/ui/public/vislib/lib/types/index.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/index.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/types/index.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/index.js diff --git a/src/legacy/ui/public/vislib/lib/types/pie.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/pie.js similarity index 100% rename from src/legacy/ui/public/vislib/lib/types/pie.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/pie.js diff --git a/src/legacy/ui/public/vislib/lib/types/point_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.js similarity index 98% rename from src/legacy/ui/public/vislib/lib/types/point_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.js index 332f7408ebc66..eab3bc02f4eec 100644 --- a/src/legacy/ui/public/vislib/lib/types/point_series.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.js @@ -202,7 +202,7 @@ export const vislibPointSeriesTypes = { 'Positive and negative values are not accurately represented by stacked ' + 'area charts. Either changing the chart mode to "overlap" or using a ' + 'bar chart is recommended.', - test: function(vis, data) { + test: function(_, data) { if (!data.shouldBeStacked() || data.maxNumberOfSeries() < 2) return; const hasPos = data.getYMax(data._getY) > 0; @@ -216,7 +216,7 @@ export const vislibPointSeriesTypes = { 'Parts of or the entire area chart might not be displayed due to null ' + 'values in the data. A line chart is recommended when displaying data ' + 'with null values.', - test: function(vis, data) { + test: function(_, data) { return data.hasNullValues(); }, }, @@ -229,7 +229,7 @@ export const vislibPointSeriesTypes = { const tooManySeries = defaults.charts.length && defaults.charts[0].series.length > cfg.heatmapMaxBuckets; if (hasCharts && tooManySeries) { - defaults.error = i18n.translate('common.ui.vislib.heatmap.maxBucketsText', { + defaults.error = i18n.translate('kbnVislibVisTypes.vislib.heatmap.maxBucketsText', { defaultMessage: 'There are too many series defined ({nr}). The configured maximum is {max}.', values: { diff --git a/src/legacy/ui/public/vislib/lib/types/point_series.test.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.test.js similarity index 98% rename from src/legacy/ui/public/vislib/lib/types/point_series.test.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.test.js index cada6127282d8..38a6be548594f 100644 --- a/src/legacy/ui/public/vislib/lib/types/point_series.test.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/types/point_series.test.js @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import stackedSeries from '../../../../../../fixtures/vislib/mock_data/date_histogram/_stacked_series'; +import stackedSeries from '../../__tests__/lib/fixtures/mock_data/date_histogram/_stacked_series'; import { vislibPointSeriesTypes } from './point_series'; describe('vislibPointSeriesTypes', () => { diff --git a/src/legacy/ui/public/vislib/lib/vis_config.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/vis_config.js similarity index 93% rename from src/legacy/ui/public/vislib/lib/vis_config.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/vis_config.js index 5f10e89474809..091e6b1752d8d 100644 --- a/src/legacy/ui/public/vislib/lib/vis_config.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/vis_config.js @@ -35,8 +35,8 @@ const DEFAULT_VIS_CONFIG = { }; export class VisConfig { - constructor(visConfigArgs, data, uiState, el) { - this.data = new Data(data, uiState); + constructor(visConfigArgs, data, uiState, el, vislibColor) { + this.data = new Data(data, uiState, vislibColor); const visType = visTypes[visConfigArgs.type]; const typeDefaults = visType(visConfigArgs, this.data); diff --git a/src/legacy/ui/public/vislib/partials/touchdown.tmpl.html b/src/legacy/core_plugins/vis_type_vislib/public/vislib/partials/touchdown.tmpl.html similarity index 100% rename from src/legacy/ui/public/vislib/partials/touchdown.tmpl.html rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/partials/touchdown.tmpl.html diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/vis.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/vis.js new file mode 100644 index 0000000000000..32afb6a008b61 --- /dev/null +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/vis.js @@ -0,0 +1,184 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import _ from 'lodash'; +import d3 from 'd3'; +import { EventEmitter } from 'events'; + +import { VislibError } from './errors'; +import { VisConfig } from './lib/vis_config'; +import { Handler } from './lib/handler'; + +/** + * Creates the visualizations. + * + * @class Vis + * @constructor + * @param element {HTMLElement} jQuery selected HTML element + * @param config {Object} Parameters that define the chart type and chart options + */ +export class Vis extends EventEmitter { + constructor(element, visConfigArgs, deps) { + super(); + this.element = element.get ? element.get(0) : element; + this.visConfigArgs = _.cloneDeep(visConfigArgs); + this.visConfigArgs.dimmingOpacity = deps.uiSettings.get('visualization:dimmingOpacity'); + this.visConfigArgs.heatmapMaxBuckets = deps.uiSettings.get('visualization:heatmap:maxBuckets'); + this.deps = deps; + } + + hasLegend() { + return this.visConfigArgs.addLegend; + } + + initVisConfig(data, uiState) { + this.data = data; + this.uiState = uiState; + this.visConfig = new VisConfig( + this.visConfigArgs, + this.data, + this.uiState, + this.element, + this.deps.vislibColor + ); + } + + /** + * Renders the visualization + * + * @method render + * @param data {Object} Elasticsearch query results + */ + render(data, uiState) { + if (!data) { + throw new Error('No valid data!'); + } + + if (this.handler) { + this.data = null; + this._runOnHandler('destroy'); + } + + this.initVisConfig(data, uiState); + + this.handler = new Handler(this, this.visConfig, this.deps); + this._runOnHandler('render'); + } + + getLegendLabels() { + return this.visConfig ? this.visConfig.get('legend.labels', null) : null; + } + + getLegendColors() { + return this.visConfig ? this.visConfig.get('legend.colors', null) : null; + } + + _runOnHandler(method) { + try { + this.handler[method](); + } catch (error) { + if (error instanceof VislibError) { + error.displayToScreen(this.handler); + } else { + throw error; + } + } + } + + /** + * Destroys the visualization + * Removes chart and all elements associated with it. + * Removes chart and all elements associated with it. + * Remove event listeners and pass destroy call down to owned objects. + * + * @method destroy + */ + destroy() { + const selection = d3.select(this.element).select('.visWrapper'); + + if (this.handler) this._runOnHandler('destroy'); + + selection.remove(); + } + + /** + * Sets attributes on the visualization + * + * @method set + * @param name {String} An attribute name + * @param val {*} Value to which the attribute name is set + */ + set(name, val) { + this.visConfigArgs[name] = val; + this.render(this.data, this.uiState); + } + + /** + * Gets attributes from the visualization + * + * @method get + * @param name {String} An attribute name + * @returns {*} The value of the attribute name + */ + get(name) { + return this.visConfig.get(name); + } + + /** + * Turns on event listeners. + * + * @param event {String} + * @param listener{Function} + * @returns {*} + */ + on(event, listener) { + const first = this.listenerCount(event) === 0; + const ret = EventEmitter.prototype.on.call(this, event, listener); + const added = this.listenerCount(event) > 0; + + // if this is the first listener added for the event + // enable the event in the handler + if (first && added && this.handler) this.handler.enable(event); + + return ret; + } + + /** + * Turns off event listeners. + * + * @param event {String} + * @param listener{Function} + * @returns {*} + */ + off(event, listener) { + const last = this.listenerCount(event) === 1; + const ret = EventEmitter.prototype.off.call(this, event, listener); + const removed = this.listenerCount(event) === 0; + + // Once all listeners are removed, disable the events in the handler + if (last && removed && this.handler) this.handler.disable(event); + return ret; + } + + removeAllListeners(event) { + const ret = EventEmitter.prototype.removeAllListeners.call(this, event); + this.handler.disable(event); + return ret; + } +} diff --git a/src/legacy/ui/public/vislib/vislib.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/vislib.js similarity index 82% rename from src/legacy/ui/public/vislib/vislib.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/vislib.js index 847deb8273a73..024dee60ef2bf 100644 --- a/src/legacy/ui/public/vislib/vislib.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/vislib.js @@ -23,8 +23,7 @@ import './lib/types'; import './lib/layout/layout_types'; import './lib/data'; import './visualizations/vis_types'; -import { mappedColors } from '../vis/components/color/mapped_colors'; -import { VislibVisProvider } from './vis'; +import { Vis } from './vis'; // prefetched for faster optimization runs // end prefetching @@ -36,11 +35,9 @@ import { VislibVisProvider } from './vis'; * @main vislib * @return {Object} Contains the version number and the Vis Class for creating visualizations */ -export function VislibProvider(Private, $rootScope) { - $rootScope.$on('$routeChangeStart', () => mappedColors.purge()); - +export function VislibProvider() { return { version: '0.0.0', - Vis: Private(VislibVisProvider), + Vis, }; } diff --git a/src/legacy/ui/public/vislib/visualizations/_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/_chart.js similarity index 81% rename from src/legacy/ui/public/vislib/visualizations/_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/_chart.js index 2fbd96d6a070a..ac6e8130a846a 100644 --- a/src/legacy/ui/public/vislib/visualizations/_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/_chart.js @@ -19,12 +19,10 @@ import d3 from 'd3'; import _ from 'lodash'; + import { dataLabel } from '../lib/_data_label'; import { Dispatch } from '../lib/dispatch'; -import { Tooltip } from '../../vis/components/tooltip'; -import { getFormat } from '../../visualize/loader/pipeline_helpers/utilities'; -import { getHierarchicalTooltipFormatter } from '../../vis/components/tooltip/_hierarchical_tooltip_formatter'; -import { getPointSeriesTooltipFormatter } from '../../vis/components/tooltip/_pointseries_tooltip_formatter'; +import { Tooltip, getFormat } from '../../legacy_imports'; /** * The Base Class for all visualizations. @@ -36,26 +34,26 @@ import { getPointSeriesTooltipFormatter } from '../../vis/components/tooltip/_po * @param chartData {Object} Elasticsearch query results for this specific chart */ export class Chart { - constructor(handler, el, chartData) { + constructor(handler, element, chartData, deps) { this.handler = handler; - this.chartEl = el; + this.chartEl = element; this.chartData = chartData; this.tooltips = []; - const events = (this.events = new Dispatch(handler)); + const events = (this.events = new Dispatch(handler, deps.uiSettings)); const fieldFormatter = getFormat(this.handler.data.get('tooltipFormatter')); const tooltipFormatterProvider = this.handler.visConfig.get('type') === 'pie' - ? getHierarchicalTooltipFormatter() - : getPointSeriesTooltipFormatter(); + ? deps.getHierarchicalTooltipFormatter() + : deps.getPointSeriesTooltipFormatter(); const tooltipFormatter = tooltipFormatterProvider(fieldFormatter); if (this.handler.visConfig && this.handler.visConfig.get('addTooltip', false)) { - const $el = this.handler.el; + const element = this.handler.el; // Add tooltip - this.tooltip = new Tooltip('chart', $el, tooltipFormatter, events); + this.tooltip = new Tooltip('chart', element, tooltipFormatter, events); this.tooltips.push(this.tooltip); } diff --git a/src/legacy/ui/public/vislib/visualizations/gauge_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js similarity index 97% rename from src/legacy/ui/public/vislib/visualizations/gauge_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js index c67b1b8881d69..729c664032603 100644 --- a/src/legacy/ui/public/vislib/visualizations/gauge_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauge_chart.js @@ -22,8 +22,8 @@ import { Chart } from './_chart'; import { gaugeTypes } from './gauges/gauge_types'; export class GaugeChart extends Chart { - constructor(handler, chartEl, chartData) { - super(handler, chartEl, chartData); + constructor(handler, chartEl, chartData, deps) { + super(handler, chartEl, chartData, deps); this.gaugeConfig = handler.visConfig.get('gauge', {}); this.gauge = new gaugeTypes[this.gaugeConfig.type](this); } diff --git a/src/legacy/ui/public/vislib/visualizations/gauges/_index.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/_index.scss similarity index 100% rename from src/legacy/ui/public/vislib/visualizations/gauges/_index.scss rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/_index.scss diff --git a/src/legacy/ui/public/vislib/visualizations/gauges/_meter.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/_meter.scss similarity index 100% rename from src/legacy/ui/public/vislib/visualizations/gauges/_meter.scss rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/_meter.scss diff --git a/src/legacy/ui/public/vislib/visualizations/gauges/gauge_types.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/gauge_types.js similarity index 100% rename from src/legacy/ui/public/vislib/visualizations/gauges/gauge_types.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/gauge_types.js diff --git a/src/legacy/ui/public/vislib/visualizations/gauges/meter.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js similarity index 99% rename from src/legacy/ui/public/vislib/visualizations/gauges/meter.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js index 25da3ba957181..f519914662251 100644 --- a/src/legacy/ui/public/vislib/visualizations/gauges/meter.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/gauges/meter.js @@ -19,7 +19,8 @@ import d3 from 'd3'; import _ from 'lodash'; -import { getHeatmapColors } from '../../components/color/heatmap_color'; + +import { getHeatmapColors } from '../../../legacy_imports'; const arcAngles = { angleFactor: 0.75, diff --git a/src/legacy/ui/public/vislib/visualizations/pie_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/pie_chart.js similarity index 99% rename from src/legacy/ui/public/vislib/visualizations/pie_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/pie_chart.js index 6d51c69892bc0..a2cbf0fe295ce 100644 --- a/src/legacy/ui/public/vislib/visualizations/pie_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/pie_chart.js @@ -42,8 +42,8 @@ const defaults = { * @param chartData {Object} Elasticsearch query results for this specific chart */ export class PieChart extends Chart { - constructor(handler, chartEl, chartData) { - super(handler, chartEl, chartData); + constructor(handler, chartEl, chartData, deps) { + super(handler, chartEl, chartData, deps); const charts = this.handler.data.getVisData(); this._validatePieData(charts); this._attr = _.defaults(handler.visConfig.get('chart', {}), defaults); diff --git a/src/legacy/ui/public/vislib/visualizations/point_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series.js similarity index 97% rename from src/legacy/ui/public/vislib/visualizations/point_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series.js index 84dbea2ccc823..c838c51d34bf5 100644 --- a/src/legacy/ui/public/vislib/visualizations/point_series.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series.js @@ -20,7 +20,8 @@ import d3 from 'd3'; import _ from 'lodash'; import $ from 'jquery'; -import { Tooltip } from '../../vis/components/tooltip'; + +import { Tooltip } from '../../legacy_imports'; import { Chart } from './_chart'; import { TimeMarker } from './time_marker'; import { seriesTypes } from './point_series/series_types'; @@ -39,9 +40,10 @@ const touchdownTmpl = _.template(touchdownTmplHtml); * @param chartData {Object} Elasticsearch query results for this specific chart */ export class PointSeries extends Chart { - constructor(handler, chartEl, chartData) { - super(handler, chartEl, chartData); + constructor(handler, chartEl, chartData, deps) { + super(handler, chartEl, chartData, deps); + this.deps = deps; this.handler = handler; this.chartData = chartData; this.chartEl = chartEl; @@ -255,7 +257,7 @@ export class PointSeries extends Chart { if (!seriArgs.show) return; const SeriClass = seriTypes[seriArgs.type || self.handler.visConfig.get('chart.type')] || seriTypes.line; - const series = new SeriClass(self.handler, svg, data.series[i], seriArgs); + const series = new SeriClass(self.handler, svg, data.series[i], seriArgs, this.deps); series.events = self.events; svg.call(series.draw()); self.series.push(series); diff --git a/src/legacy/ui/public/vislib/visualizations/point_series/_index.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_index.scss similarity index 100% rename from src/legacy/ui/public/vislib/visualizations/point_series/_index.scss rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_index.scss diff --git a/src/legacy/ui/public/vislib/visualizations/point_series/_labels.scss b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_labels.scss similarity index 100% rename from src/legacy/ui/public/vislib/visualizations/point_series/_labels.scss rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_labels.scss diff --git a/src/legacy/ui/public/vislib/visualizations/point_series/_point_series.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_point_series.js similarity index 100% rename from src/legacy/ui/public/vislib/visualizations/point_series/_point_series.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/_point_series.js diff --git a/src/legacy/ui/public/vislib/visualizations/point_series/area_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js similarity index 98% rename from src/legacy/ui/public/vislib/visualizations/point_series/area_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js index 08147bacdcc98..274ae82271e96 100644 --- a/src/legacy/ui/public/vislib/visualizations/point_series/area_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/area_chart.js @@ -43,8 +43,8 @@ const defaults = { * chart */ export class AreaChart extends PointSeries { - constructor(handler, chartEl, chartData, seriesConfigArgs) { - super(handler, chartEl, chartData, seriesConfigArgs); + constructor(handler, chartEl, chartData, seriesConfigArgs, deps) { + super(handler, chartEl, chartData, seriesConfigArgs, deps); this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults); this.isOverlapping = this.seriesConfig.mode !== 'stacked'; diff --git a/src/legacy/ui/public/vislib/visualizations/point_series/column_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js similarity index 98% rename from src/legacy/ui/public/vislib/visualizations/point_series/column_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js index 1f18141d86299..4b422d9b1419f 100644 --- a/src/legacy/ui/public/vislib/visualizations/point_series/column_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/column_chart.js @@ -57,8 +57,8 @@ function datumWidth(defaultWidth, datum, nextDatum, scale, gutterWidth, groupCou * @param chartData {Object} Elasticsearch query results for this specific chart */ export class ColumnChart extends PointSeries { - constructor(handler, chartEl, chartData, seriesConfigArgs) { - super(handler, chartEl, chartData, seriesConfigArgs); + constructor(handler, chartEl, chartData, seriesConfigArgs, deps) { + super(handler, chartEl, chartData, seriesConfigArgs, deps); this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults); this.labelOptions = _.defaults(handler.visConfig.get('labels', {}), defaults.showLabel); } diff --git a/src/legacy/ui/public/vislib/visualizations/point_series/heatmap_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js similarity index 98% rename from src/legacy/ui/public/vislib/visualizations/point_series/heatmap_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js index 1bc33bd5bbade..948cf98a64352 100644 --- a/src/legacy/ui/public/vislib/visualizations/point_series/heatmap_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/heatmap_chart.js @@ -19,10 +19,12 @@ import _ from 'lodash'; import moment from 'moment'; -import { PointSeries } from './_point_series'; -import { getHeatmapColors } from '../../components/color/heatmap_color'; + import { isColorDark } from '@elastic/eui'; +import { PointSeries } from './_point_series'; +import { getHeatmapColors } from '../../../legacy_imports'; + const defaults = { color: undefined, // todo fillColor: undefined, // todo @@ -38,8 +40,8 @@ const defaults = { * @param chartData {Object} Elasticsearch query results for this specific chart */ export class HeatmapChart extends PointSeries { - constructor(handler, chartEl, chartData, seriesConfigArgs) { - super(handler, chartEl, chartData, seriesConfigArgs); + constructor(handler, chartEl, chartData, seriesConfigArgs, deps) { + super(handler, chartEl, chartData, seriesConfigArgs, deps); this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults); this.handler.visConfig.set('legend', { diff --git a/src/legacy/ui/public/vislib/visualizations/point_series/line_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js similarity index 98% rename from src/legacy/ui/public/vislib/visualizations/point_series/line_chart.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js index 57090b5424f93..0038b4401b302 100644 --- a/src/legacy/ui/public/vislib/visualizations/point_series/line_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/line_chart.js @@ -42,8 +42,8 @@ const defaults = { * @param chartData {Object} Elasticsearch query results for this specific chart */ export class LineChart extends PointSeries { - constructor(handler, chartEl, chartData, seriesConfigArgs) { - super(handler, chartEl, chartData, seriesConfigArgs); + constructor(handler, chartEl, chartData, seriesConfigArgs, deps) { + super(handler, chartEl, chartData, seriesConfigArgs, deps); this.seriesConfig = _.defaults(seriesConfigArgs || {}, defaults); } diff --git a/src/legacy/ui/public/vislib/visualizations/point_series/series_types.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/series_types.js similarity index 100% rename from src/legacy/ui/public/vislib/visualizations/point_series/series_types.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/point_series/series_types.js diff --git a/src/legacy/ui/public/vislib/visualizations/time_marker.d.ts b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/time_marker.d.ts similarity index 100% rename from src/legacy/ui/public/vislib/visualizations/time_marker.d.ts rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/time_marker.d.ts diff --git a/src/legacy/ui/public/vislib/visualizations/time_marker.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/time_marker.js similarity index 100% rename from src/legacy/ui/public/vislib/visualizations/time_marker.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/time_marker.js diff --git a/src/legacy/ui/public/vislib/visualizations/vis_types.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/vis_types.js similarity index 100% rename from src/legacy/ui/public/vislib/visualizations/vis_types.js rename to src/legacy/core_plugins/vis_type_vislib/public/vislib/visualizations/vis_types.js diff --git a/src/legacy/core_plugins/visualizations/public/legacy_imports.ts b/src/legacy/core_plugins/visualizations/public/legacy_imports.ts index 92d8ac2c7db3a..e10cbe9aefea6 100644 --- a/src/legacy/core_plugins/visualizations/public/legacy_imports.ts +++ b/src/legacy/core_plugins/visualizations/public/legacy_imports.ts @@ -27,4 +27,6 @@ export { } from '../../../ui/public/agg_types/buckets/date_histogram'; export { createFormat } from '../../../ui/public/visualize/loader/pipeline_helpers/utilities'; export { I18nContext } from '../../../ui/public/i18n'; +import chrome from '../../../ui/public/chrome'; +export { chrome as legacyChrome }; import '../../../ui/public/directives/bind'; diff --git a/src/legacy/core_plugins/visualizations/public/expressions/vis.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.js similarity index 96% rename from src/legacy/core_plugins/visualizations/public/expressions/vis.js rename to src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.js index cbadfd9da3788..81224b65f7786 100644 --- a/src/legacy/core_plugins/visualizations/public/expressions/vis.js +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.js @@ -29,8 +29,9 @@ import { EventEmitter } from 'events'; import _ from 'lodash'; -import { PersistedState } from '../../../../ui/public/persisted_state'; -import { getTypes } from '../np_ready/public/services'; +import { PersistedState } from '../../../legacy_imports'; + +import { getTypes } from '../services'; export class Vis extends EventEmitter { constructor(visState = { type: 'histogram' }) { diff --git a/src/legacy/core_plugins/visualizations/public/expressions/visualization_function.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_function.ts similarity index 87% rename from src/legacy/core_plugins/visualizations/public/expressions/visualization_function.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_function.ts index 37b437c1c2dd6..24eb974f6edee 100644 --- a/src/legacy/core_plugins/visualizations/public/expressions/visualization_function.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_function.ts @@ -19,13 +19,10 @@ import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; -import chrome from 'ui/chrome'; -import { FilterBarQueryFilterProvider } from 'ui/filter_manager/query_filter'; -import { PersistedState } from 'ui/persisted_state'; import { VisResponseValue } from 'src/plugins/visualizations/public'; import { ExpressionFunction, Render } from 'src/plugins/expressions/public'; -import { npStart } from 'ui/new_platform'; -import { getTypes } from '../np_ready/public/services'; +import { PersistedState } from '../../../legacy_imports'; +import { getTypes, getIndexPatterns, getFilterManager } from '../services'; interface Arguments { index?: string | null; @@ -90,15 +87,10 @@ export const visualization = (): ExpressionFunctionVisualization => ({ }, }, async fn(context, args, handlers) { - const $injector = await chrome.dangerouslyGetActiveInjector(); - const Private = $injector.get('Private') as any; - const indexPatterns = npStart.plugins.data.indexPatterns; - const queryFilter = Private(FilterBarQueryFilterProvider); - const visConfigParams = args.visConfig ? JSON.parse(args.visConfig) : {}; const schemas = args.schemas ? JSON.parse(args.schemas) : {}; const visType = getTypes().get(args.type || 'histogram') as any; - const indexPattern = args.index ? await indexPatterns.get(args.index) : null; + const indexPattern = args.index ? await getIndexPatterns().get(args.index) : null; const uiStateParams = args.uiState ? JSON.parse(args.uiState) : {}; const uiState = new PersistedState(uiStateParams); @@ -114,7 +106,7 @@ export const visualization = (): ExpressionFunctionVisualization => ({ filters: get(context, 'filters', null), uiState, inspectorAdapters: handlers.inspectorAdapters, - queryFilter, + queryFilter: getFilterManager(), forceFetch: true, }); } diff --git a/src/legacy/core_plugins/visualizations/public/expressions/visualization_renderer.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_renderer.tsx similarity index 92% rename from src/legacy/core_plugins/visualizations/public/expressions/visualization_renderer.tsx rename to src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_renderer.tsx index 5f894c7eb60e3..2a12884ecf7c8 100644 --- a/src/legacy/core_plugins/visualizations/public/expressions/visualization_renderer.tsx +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_renderer.tsx @@ -17,12 +17,12 @@ * under the License. */ -import chrome from 'ui/chrome'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; +import { legacyChrome } from '../../../legacy_imports'; // @ts-ignore import { Vis } from './vis'; -import { Visualization } from '../np_ready/public/components'; +import { Visualization } from '../components'; export const visualization = () => ({ name: 'visualization', @@ -31,7 +31,7 @@ export const visualization = () => ({ render: async (domNode: HTMLElement, config: any, handlers: any) => { const { visData, visConfig, params } = config; const visType = config.visType || visConfig.type; - const $injector = await chrome.dangerouslyGetActiveInjector(); + const $injector = await legacyChrome.dangerouslyGetActiveInjector(); const $rootScope = $injector.get('$rootScope') as any; if (handlers.vis) { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts index 7584556324e8b..cfd22f88167c5 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ + import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { TypesService, TypesSetup, TypesStart } from './types'; import { @@ -27,13 +28,14 @@ import { setIndexPatterns, setSavedObjects, setUsageCollector, + setFilterManager, } from './services'; import { VisualizeEmbeddableFactory } from '../../embeddable/visualize_embeddable_factory'; import { VISUALIZE_EMBEDDABLE_TYPE } from '../../embeddable'; import { ExpressionsSetup } from '../../../../../../plugins/expressions/public'; import { IEmbeddableSetup } from '../../../../../../plugins/embeddable/public'; -import { visualization as visualizationFunction } from '../../expressions/visualization_function'; -import { visualization as visualizationRenderer } from '../../expressions/visualization_renderer'; +import { visualization as visualizationFunction } from './expressions/visualization_function'; +import { visualization as visualizationRenderer } from './expressions/visualization_renderer'; import { DataPublicPluginStart } from '../../../../../../plugins/data/public'; import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/public'; /** @@ -106,6 +108,7 @@ export class VisualizationsPlugin setHttp(core.http); setSavedObjects(core.savedObjects); setIndexPatterns(data.indexPatterns); + setFilterManager(data.query.filterManager); return { types, diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts index 4b426fee6c7da..433c5c7b6df0d 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts @@ -26,7 +26,7 @@ import { } from 'src/core/public'; import { TypesStart } from './types'; import { createGetterSetter } from '../../../../../../plugins/kibana_utils/public'; -import { IndexPatternsContract } from '../../../../../../plugins/data/public'; +import { FilterManager, IndexPatternsContract } from '../../../../../../plugins/data/public'; import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/public'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); @@ -43,6 +43,10 @@ export const [getTypes, setTypes] = createGetterSetter('Types'); export const [getI18n, setI18n] = createGetterSetter('I18n'); +export const [getFilterManager, setFilterManager] = createGetterSetter( + 'FilterManager' +); + export const [getIndexPatterns, setIndexPatterns] = createGetterSetter( 'IndexPatterns' ); diff --git a/src/legacy/ui/public/UI_SYSTEMS.md b/src/legacy/ui/public/UI_SYSTEMS.md index 63fd602075653..37bfbcf92f640 100644 --- a/src/legacy/ui/public/UI_SYSTEMS.md +++ b/src/legacy/ui/public/UI_SYSTEMS.md @@ -6,7 +6,3 @@ In this directory you'll find various UI systems you can use to craft effective * [banners](notify/banners/BANNERS.md) * [toastNotifications](notify/toasts/TOAST_NOTIFICATIONS.md) - -## ui/vislib - -* [VisLib](vislib/VISLIB.md) \ No newline at end of file diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss index 747ad025ef691..f5a1d0a7922a7 100644 --- a/src/legacy/ui/public/_index.scss +++ b/src/legacy/ui/public/_index.scss @@ -26,5 +26,4 @@ // Can't import vis folder here because of cascading issues, it's imported in core_plugins/kibana // @import './vis/index'; -@import './vislib/index'; @import './visualize/index'; diff --git a/src/legacy/ui/public/vislib/components/color/colormaps.ts b/src/legacy/ui/public/color_maps/color_maps.ts similarity index 100% rename from src/legacy/ui/public/vislib/components/color/colormaps.ts rename to src/legacy/ui/public/color_maps/color_maps.ts diff --git a/src/legacy/ui/public/vislib/components/color/heatmap_color.js b/src/legacy/ui/public/color_maps/heatmap_color.js similarity index 98% rename from src/legacy/ui/public/vislib/components/color/heatmap_color.js rename to src/legacy/ui/public/color_maps/heatmap_color.js index 5e788cd1f3345..06d754235f88b 100644 --- a/src/legacy/ui/public/vislib/components/color/heatmap_color.js +++ b/src/legacy/ui/public/color_maps/heatmap_color.js @@ -18,7 +18,7 @@ */ import _ from 'lodash'; -import { vislibColorMaps } from './colormaps'; +import { vislibColorMaps } from './color_maps'; function enforceBounds(x) { if (x < 0) { diff --git a/src/legacy/ui/public/color_maps/index.ts b/src/legacy/ui/public/color_maps/index.ts new file mode 100644 index 0000000000000..50dfe682f4418 --- /dev/null +++ b/src/legacy/ui/public/color_maps/index.ts @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './color_maps'; +// @ts-ignore +export { getHeatmapColors } from './heatmap_color'; +// @ts-ignore +export * from './truncated_color_maps'; diff --git a/src/legacy/ui/public/vislib/components/color/truncated_colormaps.js b/src/legacy/ui/public/color_maps/truncated_color_maps.js similarity index 86% rename from src/legacy/ui/public/vislib/components/color/truncated_colormaps.js rename to src/legacy/ui/public/color_maps/truncated_color_maps.js index ccf005b3726a8..cb7772c875e48 100644 --- a/src/legacy/ui/public/vislib/components/color/truncated_colormaps.js +++ b/src/legacy/ui/public/color_maps/truncated_color_maps.js @@ -17,14 +17,14 @@ * under the License. */ -import { vislibColorMaps } from './colormaps'; +import { vislibColorMaps } from './color_maps'; export const truncatedColorMaps = {}; const colormaps = vislibColorMaps; for (const key in colormaps) { if (colormaps.hasOwnProperty(key)) { - //slice off lightest colors + // slice off lightest colors truncatedColorMaps[key] = { ...colormaps[key], value: colormaps[key].value.slice(Math.floor(colormaps[key].value.length / 4)), @@ -32,7 +32,7 @@ for (const key in colormaps) { } } -export const colorSchemas = Object.values(truncatedColorMaps).map(({ id, label }) => ({ +export const truncatedColorSchemas = Object.values(truncatedColorMaps).map(({ id, label }) => ({ value: id, text: label, })); diff --git a/src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip_formatter.js b/src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip_formatter.js index 26a9c5b008f70..aef7bc3913a49 100644 --- a/src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip_formatter.js +++ b/src/legacy/ui/public/vis/components/tooltip/_hierarchical_tooltip_formatter.js @@ -19,6 +19,9 @@ import _ from 'lodash'; import $ from 'jquery'; + +import chrome from 'ui/chrome'; + import { collectBranch } from './_collect_branch'; import numeral from 'numeral'; import template from './_hierarchical_tooltip.html'; @@ -68,6 +71,12 @@ export const getHierarchicalTooltipFormatter = () => { return _tooltipFormatter; }; +export const initializeHierarchicalTooltipFormatter = async () => { + const $injector = await chrome.dangerouslyGetActiveInjector(); + const Private = $injector.get('Private'); + _tooltipFormatter = Private(HierarchicalTooltipFormatterProvider); +}; + export const setHierarchicalTooltipFormatter = Private => { _tooltipFormatter = Private(HierarchicalTooltipFormatterProvider); }; diff --git a/src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip_formatter.js b/src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip_formatter.js index fa0b030c736c1..88c9e3d67b4a9 100644 --- a/src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip_formatter.js +++ b/src/legacy/ui/public/vis/components/tooltip/_pointseries_tooltip_formatter.js @@ -18,6 +18,9 @@ */ import $ from 'jquery'; + +import chrome from 'ui/chrome'; + import template from './_pointseries_tooltip.html'; export function PointSeriesTooltipFormatterProvider($compile, $rootScope) { @@ -75,6 +78,12 @@ export const getPointSeriesTooltipFormatter = () => { return _tooltipFormatter; }; +export const initializePointSeriesTooltipFormatter = async () => { + const $injector = await chrome.dangerouslyGetActiveInjector(); + const Private = $injector.get('Private'); + _tooltipFormatter = Private(PointSeriesTooltipFormatterProvider); +}; + export const setPointSeriesTooltipFormatter = Private => { _tooltipFormatter = Private(PointSeriesTooltipFormatterProvider); }; diff --git a/src/legacy/ui/public/vis/editors/default/agg_groups.ts b/src/legacy/ui/public/vis/editors/default/agg_groups.ts index f55e6ecd79155..e84306144fa63 100644 --- a/src/legacy/ui/public/vis/editors/default/agg_groups.ts +++ b/src/legacy/ui/public/vis/editors/default/agg_groups.ts @@ -18,11 +18,14 @@ */ import { i18n } from '@kbn/i18n'; +import { $Values } from '@kbn/utility-types'; -export enum AggGroupNames { - Buckets = 'buckets', - Metrics = 'metrics', -} +export const AggGroupNames = Object.freeze({ + Buckets: 'buckets' as 'buckets', + Metrics: 'metrics' as 'metrics', + None: 'none' as 'none', +}); +export type AggGroupNames = $Values; export const aggGroupNamesMap = () => ({ [AggGroupNames.Metrics]: i18n.translate('common.ui.vis.editors.aggGroups.metricsText', { diff --git a/src/legacy/ui/public/vis/editors/default/components/agg_group.tsx b/src/legacy/ui/public/vis/editors/default/components/agg_group.tsx index 528914f4fd006..1c8690f6deb79 100644 --- a/src/legacy/ui/public/vis/editors/default/components/agg_group.tsx +++ b/src/legacy/ui/public/vis/editors/default/components/agg_group.tsx @@ -66,7 +66,7 @@ function DefaultEditorAggGroup({ setTouched, setValidity, }: DefaultEditorAggGroupProps) { - const groupNameLabel = aggGroupNamesMap()[groupName]; + const groupNameLabel = (aggGroupNamesMap() as any)[groupName]; // e.g. buckets can have no aggs const group: AggConfig[] = state.aggs.aggs.filter((agg: AggConfig) => agg.schema.group === groupName) || []; diff --git a/src/legacy/ui/public/vis/editors/default/schemas.js b/src/legacy/ui/public/vis/editors/default/schemas.ts similarity index 70% rename from src/legacy/ui/public/vis/editors/default/schemas.js rename to src/legacy/ui/public/vis/editors/default/schemas.ts index 69449dc8504a8..e86a73732c3f4 100644 --- a/src/legacy/ui/public/vis/editors/default/schemas.js +++ b/src/legacy/ui/public/vis/editors/default/schemas.ts @@ -18,13 +18,44 @@ */ import _ from 'lodash'; + +import { Optional } from '@kbn/utility-types'; + +import { AggParam } from '../../../agg_types'; import { IndexedArray } from '../../../indexed_array'; import { RowsOrColumnsControl } from './controls/rows_or_columns'; import { RadiusRatioOptionControl } from './controls/radius_ratio_option'; import { AggGroupNames } from './agg_groups'; +import { AggControlProps } from './controls/agg_control_props'; + +export interface Schema { + aggFilter: string | string[]; + editor: boolean | string; + group: AggGroupNames; + max: number; + min: number; + name: string; + params: AggParam[]; + title: string; + defaults: unknown; + hideCustomLabel?: boolean; + mustBeFirst?: boolean; + aggSettings?: any; + editorComponent?: React.ComponentType; +} class Schemas { - constructor(schemas) { + // @ts-ignore + all: IndexedArray; + + constructor( + schemas: Array< + Optional< + Schema, + 'min' | 'max' | 'group' | 'title' | 'aggFilter' | 'editor' | 'params' | 'defaults' + > + > + ) { _(schemas || []) .map(schema => { if (!schema.name) throw new Error('all schema must have a unique name'); @@ -35,7 +66,7 @@ class Schemas { name: 'row', default: true, }, - ]; + ] as AggParam[]; schema.editorComponent = RowsOrColumnsControl; } else if (schema.name === 'radius') { schema.editorComponent = RadiusRatioOptionControl; @@ -51,21 +82,23 @@ class Schemas { params: [], }); - return schema; + return schema as Schema; }) - .tap(schemas => { + .tap((fullSchemas: Schema[]) => { this.all = new IndexedArray({ index: ['name'], group: ['group'], immutable: true, - initialSet: schemas, + initialSet: fullSchemas, }); }) .groupBy('group') .forOwn((group, groupName) => { + // @ts-ignore this[groupName] = new IndexedArray({ index: ['name'], immutable: true, + // @ts-ignore initialSet: group, }); }) diff --git a/src/legacy/ui/public/vis/vis_types/_vislib_vis_legend.scss b/src/legacy/ui/public/vis/vis_types/_vislib_vis_legend.scss index 4d7c0e2bdcadb..62050ce4e99fd 100644 --- a/src/legacy/ui/public/vis/vis_types/_vislib_vis_legend.scss +++ b/src/legacy/ui/public/vis/vis_types/_vislib_vis_legend.scss @@ -1,4 +1,4 @@ -@import '../../vislib/variables'; +@import '../../../../core_plugins/vis_type_vislib/public/vislib/variables'; // NOTE: Some of the styles attempt to align with the TSVB legend diff --git a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/pie_utils.ts b/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/pie_utils.ts new file mode 100644 index 0000000000000..d9eea83d40b48 --- /dev/null +++ b/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/pie_utils.ts @@ -0,0 +1,103 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import _ from 'lodash'; + +/** + * Returns an array of names ordered by appearance in the nested array + * of objects + * + * > Duplicated utilty method from vislib Data class to decouple `vislib_vis_legend` from `vislib` + * + * @see src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/data.js + * + * @returns {Array} Array of unique names (strings) + */ +export function getPieNames(data: any[]): string[] { + const names: string[] = []; + + _.forEach(data, function(obj) { + const columns = obj.raw ? obj.raw.columns : undefined; + _.forEach(getNames(obj, columns), function(name) { + names.push(name); + }); + }); + + return _.uniq(names, 'label'); +} + +/** + * Flattens hierarchical data into an array of objects with a name and index value. + * The indexed value determines the order of nesting in the data. + * Returns an array with names sorted by the index value. + * + * @param data {Object} Chart data object + * @param columns {Object} Contains formatter information + * @returns {Array} Array of names (strings) + */ +function getNames(data: any, columns: any): string[] { + const slices = data.slices; + + if (slices.children) { + const namedObj = returnNames(slices.children, 0, columns); + + return _(namedObj) + .sortBy(function(obj) { + return obj.index; + }) + .unique(function(d) { + return d.label; + }) + .value(); + } + + return []; +} + +/** + * Helper function for getNames + * Returns an array of objects with a name (key) value and an index value. + * The index value allows us to sort the names in the correct nested order. + * + * @param array {Array} Array of data objects + * @param index {Number} Number of times the object is nested + * @param columns {Object} Contains name formatter information + * @returns {Array} Array of labels (strings) + */ +function returnNames(array: any[], index: number, columns: any): any[] { + const names: any[] = []; + + _.forEach(array, function(obj) { + names.push({ + label: obj.name, + values: [obj.rawData], + index, + }); + + if (obj.children) { + const plusIndex = index + 1; + + _.forEach(returnNames(obj.children, plusIndex, columns), function(namedObj) { + names.push(namedObj); + }); + } + }); + + return names; +} diff --git a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.tsx b/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.tsx index f0100e369f050..d98590f9885b9 100644 --- a/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.tsx +++ b/src/legacy/ui/public/vis/vis_types/vislib_vis_legend/vislib_vis_legend.tsx @@ -23,12 +23,11 @@ import { compact, uniq, map } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiPopoverProps, EuiIcon, keyCodes, htmlIdGenerator } from '@elastic/eui'; -// @ts-ignore -import { Data } from '../../../vislib/lib/data'; // @ts-ignore import { createFiltersFromEvent } from '../../../../../core_plugins/visualizations/public'; import { CUSTOM_LEGEND_VIS_TYPES, LegendItem } from './models'; import { VisLegendItem } from './vislib_vis_legend_item'; +import { getPieNames } from './pie_utils'; import { getTableAggs } from '../../../visualize/loader/pipeline_helpers/utilities'; export interface VisLegendProps { @@ -128,7 +127,7 @@ export class VisLegend extends PureComponent { if (!data) return []; data = data.columns || data.rows || [data]; - if (type === 'pie') return Data.prototype.pieNames(data); + if (type === 'pie') return getPieNames(data); return this.getSeriesLabels(data); }; diff --git a/src/legacy/ui/public/vislib/vis.js b/src/legacy/ui/public/vislib/vis.js deleted file mode 100644 index 6ce58c1f5b23b..0000000000000 --- a/src/legacy/ui/public/vislib/vis.js +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import d3 from 'd3'; -import { EventEmitter } from 'events'; -import chrome from '../chrome'; -import { VislibError } from './errors'; -import { VisConfig } from './lib/vis_config'; -import { Handler } from './lib/handler'; -import { setHierarchicalTooltipFormatter } from '../vis/components/tooltip/_hierarchical_tooltip_formatter'; -import { setPointSeriesTooltipFormatter } from '../vis/components/tooltip/_pointseries_tooltip_formatter'; - -const config = chrome.getUiSettingsClient(); - -export function VislibVisProvider(Private) { - setHierarchicalTooltipFormatter(Private); - setPointSeriesTooltipFormatter(Private); - - /** - * Creates the visualizations. - * - * @class Vis - * @constructor - * @param $el {HTMLElement} jQuery selected HTML element - * @param config {Object} Parameters that define the chart type and chart options - */ - class Vis extends EventEmitter { - constructor($el, visConfigArgs) { - super(); - this.el = $el.get ? $el.get(0) : $el; - this.visConfigArgs = _.cloneDeep(visConfigArgs); - this.visConfigArgs.dimmingOpacity = config.get('visualization:dimmingOpacity'); - this.visConfigArgs.heatmapMaxBuckets = config.get('visualization:heatmap:maxBuckets'); - } - - hasLegend() { - return this.visConfigArgs.addLegend; - } - - initVisConfig(data, uiState) { - this.data = data; - - this.uiState = uiState; - - this.visConfig = new VisConfig(this.visConfigArgs, this.data, this.uiState, this.el); - } - - /** - * Renders the visualization - * - * @method render - * @param data {Object} Elasticsearch query results - */ - render(data, uiState) { - if (!data) { - throw new Error('No valid data!'); - } - - if (this.handler) { - this.data = null; - this._runOnHandler('destroy'); - } - - this.initVisConfig(data, uiState); - - this.handler = new Handler(this, this.visConfig); - this._runOnHandler('render'); - } - - getLegendLabels() { - return this.visConfig ? this.visConfig.get('legend.labels', null) : null; - } - - getLegendColors() { - return this.visConfig ? this.visConfig.get('legend.colors', null) : null; - } - - _runOnHandler(method) { - try { - this.handler[method](); - } catch (error) { - if (error instanceof VislibError) { - error.displayToScreen(this.handler); - } else { - throw error; - } - } - } - - /** - * Destroys the visualization - * Removes chart and all elements associated with it. - * Removes chart and all elements associated with it. - * Remove event listeners and pass destroy call down to owned objects. - * - * @method destroy - */ - destroy() { - const selection = d3.select(this.el).select('.visWrapper'); - - if (this.handler) this._runOnHandler('destroy'); - - selection.remove(); - } - - /** - * Sets attributes on the visualization - * - * @method set - * @param name {String} An attribute name - * @param val {*} Value to which the attribute name is set - */ - set(name, val) { - this.visConfigArgs[name] = val; - this.render(this.data, this.uiState); - } - - /** - * Gets attributes from the visualization - * - * @method get - * @param name {String} An attribute name - * @returns {*} The value of the attribute name - */ - get(name) { - return this.visConfig.get(name); - } - - /** - * Turns on event listeners. - * - * @param event {String} - * @param listener{Function} - * @returns {*} - */ - on(event, listener) { - const first = this.listenerCount(event) === 0; - const ret = EventEmitter.prototype.on.call(this, event, listener); - const added = this.listenerCount(event) > 0; - - // if this is the first listener added for the event - // enable the event in the handler - if (first && added && this.handler) this.handler.enable(event); - - return ret; - } - - /** - * Turns off event listeners. - * - * @param event {String} - * @param listener{Function} - * @returns {*} - */ - off(event, listener) { - const last = this.listenerCount(event) === 1; - const ret = EventEmitter.prototype.off.call(this, event, listener); - const removed = this.listenerCount(event) === 0; - - // Once all listeners are removed, disable the events in the handler - if (last && removed && this.handler) this.handler.disable(event); - return ret; - } - - removeAllListeners(event) { - const ret = EventEmitter.prototype.removeAllListeners.call(this, event); - this.handler.disable(event); - return ret; - } - } - - return Vis; -} diff --git a/src/plugins/bfetch/README.md b/src/plugins/bfetch/README.md index 9c18720e30d96..9ed90a4de306e 100644 --- a/src/plugins/bfetch/README.md +++ b/src/plugins/bfetch/README.md @@ -3,7 +3,54 @@ `bfetch` allows to batch HTTP requests and streams responses back. +# Example + +We will create a batch processing endpoint that receives a number then doubles it +and streams it back. We will also consider the number to be time in milliseconds +and before streaming the number back the server will wait for the specified number of +milliseconds. + +To do that, first create server-side batch processing route using [`addBatchProcessingRoute`](./docs/server/reference.md#addBatchProcessingRoute). + +```ts +plugins.bfetch.addBatchProcessingRoute<{ num: number }, { num: number }>( + '/my-plugin/double', + () => ({ + onBatchItem: async ({ num }) => { + // Validate inputs. + if (num < 0) throw new Error('Invalid number'); + // Wait number of specified milliseconds. + await new Promise(r => setTimeout(r, num)); + // Double the number and send it back. + return { num: 2 * num }; + }, + }) +); +``` + +Now on client-side create `double` function using [`batchedFunction`](./docs/browser/reference.md#batchedFunction). +The newly created `double` function can be called many times and it +will package individual calls into batches and send them to the server. + +```ts +const double = plugins.bfetch.batchedFunction<{ num: number }, { num: number }>({ + url: '/my-plugin/double', +}); +``` + +Note: the created `double` must accept a single object argument (`{ num: number }` in this case) +and it will return a promise that resolves into an object, too (also `{ num: number }` in this case). + +Use the `double` function. + +```ts +double({ num: 1 }).then(console.log, console.error); // { num: 2 } +double({ num: 2 }).then(console.log, console.error); // { num: 4 } +double({ num: 3 }).then(console.log, console.error); // { num: 6 } +``` + + ## Reference - [Browser](./docs/browser/reference.md) -- Server +- [Server](./docs/server/reference.md) diff --git a/src/plugins/bfetch/common/batch.ts b/src/plugins/bfetch/common/batch.ts new file mode 100644 index 0000000000000..6fd2c7e35ed91 --- /dev/null +++ b/src/plugins/bfetch/common/batch.ts @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export interface ErrorLike { + message: string; +} + +export interface BatchRequestData { + batch: Item[]; +} + +export interface BatchResponseItem { + id: number; + result?: Result; + error?: Error; +} diff --git a/src/plugins/bfetch/common/buffer/create_batched_function.ts b/src/plugins/bfetch/common/buffer/create_batched_function.ts new file mode 100644 index 0000000000000..24f28659863a7 --- /dev/null +++ b/src/plugins/bfetch/common/buffer/create_batched_function.ts @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ItemBufferParams } from './item_buffer'; +import { TimedItemBufferParams, TimedItemBuffer } from './timed_item_buffer'; + +type Fn = (...args: any) => any; + +export interface BatchedFunctionParams { + onCall: (...args: Parameters) => [ReturnType, BatchEntry]; + onBatch: (items: BatchEntry[]) => void; + flushOnMaxItems?: ItemBufferParams['flushOnMaxItems']; + maxItemAge?: TimedItemBufferParams['maxItemAge']; +} + +export const createBatchedFunction = ( + params: BatchedFunctionParams +): [Func, TimedItemBuffer] => { + const { onCall, onBatch, maxItemAge = 10, flushOnMaxItems = 25 } = params; + const buffer = new TimedItemBuffer({ + onFlush: onBatch, + maxItemAge, + flushOnMaxItems, + }); + + const fn: Func = ((...args) => { + const [result, batchEntry] = onCall(...args); + buffer.write(batchEntry); + return result; + }) as Func; + + return [fn, buffer]; +}; diff --git a/src/plugins/bfetch/common/buffer/index.ts b/src/plugins/bfetch/common/buffer/index.ts new file mode 100644 index 0000000000000..33bc52733289b --- /dev/null +++ b/src/plugins/bfetch/common/buffer/index.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './item_buffer'; +export * from './timed_item_buffer'; +export * from './create_batched_function'; diff --git a/src/plugins/bfetch/common/buffer/item_buffer.ts b/src/plugins/bfetch/common/buffer/item_buffer.ts new file mode 100644 index 0000000000000..663aa5d7b0b7f --- /dev/null +++ b/src/plugins/bfetch/common/buffer/item_buffer.ts @@ -0,0 +1,81 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export interface ItemBufferParams { + /** + * Flushes buffer automatically if number of items in the buffer reaches + * this number. Omit it or set to `Infinity` to never flush on max buffer + * size automatically. + */ + flushOnMaxItems?: number; + + /** + * Callback that is called every time buffer is flushed. It receives a single + * argument which is a list of all buffered items. If `.flush()` is called + * when buffer is empty, `.onflush` is called with empty array. + */ + onFlush: (items: Item[]) => void; +} + +/** + * A simple buffer that collects items. Can be cleared or flushed; and can + * automatically flush when specified number of items is reached. + */ +export class ItemBuffer { + private list: Item[] = []; + + constructor(public readonly params: ItemBufferParams) {} + + /** + * Get current buffer size. + */ + public get length(): number { + return this.list.length; + } + + /** + * Add item to the buffer. + */ + public write(item: Item) { + this.list.push(item); + + const { flushOnMaxItems } = this.params; + if (flushOnMaxItems) { + if (this.list.length >= flushOnMaxItems) { + this.flush(); + } + } + } + + /** + * Remove all items from the buffer. + */ + public clear() { + this.list = []; + } + + /** + * Call `.onflush` method and clear buffer. + */ + public flush() { + let list; + [list, this.list] = [this.list, []]; + this.params.onFlush(list); + } +} diff --git a/src/plugins/bfetch/common/buffer/tests/create_batched_function.test.ts b/src/plugins/bfetch/common/buffer/tests/create_batched_function.test.ts new file mode 100644 index 0000000000000..5b145a2523070 --- /dev/null +++ b/src/plugins/bfetch/common/buffer/tests/create_batched_function.test.ts @@ -0,0 +1,75 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { createBatchedFunction } from '../create_batched_function'; + +describe('createBatchedFunction', () => { + test('calls onCall every time fn is called, calls onBatch once flushOnMaxItems reached', async () => { + const onBatch = jest.fn(); + const onCall = jest.fn(() => [1, 2] as any); + const [fn] = createBatchedFunction({ + onBatch, + onCall, + flushOnMaxItems: 2, + maxItemAge: 10, + }); + + expect(onCall).toHaveBeenCalledTimes(0); + expect(onBatch).toHaveBeenCalledTimes(0); + + fn(123); + + expect(onCall).toHaveBeenCalledTimes(1); + expect(onCall).toHaveBeenCalledWith(123); + expect(onBatch).toHaveBeenCalledTimes(0); + + fn(456); + + expect(onCall).toHaveBeenCalledTimes(2); + expect(onCall).toHaveBeenCalledWith(456); + expect(onBatch).toHaveBeenCalledTimes(1); + expect(onBatch).toHaveBeenCalledWith([2, 2]); + }); + + test('calls onBatch once timeout is reached', async () => { + const onBatch = jest.fn(); + const onCall = jest.fn(() => [4, 3] as any); + const [fn] = createBatchedFunction({ + onBatch, + onCall, + flushOnMaxItems: 2, + maxItemAge: 10, + }); + + expect(onCall).toHaveBeenCalledTimes(0); + expect(onBatch).toHaveBeenCalledTimes(0); + + fn(123); + + expect(onCall).toHaveBeenCalledTimes(1); + expect(onCall).toHaveBeenCalledWith(123); + expect(onBatch).toHaveBeenCalledTimes(0); + + await new Promise(r => setTimeout(r, 15)); + + expect(onCall).toHaveBeenCalledTimes(1); + expect(onBatch).toHaveBeenCalledTimes(1); + expect(onBatch).toHaveBeenCalledWith([3]); + }); +}); diff --git a/src/plugins/bfetch/common/buffer/tests/item_buffer.test.ts b/src/plugins/bfetch/common/buffer/tests/item_buffer.test.ts new file mode 100644 index 0000000000000..a921fa8e589a3 --- /dev/null +++ b/src/plugins/bfetch/common/buffer/tests/item_buffer.test.ts @@ -0,0 +1,23 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ItemBuffer } from '../item_buffer'; +import { runItemBufferTests } from './run_item_buffer_tests'; + +runItemBufferTests(ItemBuffer); diff --git a/src/plugins/bfetch/common/buffer/tests/run_item_buffer_tests.ts b/src/plugins/bfetch/common/buffer/tests/run_item_buffer_tests.ts new file mode 100644 index 0000000000000..b3ba9375448dc --- /dev/null +++ b/src/plugins/bfetch/common/buffer/tests/run_item_buffer_tests.ts @@ -0,0 +1,239 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ItemBuffer, ItemBufferParams } from '../item_buffer'; + +export const runItemBufferTests = ( + Buffer: new >(params: Params) => ItemBuffer +) => { + describe('ItemBuffer', () => { + test('can create with or without optional "flushOnMaxItems" param', () => { + new Buffer({ + onFlush: () => {}, + }); + + new Buffer({ + onFlush: () => {}, + flushOnMaxItems: 123, + }); + }); + + test('can add items to the buffer', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + }); + + buf.write('a'); + buf.write('b'); + buf.write('c'); + }); + + test('returns number of items in the buffer', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + }); + + expect(buf.length).toBe(0); + buf.write('a'); + expect(buf.length).toBe(1); + buf.write('b'); + expect(buf.length).toBe(2); + buf.write('c'); + expect(buf.length).toBe(3); + }); + + test('returns correct number of items after .clear() was called', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + }); + + expect(buf.length).toBe(0); + buf.write('a'); + expect(buf.length).toBe(1); + buf.clear(); + buf.write('b'); + expect(buf.length).toBe(1); + buf.write('c'); + expect(buf.length).toBe(2); + }); + + test('returns correct number of items after .flush() was called', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + }); + + expect(buf.length).toBe(0); + buf.write('a'); + expect(buf.length).toBe(1); + buf.flush(); + buf.write('b'); + expect(buf.length).toBe(1); + buf.write('c'); + expect(buf.length).toBe(2); + }); + + test('can flush buffer and receive items in chronological order', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + }); + + buf.write('a'); + buf.write('b'); + buf.write('c'); + + buf.flush(); + + expect(onFlush).toHaveBeenCalledTimes(1); + expect(onFlush.mock.calls[0][0]).toEqual(['a', 'b', 'c']); + }); + + test('clears buffer after flush', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + }); + + buf.write('a'); + buf.write('b'); + buf.write('c'); + + buf.flush(); + + expect(onFlush).toHaveBeenCalledTimes(1); + expect(onFlush.mock.calls[0][0]).toEqual(['a', 'b', 'c']); + + buf.write('d'); + + buf.flush(); + + expect(onFlush).toHaveBeenCalledTimes(2); + expect(onFlush.mock.calls[1][0]).toEqual(['d']); + }); + + test('can call .flush() any time as many times as needed', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + }); + + buf.flush(); + buf.write(123); + buf.flush(); + buf.flush(); + buf.flush(); + + expect(onFlush).toHaveBeenCalledTimes(4); + expect(onFlush.mock.calls[0][0]).toEqual([]); + expect(onFlush.mock.calls[1][0]).toEqual([123]); + expect(onFlush.mock.calls[2][0]).toEqual([]); + expect(onFlush.mock.calls[3][0]).toEqual([]); + }); + + test('calling .clear() before .flush() cases to return empty list', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + }); + + buf.write(1); + buf.write(2); + buf.clear(); + buf.flush(); + + expect(onFlush).toHaveBeenCalledTimes(1); + expect(onFlush.mock.calls[0][0]).toEqual([]); + }); + + test('can call .clear() any time as many times as needed', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + }); + + buf.clear(); + buf.flush(); + buf.write(123); + buf.clear(); + buf.flush(); + buf.clear(); + buf.clear(); + buf.flush(); + buf.flush(); + + expect(onFlush).toHaveBeenCalledTimes(4); + expect(onFlush.mock.calls[0][0]).toEqual([]); + expect(onFlush.mock.calls[1][0]).toEqual([]); + expect(onFlush.mock.calls[2][0]).toEqual([]); + expect(onFlush.mock.calls[3][0]).toEqual([]); + }); + + describe('when `flushOnMaxItems` is set', () => { + test('does not flush automatically before `flushOnMaxItems` is reached', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + flushOnMaxItems: 2, + }); + + buf.write(1); + + expect(onFlush).toHaveBeenCalledTimes(0); + }); + + test('automatically flushes buffer when `flushOnMaxItems` is reached', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + flushOnMaxItems: 2, + }); + + buf.write(1); + buf.write(2); + + expect(onFlush).toHaveBeenCalledTimes(1); + expect(onFlush.mock.calls[0][0]).toEqual([1, 2]); + }); + + test('flushes again when `flushOnMaxItems` limit is reached the second time', () => { + const onFlush = jest.fn(); + const buf = new Buffer({ + onFlush, + flushOnMaxItems: 2, + }); + + buf.write(1); + buf.write(2); + buf.write(3); + buf.write(4); + buf.write(5); + buf.flush(); + + expect(onFlush).toHaveBeenCalledTimes(3); + expect(onFlush.mock.calls[0][0]).toEqual([1, 2]); + expect(onFlush.mock.calls[1][0]).toEqual([3, 4]); + expect(onFlush.mock.calls[2][0]).toEqual([5]); + }); + }); + }); +}; diff --git a/src/plugins/bfetch/common/buffer/tests/timed_item_buffer.test.ts b/src/plugins/bfetch/common/buffer/tests/timed_item_buffer.test.ts new file mode 100644 index 0000000000000..c1c6a8f187a44 --- /dev/null +++ b/src/plugins/bfetch/common/buffer/tests/timed_item_buffer.test.ts @@ -0,0 +1,104 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { TimedItemBuffer } from '../timed_item_buffer'; +import { runItemBufferTests } from './run_item_buffer_tests'; + +describe('TimedItemBuffer', () => { + runItemBufferTests(TimedItemBuffer); + + test('does not do unnecessary flushes', async () => { + const onFlush = jest.fn(); + const buf = new TimedItemBuffer({ + onFlush, + maxItemAge: 3, + }); + + expect(onFlush).toHaveBeenCalledTimes(0); + buf.write(0); + expect(onFlush).toHaveBeenCalledTimes(0); + buf.flush(); + expect(onFlush).toHaveBeenCalledTimes(1); + }); + + test('does not do extra flush after timeout if buffer was flushed during timeout wait', async () => { + const onFlush = jest.fn(); + const buf = new TimedItemBuffer({ + onFlush, + maxItemAge: 10, + }); + + buf.write(0); + await new Promise(r => setTimeout(r, 3)); + buf.flush(); + await new Promise(r => setTimeout(r, 11)); + + expect(onFlush).toHaveBeenCalledTimes(1); + }); + + test('flushes buffer automatically after timeout reached', async () => { + const onFlush = jest.fn(); + const buf = new TimedItemBuffer({ + onFlush, + maxItemAge: 2, + }); + + buf.write(1); + buf.write(2); + expect(onFlush).toHaveBeenCalledTimes(0); + + await new Promise(r => setTimeout(r, 3)); + expect(onFlush).toHaveBeenCalledTimes(1); + expect(onFlush).toHaveBeenCalledWith([1, 2]); + }); + + test('does not call flush after timeout if flush was triggered because buffer size reached', async () => { + const onFlush = jest.fn(); + const buf = new TimedItemBuffer({ + onFlush, + flushOnMaxItems: 2, + maxItemAge: 2, + }); + + buf.write(1); + buf.write(2); + + expect(onFlush).toHaveBeenCalledTimes(1); + await new Promise(r => setTimeout(r, 3)); + expect(onFlush).toHaveBeenCalledTimes(1); + }); + + test('does not automatically flush if `.clear()` was called', async () => { + const onFlush = jest.fn(); + const buf = new TimedItemBuffer({ + onFlush, + flushOnMaxItems: 25, + maxItemAge: 5, + }); + + buf.write(1); + buf.write(2); + await new Promise(r => setImmediate(r)); + buf.clear(); + + expect(onFlush).toHaveBeenCalledTimes(0); + await new Promise(r => setTimeout(r, 6)); + expect(onFlush).toHaveBeenCalledTimes(0); + }); +}); diff --git a/src/plugins/bfetch/common/buffer/timed_item_buffer.ts b/src/plugins/bfetch/common/buffer/timed_item_buffer.ts new file mode 100644 index 0000000000000..8d0f9e4856f8c --- /dev/null +++ b/src/plugins/bfetch/common/buffer/timed_item_buffer.ts @@ -0,0 +1,58 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ItemBuffer, ItemBufferParams } from './item_buffer'; + +export interface TimedItemBufferParams extends ItemBufferParams { + /** + * Flushes buffer when oldest item reaches age specified by this parameter, + * in milliseconds. + */ + maxItemAge?: number; +} + +export class TimedItemBuffer extends ItemBuffer { + private timer: any; + + constructor(public readonly params: TimedItemBufferParams) { + super(params); + } + + public write(item: Item) { + super.write(item); + + if (this.params.maxItemAge && this.length === 1) { + this.timer = setTimeout(this.onTimeout, this.params.maxItemAge); + } + } + + public clear() { + clearTimeout(this.timer); + super.clear(); + } + + public flush() { + clearTimeout(this.timer); + super.flush(); + } + + private onTimeout = () => { + this.flush(); + }; +} diff --git a/src/plugins/bfetch/common/index.ts b/src/plugins/bfetch/common/index.ts index afa73ade80084..085b8e7c58a67 100644 --- a/src/plugins/bfetch/common/index.ts +++ b/src/plugins/bfetch/common/index.ts @@ -19,3 +19,5 @@ export * from './util'; export * from './streaming'; +export * from './buffer'; +export * from './batch'; diff --git a/src/plugins/bfetch/common/streaming/types.ts b/src/plugins/bfetch/common/streaming/types.ts index 1ee92edbc89ff..197ee9a52ff01 100644 --- a/src/plugins/bfetch/common/streaming/types.ts +++ b/src/plugins/bfetch/common/streaming/types.ts @@ -20,5 +20,5 @@ import { Observable } from 'rxjs'; export interface StreamingResponseHandler { - onRequest(payload: Payload): Observable; + getResponseStream(payload: Payload): Observable; } diff --git a/src/plugins/bfetch/common/util/index.ts b/src/plugins/bfetch/common/util/index.ts index 02843af9b4350..b5d1fcabbcd85 100644 --- a/src/plugins/bfetch/common/util/index.ts +++ b/src/plugins/bfetch/common/util/index.ts @@ -17,4 +17,5 @@ * under the License. */ +export * from './normalize_error'; export * from './remove_leading_slash'; diff --git a/src/plugins/bfetch/common/util/normalize_error.ts b/src/plugins/bfetch/common/util/normalize_error.ts new file mode 100644 index 0000000000000..c2ee3d83f5eb5 --- /dev/null +++ b/src/plugins/bfetch/common/util/normalize_error.ts @@ -0,0 +1,40 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ErrorLike } from '../batch'; + +export const normalizeError = (err: any): E => { + if (!err) { + return { + message: 'Unknown error.', + } as E; + } + if (err instanceof Error) { + return { message: err.message } as E; + } + if (typeof err === 'object') { + return { + ...err, + message: err.message || 'Unknown error.', + } as E; + } + return { + message: String(err), + } as E; +}; diff --git a/src/plugins/bfetch/docs/browser/reference.md b/src/plugins/bfetch/docs/browser/reference.md index 47a67c08a4c1f..444b1aa08a98e 100644 --- a/src/plugins/bfetch/docs/browser/reference.md +++ b/src/plugins/bfetch/docs/browser/reference.md @@ -1,8 +1,37 @@ # `bfetch` browser reference +- [`batchedFunction`](#batchedFunction) - [`fetchStreaming`](#fetchStreaming) +## `batchedFunction` + +Creates a function that will buffer its calls (until timeout—10ms default— or capacity reached—25 default) +and send all calls in one batch to the specified endpoint. The endpoint is expected +to stream results back in ND-JSON format using `Transfer-Encoding: chunked`, which is +implemented by `addBatchProcessingRoute` server-side method of `bfetch` plugin. + +The created function is expected to be called with a single object argument and will +return a promise that will resolve to an object. + +```ts +const fn = bfetch.batchedFunction({ url: '/my-plugin/something' }); + +const result = await fn({ foo: 'bar' }); +``` + +Options: + +- `url` — URL endpoint that will receive a batch of requests. This endpoint is expected + to receive batch as a serialized JSON array. It should stream responses back + in ND-JSON format using `Transfer-Encoding: chunked` HTTP/1 streaming. +- `fetchStreaming` — The instance of `fetchStreaming` function that will perform ND-JSON handling. + There should be a version of this function available in setup contract of `bfetch` plugin. +- `flushOnMaxItems` — The maximum size of function call buffer before sending the batch request. +- `maxItemAge` — The maximum timeout in milliseconds of the oldest item in the batch + before sending the batch request. + + ## `fetchStreaming` Executes an HTTP request and expects that server streams back results using @@ -12,4 +41,4 @@ HTTP/1 `Transfer-Encoding: chunked`. const { stream } = bfetch.fetchStreaming({ url: 'http://elastic.co' }); stream.subscribe(value => {}); -``` \ No newline at end of file +``` diff --git a/src/plugins/bfetch/docs/server/reference.md b/src/plugins/bfetch/docs/server/reference.md new file mode 100644 index 0000000000000..424532a50b817 --- /dev/null +++ b/src/plugins/bfetch/docs/server/reference.md @@ -0,0 +1,54 @@ +# `bfetch` server reference + +- [`addBatchProcessingRoute`](#addBatchProcessingRoute) +- [`addStreamingResponseRoute`](#addStreamingResponseRoute) + + +## `addBatchProcessingRoute` + +Sets up a server endpoint that expects to work with [`batchedFunction`](../browser/reference.md#batchedFunction). +The endpoint receives a batch of requests, processes each request and streams results +back immediately as they become available. You only need to implement the +processing of each request (`onBatchItem` function), everything else is handled. + +`onBatchItem` function is called for each individual request in the batch. +`onBatchItem` function receives a single object argument which is the payload +of one request; and it must return a promise that resolves to an object, too. +`onBatchItem` function is allowed to throw, in that case the error will be forwarded +to the browser only to the individual request, the rest of the batch will still continue +executing. + +```ts +plugins.bfetch.addBatchProcessingRoute( + '/my-plugin/double', + request => ({ + onBatchItem: async (payload) => { + // ... + return {}; + }, + }) +); +``` + +`request` is the `KibanaRequest` object. `addBatchProcessingRoute` together with `batchedFunction` +ensure that errors are handled and that all items in the batch get executed. + + +## `addStreamingResponseRoute` + +`addStreamingResponseRoute` is a lower-level interface that receives and `payload` +message returns and observable which results are streamed back as ND-JSON messages +until the observable completes. `addStreamingResponseRoute` does not know about the +type of the messages, it does not handle errors, and it does not have a concept of +batch size—observable can stream any number of messages until it completes. + +```ts +plugins.bfetch.addStreamingResponseRoute('/my-plugin/foo', request => ({ + getResponseStream: (payload) => { + const subject = new Subject(); + setTimeout(() => { subject.next('123'); }, 100); + setTimeout(() => { subject.complete(); }, 200); + return subject; + }, +})); +``` diff --git a/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts b/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts new file mode 100644 index 0000000000000..064b791327e69 --- /dev/null +++ b/src/plugins/bfetch/public/batching/create_streaming_batched_function.test.ts @@ -0,0 +1,521 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { createStreamingBatchedFunction } from './create_streaming_batched_function'; +import { fetchStreaming as fetchStreamingReal } from '../streaming/fetch_streaming'; +import { defer, of } from '../../../kibana_utils/public'; +import { Subject } from 'rxjs'; + +const getPromiseState = (promise: Promise): Promise<'resolved' | 'rejected' | 'pending'> => + Promise.race<'resolved' | 'rejected' | 'pending'>([ + new Promise(resolve => + promise.then( + () => resolve('resolved'), + () => resolve('rejected') + ) + ), + new Promise<'pending'>(resolve => resolve()).then(() => 'pending'), + ]); + +const isPending = (promise: Promise): Promise => + getPromiseState(promise).then(state => state === 'pending'); + +const setup = () => { + const xhr = ({} as unknown) as XMLHttpRequest; + const { promise, resolve, reject } = defer(); + const stream = new Subject(); + + const fetchStreaming = (jest.fn(() => ({ + xhr, + promise, + stream, + })) as unknown) as jest.SpyInstance & typeof fetchStreamingReal; + + return { + fetchStreaming, + xhr, + promise, + resolve, + reject, + stream, + }; +}; + +describe('createStreamingBatchedFunction()', () => { + test('returns a function', () => { + const { fetchStreaming } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + }); + expect(typeof fn).toBe('function'); + }); + + test('returned function is async', () => { + const { fetchStreaming } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + }); + const res = fn({}); + expect(typeof res.then).toBe('function'); + }); + + describe('when timeout is reached', () => { + test('dispatches batch', async () => { + const { fetchStreaming } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + expect(fetchStreaming).toHaveBeenCalledTimes(0); + fn({ foo: 'bar' }); + expect(fetchStreaming).toHaveBeenCalledTimes(0); + fn({ baz: 'quix' }); + expect(fetchStreaming).toHaveBeenCalledTimes(0); + + await new Promise(r => setTimeout(r, 6)); + expect(fetchStreaming).toHaveBeenCalledTimes(1); + }); + + test('does nothing is buffer is empty', async () => { + const { fetchStreaming } = setup(); + createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + expect(fetchStreaming).toHaveBeenCalledTimes(0); + await new Promise(r => setTimeout(r, 6)); + expect(fetchStreaming).toHaveBeenCalledTimes(0); + }); + + test('sends POST request to correct endpoint', async () => { + const { fetchStreaming } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + fn({ foo: 'bar' }); + await new Promise(r => setTimeout(r, 6)); + + expect(fetchStreaming.mock.calls[0][0]).toMatchObject({ + url: '/test', + method: 'POST', + }); + }); + + test('collects calls into an array batch ordered by in same order as calls', async () => { + const { fetchStreaming } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + fn({ foo: 'bar' }); + fn({ baz: 'quix' }); + + await new Promise(r => setTimeout(r, 6)); + const { body } = fetchStreaming.mock.calls[0][0]; + expect(JSON.parse(body)).toEqual({ + batch: [{ foo: 'bar' }, { baz: 'quix' }], + }); + }); + }); + + describe('when buffer becomes full', () => { + test('dispatches batch request', async () => { + const { fetchStreaming } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + expect(fetchStreaming).toHaveBeenCalledTimes(0); + fn({ foo: 'bar' }); + expect(fetchStreaming).toHaveBeenCalledTimes(0); + fn({ baz: 'quix' }); + expect(fetchStreaming).toHaveBeenCalledTimes(0); + fn({ full: 'yep' }); + expect(fetchStreaming).toHaveBeenCalledTimes(1); + }); + + test('sends POST request to correct endpoint with items in array batched sorted in call order', async () => { + const { fetchStreaming } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + fn({ a: '1' }); + fn({ b: '2' }); + fn({ c: '3' }); + + expect(fetchStreaming.mock.calls[0][0]).toMatchObject({ + url: '/test', + method: 'POST', + }); + const { body } = fetchStreaming.mock.calls[0][0]; + expect(JSON.parse(body)).toEqual({ + batch: [{ a: '1' }, { b: '2' }, { c: '3' }], + }); + }); + + test('dispatches batch on full buffer and also on timeout', async () => { + const { fetchStreaming } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + fn({ a: '1' }); + fn({ b: '2' }); + fn({ c: '3' }); + expect(fetchStreaming).toHaveBeenCalledTimes(1); + fn({ d: '4' }); + await new Promise(r => setTimeout(r, 6)); + expect(fetchStreaming).toHaveBeenCalledTimes(2); + }); + }); + + describe('when receiving results', () => { + test('does not resolve call promises until request finishes', async () => { + const { fetchStreaming } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + const promise1 = fn({ a: '1' }); + const promise2 = fn({ b: '2' }); + await new Promise(r => setTimeout(r, 6)); + + expect(await isPending(promise1)).toBe(true); + expect(await isPending(promise2)).toBe(true); + }); + + test('resolves only promise of result that was streamed back', async () => { + const { fetchStreaming, stream } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + const promise1 = fn({ a: '1' }); + const promise2 = fn({ b: '2' }); + const promise3 = fn({ c: '3' }); + await new Promise(r => setTimeout(r, 6)); + + expect(await isPending(promise1)).toBe(true); + expect(await isPending(promise2)).toBe(true); + expect(await isPending(promise3)).toBe(true); + + stream.next( + JSON.stringify({ + id: 1, + result: { foo: 'bar' }, + }) + '\n' + ); + + expect(await isPending(promise1)).toBe(true); + expect(await isPending(promise2)).toBe(false); + expect(await isPending(promise3)).toBe(true); + + stream.next( + JSON.stringify({ + id: 0, + result: { foo: 'bar 2' }, + }) + '\n' + ); + + expect(await isPending(promise1)).toBe(false); + expect(await isPending(promise2)).toBe(false); + expect(await isPending(promise3)).toBe(true); + }); + + test('resolves each promise with correct data', async () => { + const { fetchStreaming, stream } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + const promise1 = fn({ a: '1' }); + const promise2 = fn({ b: '2' }); + const promise3 = fn({ c: '3' }); + await new Promise(r => setTimeout(r, 6)); + + stream.next( + JSON.stringify({ + id: 1, + result: { foo: 'bar' }, + }) + '\n' + ); + stream.next( + JSON.stringify({ + id: 2, + result: { foo: 'bar 2' }, + }) + '\n' + ); + + expect(await isPending(promise1)).toBe(true); + expect(await isPending(promise2)).toBe(false); + expect(await isPending(promise3)).toBe(false); + expect(await promise2).toEqual({ foo: 'bar' }); + expect(await promise3).toEqual({ foo: 'bar 2' }); + }); + + test('rejects promise on error response', async () => { + const { fetchStreaming, stream } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + const promise = fn({ a: '1' }); + await new Promise(r => setTimeout(r, 6)); + + expect(await isPending(promise)).toBe(true); + + stream.next( + JSON.stringify({ + id: 0, + error: { message: 'oops' }, + }) + '\n' + ); + + expect(await isPending(promise)).toBe(false); + const [, error] = await of(promise); + expect(error).toEqual({ + message: 'oops', + }); + }); + + test('resolves successful requests even after rejected ones', async () => { + const { fetchStreaming, stream } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + const promise1 = of(fn({ a: '1' })); + const promise2 = of(fn({ a: '2' })); + const promise3 = of(fn({ a: '3' })); + + await new Promise(r => setTimeout(r, 6)); + + stream.next( + JSON.stringify({ + id: 2, + result: { b: '3' }, + }) + '\n' + ); + + await new Promise(r => setTimeout(r, 1)); + + stream.next( + JSON.stringify({ + id: 1, + error: { b: '2' }, + }) + '\n' + ); + + await new Promise(r => setTimeout(r, 1)); + + stream.next( + JSON.stringify({ + id: 0, + result: { b: '1' }, + }) + '\n' + ); + + await new Promise(r => setTimeout(r, 1)); + + const [result1] = await promise1; + const [, error2] = await promise2; + const [result3] = await promise3; + + expect(result1).toEqual({ b: '1' }); + expect(error2).toEqual({ b: '2' }); + expect(result3).toEqual({ b: '3' }); + }); + + describe('when stream closes prematurely', () => { + test('rejects pending promises with CONNECTION error code', async () => { + const { fetchStreaming, stream } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + const promise1 = of(fn({ a: '1' })); + const promise2 = of(fn({ a: '2' })); + + await new Promise(r => setTimeout(r, 6)); + + stream.complete(); + + await new Promise(r => setTimeout(r, 1)); + + const [, error1] = await promise1; + const [, error2] = await promise2; + expect(error1).toMatchObject({ + message: 'Connection terminated prematurely.', + code: 'CONNECTION', + }); + expect(error2).toMatchObject({ + message: 'Connection terminated prematurely.', + code: 'CONNECTION', + }); + }); + + test('rejects with CONNECTION error only pending promises', async () => { + const { fetchStreaming, stream } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + const promise1 = of(fn({ a: '1' })); + const promise2 = of(fn({ a: '2' })); + + await new Promise(r => setTimeout(r, 6)); + + stream.next( + JSON.stringify({ + id: 1, + result: { b: '1' }, + }) + '\n' + ); + stream.complete(); + + await new Promise(r => setTimeout(r, 1)); + + const [, error1] = await promise1; + const [result1] = await promise2; + expect(error1).toMatchObject({ + message: 'Connection terminated prematurely.', + code: 'CONNECTION', + }); + expect(result1).toMatchObject({ + b: '1', + }); + }); + }); + + describe('when stream errors', () => { + test('rejects pending promises with STREAM error code', async () => { + const { fetchStreaming, stream } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + const promise1 = of(fn({ a: '1' })); + const promise2 = of(fn({ a: '2' })); + + await new Promise(r => setTimeout(r, 6)); + + stream.error({ + message: 'something went wrong', + }); + + await new Promise(r => setTimeout(r, 1)); + + const [, error1] = await promise1; + const [, error2] = await promise2; + expect(error1).toMatchObject({ + message: 'something went wrong', + code: 'STREAM', + }); + expect(error2).toMatchObject({ + message: 'something went wrong', + code: 'STREAM', + }); + }); + + test('rejects with STREAM error only pending promises', async () => { + const { fetchStreaming, stream } = setup(); + const fn = createStreamingBatchedFunction({ + url: '/test', + fetchStreaming, + maxItemAge: 5, + flushOnMaxItems: 3, + }); + + const promise1 = of(fn({ a: '1' })); + const promise2 = of(fn({ a: '2' })); + + await new Promise(r => setTimeout(r, 6)); + + stream.next( + JSON.stringify({ + id: 1, + result: { b: '1' }, + }) + '\n' + ); + stream.error('oops'); + + await new Promise(r => setTimeout(r, 1)); + + const [, error1] = await promise1; + const [result1] = await promise2; + expect(error1).toMatchObject({ + message: 'oops', + code: 'STREAM', + }); + expect(result1).toMatchObject({ + b: '1', + }); + }); + }); + }); +}); diff --git a/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts b/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts new file mode 100644 index 0000000000000..07d5724a2520d --- /dev/null +++ b/src/plugins/bfetch/public/batching/create_streaming_batched_function.ts @@ -0,0 +1,140 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { defer, Defer } from '../../../kibana_utils/public'; +import { + ItemBufferParams, + TimedItemBufferParams, + createBatchedFunction, + BatchResponseItem, + ErrorLike, +} from '../../common'; +import { fetchStreaming, split } from '../streaming'; +import { normalizeError } from '../../common'; + +export interface BatchItem { + payload: Payload; + future: Defer; +} + +export type BatchedFunc = (payload: Payload) => Promise; + +export interface BatchedFunctionProtocolError extends ErrorLike { + code: string; +} + +export interface StreamingBatchedFunctionParams { + /** + * URL endpoint that will receive a batch of requests. This endpoint is expected + * to receive batch as a serialized JSON array. It should stream responses back + * in ND-JSON format using `Transfer-Encoding: chunked` HTTP/1 streaming. + */ + url: string; + + /** + * The instance of `fetchStreaming` function that will perform ND-JSON handling. + * There should be a version of this function available in setup contract of `bfetch` + * plugin. + */ + fetchStreaming?: typeof fetchStreaming; + + /** + * The maximum size of function call buffer before sending the batch request. + */ + flushOnMaxItems?: ItemBufferParams['flushOnMaxItems']; + + /** + * The maximum timeout in milliseconds of the oldest item in the batch + * before sending the batch request. + */ + maxItemAge?: TimedItemBufferParams['maxItemAge']; +} + +/** + * Returns a function that does not execute immediately but buffers the call internally until + * `params.flushOnMaxItems` is reached or after `params.maxItemAge` timeout in milliseconds is reached. Once + * one of those thresholds is reached all buffered calls are sent in one batch to the + * server using `params.fetchStreaming` in a POST request. Responses are streamed back + * and each batch item is resolved once corresponding response is received. + */ +export const createStreamingBatchedFunction = ( + params: StreamingBatchedFunctionParams +): BatchedFunc => { + const { + url, + fetchStreaming: fetchStreamingInjected = fetchStreaming, + flushOnMaxItems = 25, + maxItemAge = 10, + } = params; + const [fn] = createBatchedFunction, BatchItem>({ + onCall: (payload: Payload) => { + const future = defer(); + const entry: BatchItem = { + payload, + future, + }; + return [future.promise, entry]; + }, + onBatch: async items => { + try { + let responsesReceived = 0; + const batch = items.map(({ payload }) => payload); + const { stream } = fetchStreamingInjected({ + url, + body: JSON.stringify({ batch }), + method: 'POST', + }); + stream.pipe(split('\n')).subscribe({ + next: (json: string) => { + const response = JSON.parse(json) as BatchResponseItem; + if (response.error) { + responsesReceived++; + items[response.id].future.reject(response.error); + } else if (response.result) { + responsesReceived++; + items[response.id].future.resolve(response.result); + } + }, + error: error => { + const normalizedError = normalizeError(error); + normalizedError.code = 'STREAM'; + for (const { future } of items) future.reject(normalizedError); + }, + complete: () => { + const streamTerminatedPrematurely = responsesReceived !== items.length; + if (streamTerminatedPrematurely) { + const error: BatchedFunctionProtocolError = { + message: 'Connection terminated prematurely.', + code: 'CONNECTION', + }; + for (const { future } of items) future.reject(error); + } + }, + }); + await stream.toPromise(); + } catch (error) { + for (const item of items) item.future.reject(error); + } + }, + flushOnMaxItems, + maxItemAge, + }); + + return fn; +}; diff --git a/src/plugins/bfetch/public/index.ts b/src/plugins/bfetch/public/index.ts index a57dd77fe7e67..8707e5a438159 100644 --- a/src/plugins/bfetch/public/index.ts +++ b/src/plugins/bfetch/public/index.ts @@ -20,7 +20,8 @@ import { PluginInitializerContext } from '../../../core/public'; import { BfetchPublicPlugin } from './plugin'; -export { BfetchPublicSetup, BfetchPublicStart, BfetchPublicApi } from './plugin'; +export { BfetchPublicSetup, BfetchPublicStart, BfetchPublicContract } from './plugin'; +export { split } from './streaming'; export function plugin(initializerContext: PluginInitializerContext) { return new BfetchPublicPlugin(initializerContext); diff --git a/src/plugins/bfetch/public/mocks.ts b/src/plugins/bfetch/public/mocks.ts index e8caf5c9cb739..f457b9ae5d671 100644 --- a/src/plugins/bfetch/public/mocks.ts +++ b/src/plugins/bfetch/public/mocks.ts @@ -27,6 +27,7 @@ export type Start = jest.Mocked; const createSetupContract = (): Setup => { const setupContract: Setup = { fetchStreaming: jest.fn(), + batchedFunction: jest.fn(), }; return setupContract; }; @@ -34,6 +35,7 @@ const createSetupContract = (): Setup => { const createStartContract = (): Start => { const startContract: Start = { fetchStreaming: jest.fn(), + batchedFunction: jest.fn(), }; return startContract; @@ -56,7 +58,7 @@ const createPlugin = async () => { }; }; -export const uiActionsPluginMock = { +export const bfetchPluginMock = { createSetupContract, createStartContract, createPlugin, diff --git a/src/plugins/bfetch/public/plugin.ts b/src/plugins/bfetch/public/plugin.ts index db18a15afa1e7..783c448c567e5 100644 --- a/src/plugins/bfetch/public/plugin.ts +++ b/src/plugins/bfetch/public/plugin.ts @@ -20,6 +20,11 @@ import { CoreStart, PluginInitializerContext, CoreSetup, Plugin } from 'src/core/public'; import { fetchStreaming as fetchStreamingStatic, FetchStreamingParams } from './streaming'; import { removeLeadingSlash } from '../common'; +import { + createStreamingBatchedFunction, + BatchedFunc, + StreamingBatchedFunctionParams, +} from './batching/create_streaming_batched_function'; // eslint-disable-next-line export interface BfetchPublicSetupDependencies {} @@ -27,12 +32,15 @@ export interface BfetchPublicSetupDependencies {} // eslint-disable-next-line export interface BfetchPublicStartDependencies {} -export interface BfetchPublicApi { +export interface BfetchPublicContract { fetchStreaming: (params: FetchStreamingParams) => ReturnType; + batchedFunction: ( + params: StreamingBatchedFunctionParams + ) => BatchedFunc; } -export type BfetchPublicSetup = BfetchPublicApi; -export type BfetchPublicStart = BfetchPublicApi; +export type BfetchPublicSetup = BfetchPublicContract; +export type BfetchPublicStart = BfetchPublicContract; export class BfetchPublicPlugin implements @@ -42,7 +50,7 @@ export class BfetchPublicPlugin BfetchPublicSetupDependencies, BfetchPublicStartDependencies > { - private api!: BfetchPublicApi; + private contract!: BfetchPublicContract; constructor(private readonly initializerContext: PluginInitializerContext) {} @@ -51,16 +59,18 @@ export class BfetchPublicPlugin const basePath = core.http.basePath.get(); const fetchStreaming = this.fetchStreaming(version, basePath); + const batchedFunction = this.batchedFunction(fetchStreaming); - this.api = { + this.contract = { fetchStreaming, + batchedFunction, }; - return this.api; + return this.contract; } public start(core: CoreStart, plugins: BfetchPublicStartDependencies): BfetchPublicStart { - return this.api; + return this.contract; } public stop() {} @@ -78,4 +88,12 @@ export class BfetchPublicPlugin ...(params.headers || {}), }, }); + + private batchedFunction = ( + fetchStreaming: BfetchPublicContract['fetchStreaming'] + ): BfetchPublicContract['batchedFunction'] => params => + createStreamingBatchedFunction({ + ...params, + fetchStreaming: params.fetchStreaming || fetchStreaming, + }); } diff --git a/src/plugins/bfetch/public/streaming/fetch_streaming.test.ts b/src/plugins/bfetch/public/streaming/fetch_streaming.test.ts index e59af71cb76bc..7845616026ea1 100644 --- a/src/plugins/bfetch/public/streaming/fetch_streaming.test.ts +++ b/src/plugins/bfetch/public/streaming/fetch_streaming.test.ts @@ -36,14 +36,6 @@ test('returns XHR request', () => { expect(typeof xhr.readyState).toBe('number'); }); -test('returns promise', () => { - setup(); - const { promise } = fetchStreaming({ - url: 'http://example.com', - }); - expect(typeof promise.then).toBe('function'); -}); - test('returns stream', () => { setup(); const { stream } = fetchStreaming({ @@ -54,12 +46,12 @@ test('returns stream', () => { test('promise resolves when request completes', async () => { const env = setup(); - const { promise } = fetchStreaming({ + const { stream } = fetchStreaming({ url: 'http://example.com', }); let resolved = false; - promise.then(() => (resolved = true)); + stream.toPromise().then(() => (resolved = true)); await tick(); expect(resolved).toBe(false); @@ -142,12 +134,12 @@ test('completes stream observable when request finishes', async () => { test('promise throws when request errors', async () => { const env = setup(); - const { promise } = fetchStreaming({ + const { stream } = fetchStreaming({ url: 'http://example.com', }); const spy = jest.fn(); - promise.catch(spy); + stream.toPromise().catch(spy); await tick(); expect(spy).toHaveBeenCalledTimes(0); @@ -168,12 +160,11 @@ test('promise throws when request errors', async () => { test('stream observable errors when request errors', async () => { const env = setup(); - const { promise, stream } = fetchStreaming({ + const { stream } = fetchStreaming({ url: 'http://example.com', }); const spy = jest.fn(); - promise.catch(() => {}); stream.subscribe({ error: spy, }); diff --git a/src/plugins/bfetch/public/streaming/fetch_streaming.ts b/src/plugins/bfetch/public/streaming/fetch_streaming.ts index 44a3693e7010b..899e8a1824a41 100644 --- a/src/plugins/bfetch/public/streaming/fetch_streaming.ts +++ b/src/plugins/bfetch/public/streaming/fetch_streaming.ts @@ -17,7 +17,6 @@ * under the License. */ -import { defer } from '../../../kibana_utils/common'; import { fromStreamingXhr } from './from_streaming_xhr'; export interface FetchStreamingParams { @@ -38,7 +37,6 @@ export function fetchStreaming({ body = '', }: FetchStreamingParams) { const xhr = new window.XMLHttpRequest(); - const { promise, resolve, reject } = defer(); // Begin the request xhr.open(method, url); @@ -49,17 +47,11 @@ export function fetchStreaming({ const stream = fromStreamingXhr(xhr); - stream.subscribe({ - complete: () => resolve(), - error: error => reject(error), - }); - // Send the payload to the server xhr.send(body); return { xhr, - promise, stream, }; } diff --git a/src/plugins/bfetch/server/index.ts b/src/plugins/bfetch/server/index.ts index f1a3f7fd44cf6..06b7c793c537e 100644 --- a/src/plugins/bfetch/server/index.ts +++ b/src/plugins/bfetch/server/index.ts @@ -20,7 +20,7 @@ import { PluginInitializerContext } from '../../../core/server'; import { BfetchServerPlugin } from './plugin'; -export { BfetchServerSetup, BfetchServerStart } from './plugin'; +export { BfetchServerSetup, BfetchServerStart, BatchProcessingRouteParams } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { return new BfetchServerPlugin(initializerContext); diff --git a/src/plugins/bfetch/server/mocks.ts b/src/plugins/bfetch/server/mocks.ts index 8ec68650a60dc..e0a76ba8da325 100644 --- a/src/plugins/bfetch/server/mocks.ts +++ b/src/plugins/bfetch/server/mocks.ts @@ -26,6 +26,7 @@ export type Start = jest.Mocked; const createSetupContract = (): Setup => { const setupContract: Setup = { + addBatchProcessingRoute: jest.fn(), addStreamingResponseRoute: jest.fn(), }; return setupContract; @@ -54,7 +55,7 @@ const createPlugin = async () => { }; }; -export const uiActionsPluginMock = { +export const bfetchPluginMock = { createSetupContract, createStartContract, createPlugin, diff --git a/src/plugins/bfetch/server/plugin.ts b/src/plugins/bfetch/server/plugin.ts index 75baeafc17669..fd1fe009e93ae 100644 --- a/src/plugins/bfetch/server/plugin.ts +++ b/src/plugins/bfetch/server/plugin.ts @@ -17,9 +17,24 @@ * under the License. */ -import { CoreStart, PluginInitializerContext, CoreSetup, Plugin, Logger } from 'src/core/server'; +import { + CoreStart, + PluginInitializerContext, + CoreSetup, + Plugin, + Logger, + KibanaRequest, +} from 'src/core/server'; import { schema } from '@kbn/config-schema'; -import { StreamingResponseHandler, removeLeadingSlash } from '../common'; +import { Subject } from 'rxjs'; +import { + StreamingResponseHandler, + BatchRequestData, + BatchResponseItem, + ErrorLike, + removeLeadingSlash, + normalizeError, +} from '../common'; import { createNDJSONStream } from './streaming'; // eslint-disable-next-line @@ -28,8 +43,19 @@ export interface BfetchServerSetupDependencies {} // eslint-disable-next-line export interface BfetchServerStartDependencies {} +export interface BatchProcessingRouteParams { + onBatchItem: (data: BatchItemData) => Promise; +} + export interface BfetchServerSetup { - addStreamingResponseRoute: (path: string, handler: StreamingResponseHandler) => void; + addBatchProcessingRoute: ( + path: string, + handler: (request: KibanaRequest) => BatchProcessingRouteParams + ) => void; + addStreamingResponseRoute: ( + path: string, + params: (request: KibanaRequest) => StreamingResponseHandler + ) => void; } // eslint-disable-next-line @@ -49,8 +75,10 @@ export class BfetchServerPlugin const logger = this.initializerContext.logger.get(); const router = core.http.createRouter(); const addStreamingResponseRoute = this.addStreamingResponseRoute({ router, logger }); + const addBatchProcessingRoute = this.addBatchProcessingRoute(addStreamingResponseRoute); return { + addBatchProcessingRoute, addStreamingResponseRoute, }; } @@ -76,17 +104,56 @@ export class BfetchServerPlugin }, }, async (context, request, response) => { + const handlerInstance = handler(request); const data = request.body; + const headers = { + 'Content-Type': 'application/x-ndjson', + Connection: 'keep-alive', + 'Transfer-Encoding': 'chunked', + 'Cache-Control': 'no-cache', + }; return response.ok({ - headers: { - 'Content-Type': 'application/x-ndjson', - Connection: 'keep-alive', - 'Transfer-Encoding': 'chunked', - 'Cache-Control': 'no-cache', - }, - body: createNDJSONStream(data, handler, logger), + headers, + body: createNDJSONStream(data, handlerInstance, logger), }); } ); }; + + private addBatchProcessingRoute = ( + addStreamingResponseRoute: BfetchServerSetup['addStreamingResponseRoute'] + ): BfetchServerSetup['addBatchProcessingRoute'] => < + BatchItemData extends object, + BatchItemResult extends object, + E extends ErrorLike = ErrorLike + >( + path: string, + handler: (request: KibanaRequest) => BatchProcessingRouteParams + ) => { + addStreamingResponseRoute< + BatchRequestData, + BatchResponseItem + >(path, request => { + const handlerInstance = handler(request); + return { + getResponseStream: ({ batch }) => { + const subject = new Subject>(); + let cnt = batch.length; + batch.forEach(async (batchItem, id) => { + try { + const result = await handlerInstance.onBatchItem(batchItem); + subject.next({ id, result }); + } catch (err) { + const error = normalizeError(err); + subject.next({ id, error }); + } finally { + cnt--; + if (!cnt) subject.complete(); + } + }); + return subject; + }, + }; + }); + }; } diff --git a/src/plugins/bfetch/server/streaming/create_ndjson_stream.ts b/src/plugins/bfetch/server/streaming/create_ndjson_stream.ts index b1f39f4acbcb5..82fe31906e8bf 100644 --- a/src/plugins/bfetch/server/streaming/create_ndjson_stream.ts +++ b/src/plugins/bfetch/server/streaming/create_ndjson_stream.ts @@ -29,7 +29,7 @@ export const createNDJSONStream = ( logger: Logger ): Stream => { const stream = new PassThrough(); - const results = handler.onRequest(payload); + const results = handler.getResponseStream(payload); results.subscribe({ next: (message: Response) => { diff --git a/src/plugins/expressions/common/type.ts b/src/plugins/expressions/common/type.ts index de9c43d01a0aa..c9daed9b6785a 100644 --- a/src/plugins/expressions/common/type.ts +++ b/src/plugins/expressions/common/type.ts @@ -30,11 +30,6 @@ export function getType(node: any) { } export function serializeProvider(types: any) { - return { - serialize: provider('serialize'), - deserialize: provider('deserialize'), - }; - function provider(key: any) { return (context: any) => { const type = getType(context); @@ -43,6 +38,11 @@ export function serializeProvider(types: any) { return fn(context); }; } + + return { + serialize: provider('serialize'), + deserialize: provider('deserialize'), + }; } export class Type { diff --git a/src/plugins/expressions/kibana.json b/src/plugins/expressions/kibana.json index ec87b56f3745e..cba693dd4bc20 100644 --- a/src/plugins/expressions/kibana.json +++ b/src/plugins/expressions/kibana.json @@ -1,9 +1,10 @@ { "id": "expressions", "version": "kibana", - "server": false, + "server": true, "ui": true, "requiredPlugins": [ + "bfetch", "inspector" ] } diff --git a/src/legacy/core_plugins/interpreter/public/canvas/batched_fetch.test.ts b/src/plugins/expressions/public/batched_fetch.test.ts similarity index 97% rename from src/legacy/core_plugins/interpreter/public/canvas/batched_fetch.test.ts rename to src/plugins/expressions/public/batched_fetch.test.ts index 3da15cf54cda0..7273be872a725 100644 --- a/src/legacy/core_plugins/interpreter/public/canvas/batched_fetch.test.ts +++ b/src/plugins/expressions/public/batched_fetch.test.ts @@ -18,7 +18,7 @@ */ import { batchedFetch, Request } from './batched_fetch'; -import { defer } from '../../../../../plugins/kibana_utils/public'; +import { defer } from '../../kibana_utils/public'; import { Subject } from 'rxjs'; const serialize = (o: any) => JSON.stringify(o); diff --git a/src/legacy/core_plugins/interpreter/public/canvas/batched_fetch.ts b/src/plugins/expressions/public/batched_fetch.ts similarity index 87% rename from src/legacy/core_plugins/interpreter/public/canvas/batched_fetch.ts rename to src/plugins/expressions/public/batched_fetch.ts index 717a87fc90f9f..6a155b7d42b72 100644 --- a/src/legacy/core_plugins/interpreter/public/canvas/batched_fetch.ts +++ b/src/plugins/expressions/public/batched_fetch.ts @@ -20,13 +20,11 @@ import _ from 'lodash'; import { filter, map } from 'rxjs/operators'; // eslint-disable-next-line -import { split } from '../../../../../plugins/bfetch/public/streaming'; -import { BfetchPublicApi } from '../../../../../plugins/bfetch/public'; -import { defer } from '../../../../../plugins/kibana_utils/public'; -import { FUNCTIONS_URL } from './consts'; +import { split, BfetchPublicContract } from '../../bfetch/public'; +import { defer } from '../../kibana_utils/public'; export interface Options { - fetchStreaming: BfetchPublicApi['fetchStreaming']; + fetchStreaming: BfetchPublicContract['fetchStreaming']; serialize: any; ms?: number; } @@ -111,9 +109,9 @@ export function batchedFetch({ fetchStreaming, serialize, ms = 10 }: Options) { * Runs the specified batch of functions on the server, then resolves * the related promises. */ -async function processBatch(fetchStreaming: BfetchPublicApi['fetchStreaming'], batch: Batch) { - const { stream, promise } = fetchStreaming({ - url: FUNCTIONS_URL, +async function processBatch(fetchStreaming: BfetchPublicContract['fetchStreaming'], batch: Batch) { + const { stream } = fetchStreaming({ + url: `/api/interpreter/fns`, body: JSON.stringify({ functions: Object.values(batch).map(({ request }) => request), }), @@ -137,7 +135,7 @@ async function processBatch(fetchStreaming: BfetchPublicApi['fetchStreaming'], b }); try { - await promise; + await stream.toPromise(); } catch (error) { Object.values(batch).forEach(({ future }) => { future.reject(error); diff --git a/src/plugins/expressions/public/mocks.tsx b/src/plugins/expressions/public/mocks.tsx index 089c324677712..a3476a24dd7ed 100644 --- a/src/plugins/expressions/public/mocks.tsx +++ b/src/plugins/expressions/public/mocks.tsx @@ -23,6 +23,7 @@ import { ExpressionsSetup, ExpressionsStart, plugin as pluginInitializer } from /* eslint-disable */ import { coreMock } from '../../../core/public/mocks'; import { inspectorPluginMock } from '../../inspector/public/mocks'; +import { bfetchPluginMock } from '../../bfetch/public/mocks'; /* eslint-enable */ export type Setup = jest.Mocked; @@ -48,6 +49,7 @@ const createSetupContract = (): Setup => { interpretAst: () => {}, }, }), + loadLegacyServerFunctionWrappers: () => Promise.resolve(), }, }; return setupContract; @@ -71,6 +73,7 @@ const createPlugin = async () => { const coreStart = coreMock.createStart(); const plugin = pluginInitializer(pluginInitializerContext); const setup = await plugin.setup(coreSetup, { + bfetch: bfetchPluginMock.createSetupContract(), inspector: inspectorPluginMock.createSetupContract(), }); @@ -82,6 +85,7 @@ const createPlugin = async () => { setup, doStart: async () => await plugin.start(coreStart, { + bfetch: bfetchPluginMock.createStartContract(), inspector: inspectorPluginMock.createStartContract(), }), }; diff --git a/src/plugins/expressions/public/plugin.ts b/src/plugins/expressions/public/plugin.ts index 11f804464704e..2ba10be76cd92 100644 --- a/src/plugins/expressions/public/plugin.ts +++ b/src/plugins/expressions/public/plugin.ts @@ -20,6 +20,7 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { ExpressionInterpretWithHandlers, ExpressionExecutor } from './types'; import { FunctionsRegistry, RenderFunctionsRegistry, TypesRegistry } from './registries'; +import { BfetchPublicSetup, BfetchPublicStart } from '../../bfetch/public'; import { Setup as InspectorSetup, Start as InspectorStart } from '../../inspector/public'; import { setCoreStart, @@ -58,12 +59,15 @@ import { ExpressionLoader, loader } from './loader'; import { ExpressionDataHandler, execute } from './execute'; import { render, ExpressionRenderHandler } from './render'; import { AnyExpressionFunction, AnyExpressionType } from '../common/types'; +import { serializeProvider } from '../common'; export interface ExpressionsSetupDeps { + bfetch: BfetchPublicSetup; inspector: InspectorSetup; } export interface ExpressionsStartDeps { + bfetch: BfetchPublicStart; inspector: InspectorStart; } @@ -76,6 +80,7 @@ export interface ExpressionsSetup { renderers: RenderFunctionsRegistry; types: TypesRegistry; getExecutor: () => ExpressionExecutor; + loadLegacyServerFunctionWrappers: () => Promise; }; } @@ -98,7 +103,7 @@ export class ExpressionsPublicPlugin constructor(initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup, { inspector }: ExpressionsSetupDeps): ExpressionsSetup { + public setup(core: CoreSetup, { inspector, bfetch }: ExpressionsSetupDeps): ExpressionsSetup { const { functions, renderers, types } = this; setRenderersRegistry(renderers); @@ -146,6 +151,31 @@ export class ExpressionsPublicPlugin setInterpreter(getExecutor().interpreter); + let cached: Promise | null = null; + const loadLegacyServerFunctionWrappers = async () => { + if (!cached) { + cached = (async () => { + const serverFunctionList = await core.http.get(`/api/interpreter/fns`); + const batchedFunction = bfetch.batchedFunction({ url: `/api/interpreter/fns` }); + const { serialize } = serializeProvider(types.toJS()); + + // For every sever-side function, register a client-side + // function that matches its definition, but which simply + // calls the server-side function endpoint. + Object.keys(serverFunctionList).forEach(functionName => { + const fn = () => ({ + ...serverFunctionList[functionName], + fn: (context: any, args: any) => { + return batchedFunction({ functionName, args, context: serialize(context) }); + }, + }); + registerFunction(fn); + }); + })(); + } + return cached; + }; + const setup: ExpressionsSetup = { registerFunction, registerRenderer: (renderer: any) => { @@ -159,6 +189,7 @@ export class ExpressionsPublicPlugin renderers, types, getExecutor, + loadLegacyServerFunctionWrappers, }, }; diff --git a/src/legacy/core_plugins/kbn_vislib_vis_types/index.js b/src/plugins/expressions/server/index.ts similarity index 71% rename from src/legacy/core_plugins/kbn_vislib_vis_types/index.js rename to src/plugins/expressions/server/index.ts index 6e1f17c941ece..6718602ccdef5 100644 --- a/src/legacy/core_plugins/kbn_vislib_vis_types/index.js +++ b/src/plugins/expressions/server/index.ts @@ -17,14 +17,11 @@ * under the License. */ -export default function(kibana) { - return new kibana.Plugin({ - uiExports: { - visTypes: ['plugins/kbn_vislib_vis_types/kbn_vislib_vis_types'], - interpreter: [ - 'plugins/kbn_vislib_vis_types/pie_fn', - 'plugins/kbn_vislib_vis_types/vislib_fn', - ], - }, - }); +import { PluginInitializerContext } from '../../../core/server'; +import { ExpressionsServerPlugin } from './plugin'; + +export { ExpressionsServerSetup, ExpressionsServerStart } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new ExpressionsServerPlugin(initializerContext); } diff --git a/src/plugins/expressions/server/legacy.ts b/src/plugins/expressions/server/legacy.ts new file mode 100644 index 0000000000000..54e2a5a387342 --- /dev/null +++ b/src/plugins/expressions/server/legacy.ts @@ -0,0 +1,135 @@ +/* + * 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. + */ + +/* eslint-disable max-classes-per-file */ + +// TODO: Remove this file once https://github.com/elastic/kibana/issues/46906 is complete. + +// @ts-ignore +import { register, registryFactory, Registry, Fn } from '@kbn/interpreter/common'; + +import Boom from 'boom'; +import { schema } from '@kbn/config-schema'; +import { CoreSetup, Logger } from 'src/core/server'; +import { ExpressionsServerSetupDependencies } from './plugin'; +import { typeSpecs as types, Type } from '../common'; +import { serializeProvider } from '../common'; + +export class TypesRegistry extends Registry { + wrapper(obj: any) { + return new (Type as any)(obj); + } +} + +export class FunctionsRegistry extends Registry { + wrapper(obj: any) { + return new Fn(obj); + } +} + +export const registries = { + types: new TypesRegistry(), + serverFunctions: new FunctionsRegistry(), +}; + +export interface LegacyInterpreterServerApi { + registries(): typeof registries; + register(specs: Record): typeof registries; +} + +export const createLegacyServerInterpreterApi = (): LegacyInterpreterServerApi => { + const api = registryFactory(registries); + + register(registries, { + types, + }); + + return api; +}; + +export const createLegacyServerEndpoints = ( + api: LegacyInterpreterServerApi, + logger: Logger, + core: CoreSetup, + plugins: ExpressionsServerSetupDependencies +) => { + const router = core.http.createRouter(); + + /** + * Register the endpoint that returns the list of server-only functions. + */ + router.get( + { + path: `/api/interpreter/fns`, + validate: { + body: schema.any(), + }, + }, + async (context, request, response) => { + const functions = api.registries().serverFunctions.toJS(); + const body = JSON.stringify(functions); + return response.ok({ + body, + }); + } + ); + + /** + * Run a single Canvas function. + * + * @param {*} server - The Kibana server object + * @param {*} handlers - The Canvas handlers + * @param {*} fnCall - Describes the function being run `{ functionName, args, context }` + */ + async function runFunction(handlers: any, fnCall: any) { + const { functionName, args, context } = fnCall; + const { deserialize } = serializeProvider(registries.types.toJS()); + const fnDef = registries.serverFunctions.toJS()[functionName]; + if (!fnDef) throw Boom.notFound(`Function "${functionName}" could not be found.`); + const deserialized = deserialize(context); + const result = fnDef.fn(deserialized, args, handlers); + return result; + } + + /** + * Register an endpoint that executes a batch of functions, and streams the + * results back using ND-JSON. + */ + plugins.bfetch.addBatchProcessingRoute(`/api/interpreter/fns`, request => { + const scopedClient = core.elasticsearch.dataClient.asScoped(request); + const handlers = { + environment: 'server', + elasticsearchClient: async ( + endpoint: string, + clientParams: Record = {}, + options?: any + ) => scopedClient.callAsCurrentUser(endpoint, clientParams, options), + }; + + return { + onBatchItem: async (fnCall: any) => { + const result = await runFunction(handlers, fnCall); + if (typeof result === 'undefined') { + throw new Error(`Function ${fnCall.functionName} did not return anything.`); + } + return result; + }, + }; + }); +}; diff --git a/src/plugins/expressions/server/mocks.ts b/src/plugins/expressions/server/mocks.ts new file mode 100644 index 0000000000000..4510ae6dc0b4a --- /dev/null +++ b/src/plugins/expressions/server/mocks.ts @@ -0,0 +1,73 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ExpressionsServerSetup, ExpressionsServerStart } from '.'; +import { plugin as pluginInitializer } from '.'; +import { coreMock } from '../../../core/server/mocks'; + +/* eslint-disable */ +import { bfetchPluginMock } from '../../bfetch/server/mocks'; +/* eslint-enable */ + +export type Setup = jest.Mocked; +export type Start = jest.Mocked; + +const createSetupContract = (): Setup => { + const setupContract: Setup = { + __LEGACY: { + register: jest.fn(), + registries: jest.fn(), + }, + }; + return setupContract; +}; + +const createStartContract = (): Start => { + const startContract: Start = {}; + + return startContract; +}; + +const createPlugin = async () => { + const pluginInitializerContext = coreMock.createPluginInitializerContext(); + const coreSetup = coreMock.createSetup(); + const coreStart = coreMock.createStart(); + const plugin = pluginInitializer(pluginInitializerContext); + const setup = await plugin.setup(coreSetup, { + bfetch: bfetchPluginMock.createSetupContract(), + }); + + return { + pluginInitializerContext, + coreSetup, + coreStart, + plugin, + setup, + doStart: async () => + await plugin.start(coreStart, { + bfetch: bfetchPluginMock.createStartContract(), + }), + }; +}; + +export const expressionsPluginMock = { + createSetupContract, + createStartContract, + createPlugin, +}; diff --git a/src/plugins/expressions/server/plugin.ts b/src/plugins/expressions/server/plugin.ts new file mode 100644 index 0000000000000..84c780b5ca226 --- /dev/null +++ b/src/plugins/expressions/server/plugin.ts @@ -0,0 +1,77 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { CoreStart, PluginInitializerContext, CoreSetup, Plugin } from 'src/core/server'; +import { BfetchServerSetup, BfetchServerStart } from '../../bfetch/server'; +import { + LegacyInterpreterServerApi, + createLegacyServerInterpreterApi, + createLegacyServerEndpoints, +} from './legacy'; + +// eslint-disable-next-line +export interface ExpressionsServerSetupDependencies { + bfetch: BfetchServerSetup; +} + +// eslint-disable-next-line +export interface ExpressionsServerStartDependencies { + bfetch: BfetchServerStart; +} + +export interface ExpressionsServerSetup { + __LEGACY: LegacyInterpreterServerApi; +} + +// eslint-disable-next-line +export interface ExpressionsServerStart {} + +export class ExpressionsServerPlugin + implements + Plugin< + ExpressionsServerSetup, + ExpressionsServerStart, + ExpressionsServerSetupDependencies, + ExpressionsServerStartDependencies + > { + constructor(private readonly initializerContext: PluginInitializerContext) {} + + public setup( + core: CoreSetup, + plugins: ExpressionsServerSetupDependencies + ): ExpressionsServerSetup { + const logger = this.initializerContext.logger.get(); + + const legacyApi = createLegacyServerInterpreterApi(); + createLegacyServerEndpoints(legacyApi, logger, core, plugins); + + return { + __LEGACY: legacyApi, + }; + } + + public start( + core: CoreStart, + plugins: ExpressionsServerStartDependencies + ): ExpressionsServerStart { + return {}; + } + + public stop() {} +} diff --git a/src/plugins/kibana_utils/common/index.ts b/src/plugins/kibana_utils/common/index.ts index eb3bb96c8e874..bfb45b88964d8 100644 --- a/src/plugins/kibana_utils/common/index.ts +++ b/src/plugins/kibana_utils/common/index.ts @@ -18,4 +18,5 @@ */ export * from './defer'; +export * from './of'; export { distinctUntilChangedWithInitialValue } from './distinct_until_changed_with_initial_value'; diff --git a/src/plugins/kibana_utils/common/of.test.ts b/src/plugins/kibana_utils/common/of.test.ts new file mode 100644 index 0000000000000..6c3f0ec1592bd --- /dev/null +++ b/src/plugins/kibana_utils/common/of.test.ts @@ -0,0 +1,63 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { of } from './of'; + +describe('of()', () => { + describe('when promise resolves', () => { + const promise = new Promise(resolve => resolve()).then(() => 123); + + test('first member of 3-tuple is the promise value', async () => { + const [result] = await of(promise); + expect(result).toBe(123); + }); + + test('second member of 3-tuple is undefined', async () => { + const [, error] = await of(promise); + expect(error).toBe(undefined); + }); + + test('third, flag member, of 3-tuple is true', async () => { + const [, , resolved] = await of(promise); + expect(resolved).toBe(true); + }); + }); + + describe('when promise rejects', () => { + const promise = new Promise(resolve => resolve()).then(() => { + // eslint-disable-next-line no-throw-literal + throw 123; + }); + + test('first member of 3-tuple is undefined', async () => { + const [result] = await of(promise); + expect(result).toBe(undefined); + }); + + test('second member of 3-tuple is thrown error', async () => { + const [, error] = await of(promise); + expect(error).toBe(123); + }); + + test('third, flag member, of 3-tuple is false', async () => { + const [, , resolved] = await of(promise); + expect(resolved).toBe(false); + }); + }); +}); diff --git a/src/legacy/core_plugins/interpreter/server/lib/create_handlers.ts b/src/plugins/kibana_utils/common/of.ts similarity index 56% rename from src/legacy/core_plugins/interpreter/server/lib/create_handlers.ts rename to src/plugins/kibana_utils/common/of.ts index 6e295d0aecaa5..fa0ec8b0ce306 100644 --- a/src/legacy/core_plugins/interpreter/server/lib/create_handlers.ts +++ b/src/plugins/kibana_utils/common/of.ts @@ -17,16 +17,21 @@ * under the License. */ -export const createHandlers = (request: any, server: any) => { - const { callWithRequest } = server.plugins.elasticsearch.getCluster('data'); - const config = server.config(); - - return { - environment: 'server', - serverUri: - config.has('server.rewriteBasePath') && config.get('server.rewriteBasePath') - ? `${server.info.uri}${config.get('server.basePath')}` - : server.info.uri, - elasticsearchClient: async (...args: any) => callWithRequest(request, ...args), - }; +/** + * Given a promise awaits it and returns a 3-tuple, with the following members: + * + * - First entry is either the resolved value of the promise or `undefined`. + * - Second entry is either the error thrown by promise or `undefined`. + * - Third entry is a boolean, truthy if promise was resolved and falsy if rejected. + * + * @param promise Promise to convert to 3-tuple. + */ +export const of = async ( + promise: Promise +): Promise<[T | undefined, E | undefined, boolean]> => { + try { + return [await promise, undefined, true]; + } catch (error) { + return [undefined, error, false]; + } }; diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts index 0ba444c4e9395..fa58a61e51232 100644 --- a/src/plugins/kibana_utils/public/index.ts +++ b/src/plugins/kibana_utils/public/index.ts @@ -17,7 +17,7 @@ * under the License. */ -export { defer } from '../common'; +export { defer, Defer, of } from '../common'; export * from './core'; export * from './errors'; export * from './field_mapping'; diff --git a/src/plugins/testbed/server/index.ts b/src/plugins/testbed/server/index.ts index 4873fe0926472..96d5612508a96 100644 --- a/src/plugins/testbed/server/index.ts +++ b/src/plugins/testbed/server/index.ts @@ -23,7 +23,6 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { CoreSetup, CoreStart, - LegacyRenderOptions, Logger, PluginInitializerContext, PluginConfigDescriptor, @@ -78,29 +77,6 @@ class Plugin { } ); - router.get( - { - path: '/requestcontext/render/{id}', - validate: { - params: schema.object({ - id: schema.maybe(schema.string()), - }), - }, - }, - async (context, req, res) => { - const { id } = req.params; - const options: Partial = { app: { getId: () => id! } }; - const body = await context.core.rendering.render(options); - - return res.ok({ - body, - headers: { - 'content-securty-policy': core.http.csp.header, - }, - }); - } - ); - return { data$: this.initializerContext.config.create().pipe( map(configValue => { diff --git a/test/api_integration/apis/core/index.js b/test/api_integration/apis/core/index.js index 766faf067e4ac..be1ecf9b9c497 100644 --- a/test/api_integration/apis/core/index.js +++ b/test/api_integration/apis/core/index.js @@ -33,11 +33,6 @@ export default function({ getService }) { 200, 'SavedObjects client: {"page":1,"per_page":20,"total":0,"saved_objects":[]}' )); - - it('provides access to application rendering client', async () => { - await supertest.get('/requestcontext/render/core').expect(200, /app:core/); - await supertest.get('/requestcontext/render/testbed').expect(200, /app:testbed/); - }); }); describe('compression', () => { diff --git a/test/functional/apps/home/_navigation.ts b/test/functional/apps/home/_navigation.ts index 96285901289b6..6fd631baa27d7 100644 --- a/test/functional/apps/home/_navigation.ts +++ b/test/functional/apps/home/_navigation.ts @@ -64,8 +64,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { } }); - // FLAKY: https://github.com/elastic/kibana/issues/33468 - it.skip('detect navigate back issues', async () => { + it('detect navigate back issues', async () => { let currUrl; // Detects bug described in issue #31238 - where back navigation would get stuck to URL encoding handling in Angular. // Navigate to home app diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json index 96efd952e6ba2..d96febee7b06d 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "18.2.0", + "@elastic/eui": "18.2.1", "react": "^16.12.0", "react-dom": "^16.12.0" } diff --git a/test/plugin_functional/config.js b/test/plugin_functional/config.js index e9a4f3bcc4b1a..e63054f1b6912 100644 --- a/test/plugin_functional/config.js +++ b/test/plugin_functional/config.js @@ -38,6 +38,7 @@ export default async function({ readConfigFile }) { require.resolve('./test_suites/embeddable_explorer'), require.resolve('./test_suites/core_plugins'), require.resolve('./test_suites/management'), + require.resolve('./test_suites/bfetch_explorer'), ], services: { ...functionalConfig.get('services'), diff --git a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/kibana.json b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/kibana.json new file mode 100644 index 0000000000000..1acc7df871c94 --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "kbn_tp_bfetch_explorer", + "version": "0.0.1", + "kibanaVersion": "kibana", + "configPath": ["kbn_tp_bfetch_explorer"], + "server": true, + "ui": true, + "requiredPlugins": ["bfetch"], + "optionalPlugins": [] +} diff --git a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/package.json b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/package.json new file mode 100644 index 0000000000000..e396489a1ffc4 --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/package.json @@ -0,0 +1,17 @@ +{ + "name": "kbn_tp_bfetch_explorer", + "version": "1.0.0", + "main": "target/examples/kbn_tp_bfetch_explorer", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "kbn": "node ../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.7.2" + } +} diff --git a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/public/index.ts b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/public/index.ts new file mode 100644 index 0000000000000..547dfe2aa38d2 --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/public/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from '../../../../../examples/bfetch_explorer/public'; diff --git a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/server/index.ts b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/server/index.ts new file mode 100644 index 0000000000000..b4370eb53311e --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/server/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from '../../../../../examples/bfetch_explorer/server'; diff --git a/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/tsconfig.json b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/tsconfig.json new file mode 100644 index 0000000000000..994f81e396763 --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_bfetch_explorer/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true, + "types": [ + "node", + "jest", + "react" + ] + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "server/**/*.tsx", + "../../../../typings/**/*", + ], + "exclude": [] +} diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json index 7693d6f9c07bc..170cc77ca37cc 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/package.json @@ -7,7 +7,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "18.2.0", + "@elastic/eui": "18.2.1", "react": "^16.12.0" } } diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json index bf58535e57994..85c76071d1e94 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "18.2.0", + "@elastic/eui": "18.2.1", "react": "^16.12.0" }, "scripts": { diff --git a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json index 98dd9ab51da96..ade93c9f50099 100644 --- a/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json +++ b/test/plugin_functional/plugins/kbn_tp_sample_panel_action/package.json @@ -8,7 +8,7 @@ }, "license": "Apache-2.0", "dependencies": { - "@elastic/eui": "18.2.0", + "@elastic/eui": "18.2.1", "react": "^16.12.0" }, "scripts": { diff --git a/test/plugin_functional/plugins/rendering_plugin/kibana.json b/test/plugin_functional/plugins/rendering_plugin/kibana.json new file mode 100644 index 0000000000000..886eca2bdde1d --- /dev/null +++ b/test/plugin_functional/plugins/rendering_plugin/kibana.json @@ -0,0 +1,8 @@ +{ + "id": "rendering_plugin", + "version": "0.0.1", + "kibanaVersion": "kibana", + "configPath": ["rendering_plugin"], + "server": true, + "ui": true +} diff --git a/test/plugin_functional/plugins/rendering_plugin/package.json b/test/plugin_functional/plugins/rendering_plugin/package.json new file mode 100644 index 0000000000000..9ab6eccbe31be --- /dev/null +++ b/test/plugin_functional/plugins/rendering_plugin/package.json @@ -0,0 +1,17 @@ +{ + "name": "rendering_plugin", + "version": "1.0.0", + "main": "target/test/plugin_functional/plugins/rendering_plugin", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "kbn": "node ../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.5.3" + } +} diff --git a/test/plugin_functional/plugins/rendering_plugin/public/index.ts b/test/plugin_functional/plugins/rendering_plugin/public/index.ts new file mode 100644 index 0000000000000..c32f3c03e8c63 --- /dev/null +++ b/test/plugin_functional/plugins/rendering_plugin/public/index.ts @@ -0,0 +1,23 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { PluginInitializer } from 'kibana/public'; +import { RenderingPlugin } from './plugin'; + +export const plugin: PluginInitializer = () => new RenderingPlugin(); diff --git a/test/plugin_functional/plugins/rendering_plugin/public/plugin.tsx b/test/plugin_functional/plugins/rendering_plugin/public/plugin.tsx new file mode 100644 index 0000000000000..6e80b56953ca0 --- /dev/null +++ b/test/plugin_functional/plugins/rendering_plugin/public/plugin.tsx @@ -0,0 +1,41 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { Plugin, CoreSetup } from 'kibana/public'; + +export class RenderingPlugin implements Plugin { + public setup(core: CoreSetup) { + core.application.register({ + id: 'rendering', + title: 'Rendering', + appRoute: '/render', + async mount(context, { element }) { + render(

rendering service

, element); + + return () => unmountComponentAtNode(element); + }, + }); + } + + public start() {} + + public stop() {} +} diff --git a/test/plugin_functional/plugins/rendering_plugin/server/index.ts b/test/plugin_functional/plugins/rendering_plugin/server/index.ts new file mode 100644 index 0000000000000..90ffdebb29f29 --- /dev/null +++ b/test/plugin_functional/plugins/rendering_plugin/server/index.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { RenderingPlugin } from './plugin'; + +export const plugin = () => new RenderingPlugin(); diff --git a/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts b/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts new file mode 100644 index 0000000000000..fad19728b7514 --- /dev/null +++ b/test/plugin_functional/plugins/rendering_plugin/server/plugin.ts @@ -0,0 +1,63 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Plugin, CoreSetup, IRenderOptions } from 'kibana/server'; + +import { schema } from '@kbn/config-schema'; + +export class RenderingPlugin implements Plugin { + public setup(core: CoreSetup) { + const router = core.http.createRouter(); + + router.get( + { + path: '/render/{id}', + validate: { + query: schema.object( + { + includeUserSettings: schema.boolean({ defaultValue: true }), + }, + { allowUnknowns: true } + ), + params: schema.object({ + id: schema.maybe(schema.string()), + }), + }, + }, + async (context, req, res) => { + const { id } = req.params; + const { includeUserSettings } = req.query; + const app = { getId: () => id! }; + const options: Partial = { app, includeUserSettings }; + const body = await context.core.rendering.render(options); + + return res.ok({ + body, + headers: { + 'content-security-policy': core.http.csp.header, + }, + }); + } + ); + } + + public start() {} + + public stop() {} +} diff --git a/test/plugin_functional/plugins/rendering_plugin/tsconfig.json b/test/plugin_functional/plugins/rendering_plugin/tsconfig.json new file mode 100644 index 0000000000000..1ba21f11b7de2 --- /dev/null +++ b/test/plugin_functional/plugins/rendering_plugin/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../../tsconfig.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../../typings/**/*", + ], + "exclude": [] +} diff --git a/test/plugin_functional/test_suites/bfetch_explorer/batched_function.ts b/test/plugin_functional/test_suites/bfetch_explorer/batched_function.ts new file mode 100644 index 0000000000000..cb2a0b41694c2 --- /dev/null +++ b/test/plugin_functional/test_suites/bfetch_explorer/batched_function.ts @@ -0,0 +1,93 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../functional/ftr_provider_context'; + +export default function({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const appsMenu = getService('appsMenu'); + + describe('batchedFunction', () => { + beforeEach(async () => { + await appsMenu.clickLink('bfetch explorer'); + await testSubjects.click('count-until'); + await testSubjects.click('double-integers'); + }); + + it('executes all requests in a batch', async () => { + const form = await testSubjects.find('DoubleIntegers'); + const btn = await form.findByCssSelector('button'); + await btn.click(); + await new Promise(r => setTimeout(r, 4000)); + const pre = await form.findByCssSelector('pre'); + const text = await pre.getVisibleText(); + const json = JSON.parse(text); + + expect(json).to.eql([ + { + num: -1, + error: { + message: 'Invalid number', + }, + }, + { + num: 300, + result: { + num: 600, + }, + }, + { + num: 1000, + result: { + num: 2000, + }, + }, + { + num: 2000, + result: { + num: 4000, + }, + }, + ]); + }); + + it('streams results back', async () => { + const form = await testSubjects.find('DoubleIntegers'); + const btn = await form.findByCssSelector('button'); + await btn.click(); + + await new Promise(r => setTimeout(r, 500)); + const pre = await form.findByCssSelector('pre'); + + const text1 = await pre.getVisibleText(); + const json1 = JSON.parse(text1); + + expect(json1.length > 0).to.be(true); + expect(json1.length < 4).to.be(true); + + await new Promise(r => setTimeout(r, 3500)); + + const text2 = await pre.getVisibleText(); + const json2 = JSON.parse(text2); + + expect(json2.length).to.be(4); + }); + }); +} diff --git a/test/plugin_functional/test_suites/bfetch_explorer/index.ts b/test/plugin_functional/test_suites/bfetch_explorer/index.ts new file mode 100644 index 0000000000000..54f127d6de89a --- /dev/null +++ b/test/plugin_functional/test_suites/bfetch_explorer/index.ts @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FtrProviderContext } from '../../../functional/ftr_provider_context'; + +export default function({ getService, getPageObjects, loadTestFile }: FtrProviderContext) { + const browser = getService('browser'); + const appsMenu = getService('appsMenu'); + const PageObjects = getPageObjects(['common', 'header']); + + describe('bfetch explorer', function() { + before(async () => { + await browser.setWindowSize(1300, 900); + await PageObjects.common.navigateToApp('settings'); + await appsMenu.clickLink('bfetch explorer'); + }); + + loadTestFile(require.resolve('./batched_function')); + }); +} diff --git a/test/plugin_functional/test_suites/core_plugins/applications.ts b/test/plugin_functional/test_suites/core_plugins/applications.ts index 231458fad155b..f50d460532556 100644 --- a/test/plugin_functional/test_suites/core_plugins/applications.ts +++ b/test/plugin_functional/test_suites/core_plugins/applications.ts @@ -121,13 +121,13 @@ export default function({ getService, getPageObjects }: PluginFunctionalProvider expect(wrapperWidth).to.be.below(windowWidth); }); - it.skip('can navigate from NP apps to legacy apps', async () => { + it('can navigate from NP apps to legacy apps', async () => { await appsMenu.clickLink('Management'); await loadingScreenShown(); await testSubjects.existOrFail('managementNav'); }); - it.skip('can navigate from legacy apps to NP apps', async () => { + it('can navigate from legacy apps to NP apps', async () => { await appsMenu.clickLink('Foo'); await loadingScreenShown(); await testSubjects.existOrFail('fooAppHome'); diff --git a/test/plugin_functional/test_suites/core_plugins/index.ts b/test/plugin_functional/test_suites/core_plugins/index.ts index d66e2e7dc5da7..d57ad11ea004e 100644 --- a/test/plugin_functional/test_suites/core_plugins/index.ts +++ b/test/plugin_functional/test_suites/core_plugins/index.ts @@ -29,5 +29,6 @@ export default function({ loadTestFile }: PluginFunctionalProviderContext) { loadTestFile(require.resolve('./top_nav')); loadTestFile(require.resolve('./application_leave_confirm')); loadTestFile(require.resolve('./application_status')); + loadTestFile(require.resolve('./rendering')); }); } diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts new file mode 100644 index 0000000000000..d0025c82a7ba5 --- /dev/null +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -0,0 +1,127 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import expect from '@kbn/expect'; + +import '../../plugins/core_provider_plugin/types'; +import { PluginFunctionalProviderContext } from '../../services'; + +// eslint-disable-next-line import/no-default-export +export default function({ getService, getPageObjects }: PluginFunctionalProviderContext) { + const PageObjects = getPageObjects(['common']); + const browser = getService('browser'); + const find = getService('find'); + const testSubjects = getService('testSubjects'); + + function navigate(path: string) { + return browser.get(`${PageObjects.common.getHostPort()}${path}`); + } + + function getLegacyMode() { + return browser.execute(() => { + return JSON.parse(document.querySelector('kbn-injected-metadata')!.getAttribute('data')!) + .legacyMode; + }); + } + + function getUserSettings() { + return browser.execute(() => { + return JSON.parse(document.querySelector('kbn-injected-metadata')!.getAttribute('data')!) + .legacyMetadata.uiSettings.user; + }); + } + + async function init() { + const loading = await testSubjects.find('kbnLoadingMessage', 5000); + + return () => find.waitForElementStale(loading); + } + + describe('rendering service', () => { + it('renders "core" application', async () => { + await navigate('/render/core'); + + const [loaded, legacyMode, userSettings] = await Promise.all([ + init(), + getLegacyMode(), + getUserSettings(), + ]); + + expect(legacyMode).to.be(false); + expect(userSettings).to.not.be.empty(); + + await loaded(); + + expect(await testSubjects.exists('renderingHeader')).to.be(true); + }); + + it('renders "core" application without user settings', async () => { + await navigate('/render/core?includeUserSettings=false'); + + const [loaded, legacyMode, userSettings] = await Promise.all([ + init(), + getLegacyMode(), + getUserSettings(), + ]); + + expect(legacyMode).to.be(false); + expect(userSettings).to.be.empty(); + + await loaded(); + + expect(await testSubjects.exists('renderingHeader')).to.be(true); + }); + + it('renders "legacy" application', async () => { + await navigate('/render/core_plugin_legacy'); + + const [loaded, legacyMode, userSettings] = await Promise.all([ + init(), + getLegacyMode(), + getUserSettings(), + ]); + + expect(legacyMode).to.be(true); + expect(userSettings).to.not.be.empty(); + + await loaded(); + + expect(await testSubjects.exists('coreLegacyCompatH1')).to.be(true); + expect(await testSubjects.exists('renderingHeader')).to.be(false); + }); + + it('renders "legacy" application without user settings', async () => { + await navigate('/render/core_plugin_legacy?includeUserSettings=false'); + + const [loaded, legacyMode, userSettings] = await Promise.all([ + init(), + getLegacyMode(), + getUserSettings(), + ]); + + expect(legacyMode).to.be(true); + expect(userSettings).to.be.empty(); + + await loaded(); + + expect(await testSubjects.exists('coreLegacyCompatH1')).to.be(true); + expect(await testSubjects.exists('renderingHeader')).to.be(false); + }); + }); +} diff --git a/test/plugin_functional/test_suites/core_plugins/server_plugins.ts b/test/plugin_functional/test_suites/core_plugins/server_plugins.ts index 3f89036fc2b65..eb232b1458991 100644 --- a/test/plugin_functional/test_suites/core_plugins/server_plugins.ts +++ b/test/plugin_functional/test_suites/core_plugins/server_plugins.ts @@ -54,15 +54,5 @@ export default function({ getService }: PluginFunctionalProviderContext) { statusCode: 400, }); }); - - it('renders core application explicitly', async () => { - await supertest.get('/requestcontext/render/core').expect(200, /app:core/); - }); - - it('renders legacy application', async () => { - await supertest - .get('/requestcontext/render/core_plugin_legacy') - .expect(200, /app:core_plugin_legacy/); - }); }); } diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/PlatinumLicensePrompt.stories.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/PlatinumLicensePrompt.stories.tsx new file mode 100644 index 0000000000000..80281c1a0a8fc --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/PlatinumLicensePrompt.stories.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 { storiesOf } from '@storybook/react'; +import React from 'react'; +import { PlatinumLicensePrompt } from './PlatinumLicensePrompt'; +import { + ApmPluginContext, + ApmPluginContextValue +} from '../../../context/ApmPluginContext'; + +storiesOf('app/ServiceMap/PlatinumLicensePrompt', module).add( + 'example', + () => { + const contextMock = ({ + core: { http: { basePath: { prepend: () => {} } } } + } as unknown) as ApmPluginContextValue; + + return ( + + + + ); + }, + { + info: { + source: false + } + } +); diff --git a/x-pack/legacy/plugins/apm/readme.md b/x-pack/legacy/plugins/apm/readme.md index 6b21f08b7695e..2106243d12aea 100644 --- a/x-pack/legacy/plugins/apm/readme.md +++ b/x-pack/legacy/plugins/apm/readme.md @@ -40,7 +40,6 @@ For testing purposes APM uses 3 custom users: **kibana_write_user** Apps: read/write. Indices: None - To create the users with the correct roles run the following script: ```sh @@ -88,6 +87,12 @@ yarn prettier "./x-pack/legacy/plugins/apm/**/*.{tsx,ts,js}" --write yarn eslint ./x-pack/legacy/plugins/apm --fix ``` +#### Storybook + +Start the [Storybook](https://storybook.js.org/) development environment with +`yarn storybook apm`. All files with a .stories.tsx extension will be loaded. +You can access the development environment at http://localhost:9001. + #### Further resources - [Cypress integration tests](cypress/README.md) diff --git a/x-pack/legacy/plugins/apm/scripts/storybook.js b/x-pack/legacy/plugins/apm/scripts/storybook.js new file mode 100644 index 0000000000000..846ee1f301e30 --- /dev/null +++ b/x-pack/legacy/plugins/apm/scripts/storybook.js @@ -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. + */ + +import { join } from 'path'; + +// eslint-disable-next-line +require('@kbn/storybook').runStorybookCli({ + name: 'apm', + storyGlobs: [ + join(__dirname, '..', 'public', 'components', '**', '*.stories.tsx') + ] +}); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/time_picker_popover/time_picker_popover.tsx b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/time_picker_popover/time_picker_popover.tsx index 1dafd7ba648c3..8f6061b688319 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/time_picker_popover/time_picker_popover.tsx +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/time_filter/components/time_picker_popover/time_picker_popover.tsx @@ -42,7 +42,16 @@ export const TimePickerPopover: FunctionComponent = ({ from, to, onSelect anchorClassName="canvasTimePickerPopover__anchor" button={button} > - {() => } + {({ closePopover }) => ( + { + onSelect(...args); + closePopover(); + }} + /> + )} ); }; diff --git a/x-pack/legacy/plugins/canvas/public/components/element_wrapper/index.js b/x-pack/legacy/plugins/canvas/public/components/element_wrapper/index.js index 1934eab85d034..60c7e731691fa 100644 --- a/x-pack/legacy/plugins/canvas/public/components/element_wrapper/index.js +++ b/x-pack/legacy/plugins/canvas/public/components/element_wrapper/index.js @@ -58,7 +58,9 @@ function selectorFactory(dispatch) { export const ElementWrapper = compose( connectAdvanced(selectorFactory), withPropsOnChange( - (props, nextProps) => !isEqual(props.element, nextProps.element), + (props, nextProps) => + !isEqual(props.element, nextProps.element) || + !isEqual(props.selectedPage, nextProps.selectedPage), props => { const { element, createHandlers } = props; const handlers = createHandlers(element, props.selectedPage); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts index a8ae785adafc1..2b2a582fb4526 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/index.ts @@ -62,12 +62,8 @@ export const WorkpadExport = compose( enabled, getExportUrl: type => { if (type === 'pdf') { - const { createPdfUri } = getPdfUrl( - workpad, - { pageCount }, - kibana.services.http.basePath.prepend - ); - return getAbsoluteUrl(createPdfUri); + const pdfUrl = getPdfUrl(workpad, { pageCount }, kibana.services.http.basePath.prepend); + return getAbsoluteUrl(pdfUrl); } throw new Error(strings.getUnknownExportErrorMessage(type)); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.test.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.test.ts new file mode 100644 index 0000000000000..ceaf82c1c07d6 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.test.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +jest.mock('../../../../common/lib/fetch'); + +import { getPdfUrl, createPdf } from './utils'; +import { workpads } from '../../../../__tests__/fixtures/workpads'; +import { fetch } from '../../../../common/lib/fetch'; + +const addBasePath = jest.fn().mockImplementation(s => `basepath/${s}`); +const workpad = workpads[0]; + +test('getPdfUrl returns the correct url', () => { + const url = getPdfUrl(workpad, { pageCount: 2 }, addBasePath); + + expect(url).toMatchInlineSnapshot( + `"basepath//api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FPhoenix,layout:(dimensions:(height:0,width:0),id:preserve_layout),objectType:'canvas%20workpad',relativeUrls:!(%2Fapp%2Fcanvas%23%2Fexport%2Fworkpad%2Fpdf%2Fbase-workpad%2Fpage%2F1,%2Fapp%2Fcanvas%23%2Fexport%2Fworkpad%2Fpdf%2Fbase-workpad%2Fpage%2F2),title:'base%20workpad')"` + ); +}); + +test('createPdf posts to create the pdf', () => { + createPdf(workpad, { pageCount: 2 }, addBasePath); + + expect(fetch.post).toBeCalled(); + + const args = (fetch.post as jest.MockedFunction).mock.calls[0]; + + expect(args[0]).toMatchInlineSnapshot(`"basepath//api/reporting/generate/printablePdf"`); + expect(args[1]).toMatchInlineSnapshot(` + Object { + "jobParams": "(browserTimezone:America/Phoenix,layout:(dimensions:(height:0,width:0),id:preserve_layout),objectType:'canvas workpad',relativeUrls:!(/app/canvas#/export/workpad/pdf/base-workpad/page/1,/app/canvas#/export/workpad/pdf/base-workpad/page/2),title:'base workpad')", + } + `); +}); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts index f0ca5fac1d271..f7f191a48de82 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts @@ -7,6 +7,7 @@ import rison from 'rison-node'; // @ts-ignore Untyped local. import { fetch } from '../../../../common/lib/fetch'; +import { getStartPlugins } from '../../../legacy'; import { CanvasWorkpad } from '../../../../types'; // type of the desired pdf output (print or preserve_layout) @@ -25,7 +26,7 @@ interface PdfUrlData { createPdfPayload: { jobParams: string }; } -export function getPdfUrl( +function getPdfUrlParts( { id, name: title, width, height }: CanvasWorkpad, { pageCount }: PageCount, addBasePath: (path: string) => string @@ -68,7 +69,16 @@ export function getPdfUrl( }; } +export function getPdfUrl(...args: Arguments): string { + const urlParts = getPdfUrlParts(...args); + + return `${urlParts.createPdfUri}?${getStartPlugins().__LEGACY.QueryString.param( + 'jobParams', + urlParts.createPdfPayload.jobParams + )}`; +} + export function createPdf(...args: Arguments) { - const { createPdfUri, createPdfPayload } = getPdfUrl(...args); + const { createPdfUri, createPdfPayload } = getPdfUrlParts(...args); return fetch.post(createPdfUri, createPdfPayload); } diff --git a/x-pack/legacy/plugins/canvas/public/legacy.ts b/x-pack/legacy/plugins/canvas/public/legacy.ts index 49b88ee60921a..61e12893b3e02 100644 --- a/x-pack/legacy/plugins/canvas/public/legacy.ts +++ b/x-pack/legacy/plugins/canvas/public/legacy.ts @@ -15,6 +15,8 @@ import { absoluteToParsedUrl } from 'ui/url/absolute_to_parsed_url'; // eslint-d import { Storage } from '../../../../../src/plugins/kibana_utils/public'; // eslint-disable-line import/order // @ts-ignore Untyped Kibana Lib import { formatMsg } from 'ui/notify/lib/format_msg'; // eslint-disable-line import/order +// @ts-ignore Untyped Kibana Lib +import { QueryString } from 'ui/utils/query_string'; // eslint-disable-line import/order const shimCoreSetup = { ...npSetup.core, @@ -30,6 +32,7 @@ const shimStartPlugins: CanvasStartDeps = { absoluteToParsedUrl, // ToDo: Copy directly into canvas formatMsg, + QueryString, // ToDo: Remove in favor of core.application.register setRootController: chrome.setRootController, storage: Storage, diff --git a/x-pack/legacy/plugins/canvas/public/plugin.tsx b/x-pack/legacy/plugins/canvas/public/plugin.tsx index 9828845d9ffa9..155eef99632a0 100644 --- a/x-pack/legacy/plugins/canvas/public/plugin.tsx +++ b/x-pack/legacy/plugins/canvas/public/plugin.tsx @@ -39,6 +39,7 @@ export interface CanvasStartDeps { __LEGACY: { absoluteToParsedUrl: (url: string, basePath: string) => any; formatMsg: any; + QueryString: any; setRootController: Chrome['setRootController']; storage: typeof Storage; trackSubUrlForApp: Chrome['trackSubUrlForApp']; diff --git a/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js b/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js index 470fa00734d27..391973f6d909b 100644 --- a/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js +++ b/x-pack/legacy/plugins/dashboard_mode/public/dashboard_viewer.js @@ -30,7 +30,7 @@ import 'uiExports/shareContextMenuExtensions'; import _ from 'lodash'; import 'ui/autoload/all'; import 'ui/kbn_top_nav'; -import 'ui/vislib'; +import 'ui/color_maps'; import 'ui/agg_response'; import 'ui/agg_types'; import 'leaflet'; diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/document_fields_header.tsx b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/document_fields_header.tsx index a97e54afbf067..a4e746aa4037d 100644 --- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/document_fields_header.tsx +++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/document_fields_header.tsx @@ -46,7 +46,15 @@ export const DocumentFieldsHeader = React.memo(({ searchValue, onSearchChange }: } )} value={searchValue} - onChange={e => onSearchChange(e.target.value)} + onChange={e => { + // Temporary fix until EUI fixes the contract + // See my comment https://github.com/elastic/eui/pull/2723/files#r366725059 + if (typeof e === 'string') { + onSearchChange(e); + } else { + onSearchChange(e.target.value); + } + }} aria-label={i18n.translate( 'xpack.idxMgmt.mappingsEditor.documentFields.searchFieldsAriaLabel', { diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx index 9402a64b22dde..a55bd96dce3d0 100644 --- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx +++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx @@ -234,7 +234,7 @@ export const LoadMappingsProvider = ({ onJson, children }: Props) => { mappings, }} diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/templates_form/templates_form.tsx b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/templates_form/templates_form.tsx index 0aa6a90039a86..471217108ba6f 100644 --- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/templates_form/templates_form.tsx +++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/templates_form/templates_form.tsx @@ -62,8 +62,11 @@ export const TemplatesForm = React.memo(({ defaultValue }: Props) => { const dispatch = useDispatch(); useEffect(() => { - const subscription = form.subscribe(updatedTemplates => { - dispatch({ type: 'templates.update', value: { ...updatedTemplates, form } }); + const subscription = form.subscribe(({ data, isValid, validate }) => { + dispatch({ + type: 'templates.update', + value: { data, isValid, validate, submitForm: form.submit }, + }); }); return subscription.unsubscribe; }, [form]); diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/parameters_definition.tsx b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/parameters_definition.tsx index 581b1223b7892..1277568b71bbd 100644 --- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/parameters_definition.tsx +++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/parameters_definition.tsx @@ -100,14 +100,10 @@ const fielddataFrequencyFilterParam = { }, }, }, - schema: t.intersection([ - t.partial({ - min: t.number, - max: t.number, - min_segment_size: t.number, - }), - t.brand(t.UnknownRecord, (v: any): v is any => !Array.isArray(v), 'Array'), - ]), + schema: t.record( + t.union([t.literal('min'), t.literal('max'), t.literal('min_segment_size')]), + t.number + ), }; const analyzerValidations = [ diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/error_reporter.ts b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/error_reporter.ts index 363ccfc2a5fab..e9beee1071597 100644 --- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/error_reporter.ts +++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/error_reporter.ts @@ -3,14 +3,14 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { ValidationError, Validation } from 'io-ts'; +import { ValidationError } from 'io-ts'; import { fold } from 'fp-ts/lib/Either'; import { Reporter } from 'io-ts/lib/Reporter'; export type ReporterResult = Array<{ path: string[]; message: string }>; -export const failure = (validation: any): ReporterResult => { - return validation.map((e: ValidationError) => { +const failure = (validation: ValidationError[]): ReporterResult => { + return validation.map(e => { const path: string[] = []; let validationName = ''; @@ -37,6 +37,6 @@ export const failure = (validation: any): ReporterResult => { const empty: never[] = []; const success = () => empty; -export const ErrorReporter: Reporter = { - report: (validation: Validation) => fold(failure, success)(validation as any), +export const errorReporter: Reporter = { + report: fold(failure, success), }; diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.ts b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.ts index 990d5ec961a6f..fff735da2e758 100644 --- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.ts +++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.ts @@ -8,7 +8,7 @@ import * as t from 'io-ts'; import { ordString } from 'fp-ts/lib/Ord'; import { toArray } from 'fp-ts/lib/Set'; import { isLeft, isRight } from 'fp-ts/lib/Either'; -import { ErrorReporter } from './error_reporter'; +import { errorReporter } from './error_reporter'; import { ALL_DATA_TYPES, PARAMETERS_DEFINITION } from '../constants'; import { FieldMeta } from '../types'; import { getFieldMeta } from '../lib'; @@ -218,7 +218,7 @@ const mappingsConfigurationSchemaKeys = Object.keys(mappingsConfigurationSchema. const validateMappingsConfiguration = ( mappingsConfiguration: any ): { value: any; errors: MappingsValidationError[] } => { - // Array to keep track of invalid configuration parameters. + // Set to keep track of invalid configuration parameters. const configurationRemoved: Set = new Set(); let copyOfMappingsConfig = { ...mappingsConfiguration }; @@ -228,7 +228,7 @@ const validateMappingsConfiguration = ( /** * To keep the logic simple we will strip out the parameters that contain errors */ - const errors = ErrorReporter.report(result); + const errors = errorReporter.report(result); errors.forEach(error => { const configurationName = error.path[0]; configurationRemoved.add(configurationName); diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_editor.tsx b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_editor.tsx index d1fee4c0af745..e3fdf42d889e9 100644 --- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_editor.tsx +++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_editor.tsx @@ -68,7 +68,7 @@ export const MappingsEditor = React.memo(({ onUpdate, defaultValue, indexSetting return; } } else if (selectedTab === 'templates') { - const { isValid: isTemplatesFormValid } = await state.templates.form!.submit(); + const { isValid: isTemplatesFormValid } = await state.templates.submitForm!(); if (!isTemplatesFormValid) { return; diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_state.tsx b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_state.tsx index 54cdea9ff8a42..65a1aa2858d14 100644 --- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_state.tsx +++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_state.tsx @@ -149,8 +149,11 @@ export const MappingsState = React.memo(({ children, onUpdate, defaultValue }: P : Promise.resolve(true); const templatesFormValidator = - state.templates.form !== undefined - ? (await state.templates.form!.submit()).isValid + state.templates.submitForm !== undefined + ? new Promise(async resolve => { + const { isValid } = await state.templates.submitForm!(); + resolve(isValid); + }) : Promise.resolve(true); const promisesToValidate = [configurationFormValidator, templatesFormValidator]; diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/reducer.ts b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/reducer.ts index e843f4e841631..26d5b8e1edfa5 100644 --- a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/reducer.ts +++ b/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/reducer.ts @@ -58,6 +58,11 @@ interface ConfigurationFormState extends OnFormUpdateArg submitForm?: FormHook['submit']; } +interface TemplatesFormState extends OnFormUpdateArg { + defaultValue: MappingsTemplates; + submitForm?: FormHook['submit']; +} + export interface State { isValid: boolean | undefined; configuration: ConfigurationFormState; @@ -72,12 +77,7 @@ export interface State { term: string; result: SearchResult[]; }; - templates: { - defaultValue: { - dynamic_templates: MappingsTemplates['dynamic_templates']; - }; - form?: FormHook; - } & OnFormUpdateArg; + templates: TemplatesFormState; } export type Action = diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js index d28f483c9b987..d38a23560fa9f 100644 --- a/x-pack/legacy/plugins/maps/index.js +++ b/x-pack/legacy/plugins/maps/index.js @@ -16,8 +16,7 @@ import { APP_ID, APP_ICON, createMapPath, MAP_SAVED_OBJECT_TYPE } from './common export function maps(kibana) { return new kibana.Plugin({ - // task_manager could be required, but is only used for telemetry - require: ['kibana', 'elasticsearch', 'xpack_main', 'tile_map'], + require: ['kibana', 'elasticsearch'], id: APP_ID, configPrefix: 'xpack.maps', publicDir: resolve(__dirname, 'public'), diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js index b9346516eeea8..ec3a588d3627f 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.js @@ -23,7 +23,6 @@ import { getQueryableUniqueIndexPatternIds } from '../selectors/map_selectors'; import { getInitialLayers } from '../angular/get_initial_layers'; import { mergeInputWithSavedMap } from './merge_input_with_saved_map'; import '../angular/services/gis_map_saved_object_loader'; -import 'ui/vis/map/service_settings'; export class MapEmbeddableFactory extends EmbeddableFactory { type = MAP_SAVED_OBJECT_TYPE; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js index fd3ae8f0ab7e3..967a3c41aec26 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js @@ -15,7 +15,7 @@ import { FIELD_ORIGIN, } from '../../../common/constants'; -const AGG_DELIMITER = '_of_'; +export const AGG_DELIMITER = '_of_'; export class AbstractESAggSource extends AbstractESSource { static METRIC_SCHEMA_CONFIG = { @@ -53,33 +53,12 @@ export class AbstractESAggSource extends AbstractESSource { : []; } - createField({ fieldName, label }) { - //if there is a corresponding field with a custom label, use that one. - if (!label) { - const matchField = this._metricFields.find(field => field.getName() === fieldName); - if (matchField) { - label = matchField.getLabel(); - } - } + getFieldByName(name) { + return this.getMetricFieldForName(name); + } - if (fieldName === COUNT_PROP_NAME) { - return new ESAggMetricField({ - aggType: COUNT_AGG_TYPE, - label: label, - source: this, - origin: this.getOriginForField(), - }); - } - //this only works because aggType is a fixed set and does not include the `_of_` string - const [aggType, docField] = fieldName.split(AGG_DELIMITER); - const esDocField = new ESDocField({ fieldName: docField, source: this }); - return new ESAggMetricField({ - label: label, - esDocField, - aggType, - source: this, - origin: this.getOriginForField(), - }); + createField() { + throw new Error('Cannot create a new field from just a fieldname for an es_agg_source.'); } hasMatchingMetricField(fieldName) { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js index 484d9853b88cd..bf679e766470c 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js @@ -39,7 +39,7 @@ const requestTypeOptions = [ }, { label: i18n.translate('xpack.maps.source.esGeoGrid.pointsDropdownOption', { - defaultMessage: 'points', + defaultMessage: 'clusters', }), value: RENDER_AS.POINT, }, diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js index 6e8b2e1e7c6f5..443668984b0b4 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js @@ -9,9 +9,14 @@ import _ from 'lodash'; import { Schemas } from 'ui/vis/editors/default/schemas'; import { AggConfigs } from 'ui/agg_types'; import { i18n } from '@kbn/i18n'; -import { DEFAULT_MAX_BUCKETS_LIMIT, FIELD_ORIGIN, METRIC_TYPE } from '../../../common/constants'; +import { + COUNT_PROP_LABEL, + DEFAULT_MAX_BUCKETS_LIMIT, + FIELD_ORIGIN, + METRIC_TYPE, +} from '../../../common/constants'; import { ESDocField } from '../fields/es_doc_field'; -import { AbstractESAggSource } from './es_agg_source'; +import { AbstractESAggSource, AGG_DELIMITER } from './es_agg_source'; const TERMS_AGG_NAME = 'join'; @@ -85,14 +90,15 @@ export class ESTermSource extends AbstractESAggSource { } formatMetricKey(aggType, fieldName) { - const metricKey = aggType !== METRIC_TYPE.COUNT ? `${aggType}_of_${fieldName}` : aggType; + const metricKey = + aggType !== METRIC_TYPE.COUNT ? `${aggType}${AGG_DELIMITER}${fieldName}` : aggType; return `${FIELD_NAME_PREFIX}${metricKey}${GROUP_BY_DELIMITER}${ this._descriptor.indexPatternTitle }.${this._termField.getName()}`; } formatMetricLabel(type, fieldName) { - const metricLabel = type !== METRIC_TYPE.COUNT ? `${type} ${fieldName}` : 'count'; + const metricLabel = type !== METRIC_TYPE.COUNT ? `${type} ${fieldName}` : COUNT_PROP_LABEL; return `${metricLabel} of ${this._descriptor.indexPatternTitle}:${this._termField.getName()}`; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js index b9d8ae86c5850..3952aacf03b33 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js @@ -50,10 +50,26 @@ export class AbstractVectorSource extends AbstractSource { ); } + /** + * factory function creating a new field-instance + * @param fieldName + * @param label + * @returns {ESAggMetricField} + */ createField() { throw new Error(`Should implemement ${this.constructor.type} ${this}`); } + /** + * Retrieves a field. This may be an existing instance. + * @param fieldName + * @param label + * @returns {ESAggMetricField} + */ + getFieldByName(name) { + return this.createField({ fieldName: name }); + } + _createDefaultLayerDescriptor(options, mapColors) { return VectorLayer.createDescriptor( { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js b/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js index 8ba39fc7bbe26..df212f23cd894 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js @@ -6,7 +6,7 @@ import React from 'react'; -import { vislibColorMaps } from 'ui/vislib/components/color/colormaps'; +import { vislibColorMaps } from 'ui/color_maps'; import { getLegendColors, getColor } from 'ui/vis/map/color_util'; import { ColorGradient } from './components/color_gradient'; import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js index 42e88220bd1d9..e80ccb9e144b9 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js @@ -227,7 +227,7 @@ export class DynamicColorProperty extends DynamicStyleProperty { } mbStops.push(defaultColor); //last color is default color - return ['match', ['get', this._options.field.name], ...mbStops]; + return ['match', ['to-string', ['get', this._options.field.name]], ...mbStops]; } _getMbOrdinalColorStops() { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js index 30d1c5726ba48..ec0afb6a6a37e 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js @@ -579,9 +579,7 @@ export class VectorStyle extends AbstractStyle { //fieldDescriptor.label is ignored. This is essentially cruft duplicating label-info from the metric-selection //Ignore this custom label if (fieldDescriptor.origin === FIELD_ORIGIN.SOURCE) { - return this._source.createField({ - fieldName: fieldDescriptor.name, - }); + return this._source.getFieldByName(fieldDescriptor.name); } else if (fieldDescriptor.origin === FIELD_ORIGIN.JOIN) { const join = this._layer.getValidJoins().find(join => { return join.getRightJoinSource().hasMatchingMetricField(fieldDescriptor.name); diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js index c250d83720580..b8f5c74139a63 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js +++ b/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js @@ -30,6 +30,9 @@ class MockSource { getSupportedShapeTypes() { return this._supportedShapeTypes; } + getFieldByName(fieldName) { + return new MockField({ fieldName }); + } createField({ fieldName }) { return new MockField({ fieldName }); } diff --git a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table.test.js b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table.test.js index b40808d51dd66..206b9e01bab8c 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table.test.js +++ b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table.test.js @@ -63,9 +63,6 @@ describe('AnomaliesTable', () => { expect(columns).toEqual( expect.arrayContaining([ - expect.objectContaining({ - name: '', - }), expect.objectContaining({ name: 'time', }), diff --git a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js index 5454911673fe2..58f1214c11e10 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js +++ b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js @@ -4,12 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButtonIcon, EuiLink } from '@elastic/eui'; +import { EuiButtonIcon, EuiLink, EuiScreenReaderOnly } from '@elastic/eui'; import React from 'react'; import _ from 'lodash'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { formatHumanReadableDate, @@ -65,7 +66,16 @@ export function getColumns( ) { const columns = [ { - name: '', + name: ( + +

+ +

+
+ ), render: item => ( toggleRow(item)} diff --git a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js index 8cbee27bdd9a8..074a584f3a136 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js +++ b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js @@ -479,7 +479,6 @@ export const LinksMenu = injectI18n( return ( (); + +const showingDocs = i18n.translate( + 'xpack.ml.dataframe.analytics.classificationExploration.documentsShownHelpText', + { + defaultMessage: 'Showing documents for which predictions exist', + } +); + +const showingFirstDocs = i18n.translate( + 'xpack.ml.dataframe.analytics.classificationExploration.firstDocumentsShownHelpText', + { + defaultMessage: 'Showing first {searchSize} documents for which predictions exist', + values: { searchSize: SEARCH_SIZE }, + } +); + interface Props { jobConfig: DataFrameAnalyticsConfig; jobStatus: DATA_FRAME_TASK_STATE; @@ -468,19 +484,11 @@ export const ResultsTable: FC = React.memo( )} {(columns.length > 0 || searchQuery !== defaultSearchQuery) && ( - {tableItems.length === SEARCH_SIZE && ( - - - - )} + + + { describe('Data Frame Analytics: ', () => { test('Minimal initialization', () => { const wrapper = shallow( - + + + ); // Without the jobConfig being loaded, the component will just return empty. expect(wrapper.text()).toMatch(''); diff --git a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx index 9691a0706121c..098f8f07bee44 100644 --- a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx +++ b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/exploration.tsx @@ -55,6 +55,7 @@ import { SEARCH_SIZE, defaultSearchQuery, } from '../../../../common'; +import { isKeywordAndTextType } from '../../../../common/fields'; import { getOutlierScoreFieldName } from './common'; import { useExploreData, TableItem } from './use_explore_data'; @@ -64,6 +65,10 @@ import { } from '../../../analytics_management/components/analytics_list/common'; import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns'; import { SavedSearchQuery } from '../../../../../contexts/kibana'; +import { getIndexPatternIdFromName } from '../../../../../util/index_utils'; +import { IIndexPattern } from '../../../../../../../../../../../src/plugins/data/common/index_patterns'; +import { newJobCapsService } from '../../../../../services/new_job_capabilities_service'; +import { useKibanaContext } from '../../../../../contexts/kibana'; const FEATURE_INFLUENCE = 'feature_influence'; @@ -110,6 +115,19 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { const [searchError, setSearchError] = useState(undefined); const [searchString, setSearchString] = useState(undefined); + const kibanaContext = useKibanaContext(); + + const initializeJobCapsService = async () => { + if (jobConfig !== undefined) { + const sourceIndex = jobConfig.source.index[0]; + const indexPatternId = getIndexPatternIdFromName(sourceIndex) || sourceIndex; + const indexPattern: IIndexPattern = await kibanaContext.indexPatterns.get(indexPatternId); + if (indexPattern !== undefined) { + await newJobCapsService.initializeFromIndexPattern(indexPattern, false, false); + } + } + }; + useEffect(() => { (async function() { const analyticsConfigs: GetDataFrameAnalyticsResponse = await ml.dataFrameAnalytics.getDataFrameAnalytics( @@ -124,6 +142,10 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { })(); }, []); + useEffect(() => { + initializeJobCapsService(); + }, [jobConfig && jobConfig.id]); + const [selectedFields, setSelectedFields] = useState([] as EsFieldName[]); const [isColumnsPopoverVisible, setColumnsPopoverVisible] = useState(false); @@ -293,10 +315,16 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { if (jobConfig !== undefined) { const outlierScoreFieldName = getOutlierScoreFieldName(jobConfig); const outlierScoreFieldSelected = selectedFields.includes(outlierScoreFieldName); + let requiresKeyword = false; const field = outlierScoreFieldSelected ? outlierScoreFieldName : selectedFields[0]; const direction = outlierScoreFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; - loadExploreData({ field, direction, searchQuery }); + + if (outlierScoreFieldSelected === false) { + requiresKeyword = isKeywordAndTextType(field); + } + + loadExploreData({ field, direction, searchQuery, requiresKeyword }); } }, [JSON.stringify(searchQuery)]); @@ -307,10 +335,16 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { if (jobConfig !== undefined && columns.length > 0 && !selectedFields.includes(sortField)) { const outlierScoreFieldName = getOutlierScoreFieldName(jobConfig); const outlierScoreFieldSelected = selectedFields.includes(outlierScoreFieldName); + let requiresKeyword = false; const field = outlierScoreFieldSelected ? outlierScoreFieldName : selectedFields[0]; const direction = outlierScoreFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; - loadExploreData({ field, direction, searchQuery }); + + if (outlierScoreFieldSelected === false) { + requiresKeyword = isKeywordAndTextType(field); + } + + loadExploreData({ field, direction, searchQuery, requiresKeyword }); return; } }, [jobConfig, columns.length, sortField, sortDirection, tableItems.length]); @@ -334,8 +368,17 @@ export const Exploration: FC = React.memo(({ jobId, jobStatus }) => { setPageIndex(index); setPageSize(size); - if (sort.field !== sortField || sort.direction !== sortDirection) { - loadExploreData({ ...sort, searchQuery }); + if ( + (sort.field !== sortField || sort.direction !== sortDirection) && + jobConfig !== undefined + ) { + const outlierScoreFieldName = getOutlierScoreFieldName(jobConfig); + let requiresKeyword = false; + + if (outlierScoreFieldName !== sort.field) { + requiresKeyword = isKeywordAndTextType(sort.field); + } + loadExploreData({ ...sort, searchQuery, requiresKeyword }); } }; } diff --git a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts index e76cbaa463f1d..24cc8d000de7e 100644 --- a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts +++ b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration/use_explore_data.ts @@ -23,18 +23,12 @@ import { defaultSearchQuery, SearchQuery, } from '../../../../common'; +import { LoadExploreDataArg } from '../../../../common/analytics'; import { getOutlierScoreFieldName } from './common'; -import { SavedSearchQuery } from '../../../../../contexts/kibana'; export type TableItem = Record; -interface LoadExploreDataArg { - field: string; - direction: SortDirection; - searchQuery: SavedSearchQuery; -} - export interface UseExploreDataReturnType { errorMessage: string; loadExploreData: (arg: LoadExploreDataArg) => void; @@ -55,7 +49,12 @@ export const useExploreData = ( const [sortField, setSortField] = useState(''); const [sortDirection, setSortDirection] = useState(SORT_DIRECTION.ASC); - const loadExploreData = async ({ field, direction, searchQuery }: LoadExploreDataArg) => { + const loadExploreData = async ({ + field, + direction, + searchQuery, + requiresKeyword, + }: LoadExploreDataArg) => { if (jobConfig !== undefined) { setErrorMessage(''); setStatus(INDEX_STATUS.LOADING); @@ -70,7 +69,7 @@ export const useExploreData = ( if (field !== undefined) { body.sort = [ { - [field]: { + [`${field}${requiresKeyword ? '.keyword' : ''}`]: { order: direction, }, }, diff --git a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx index 493ec864f3335..5e96b1f1bdd08 100644 --- a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx +++ b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx @@ -68,6 +68,21 @@ const PAGE_SIZE_OPTIONS = [5, 10, 25, 50]; const MlInMemoryTableBasic = mlInMemoryTableBasicFactory(); +const showingDocs = i18n.translate( + 'xpack.ml.dataframe.analytics.regressionExploration.documentsShownHelpText', + { + defaultMessage: 'Showing documents for which predictions exist', + } +); + +const showingFirstDocs = i18n.translate( + 'xpack.ml.dataframe.analytics.regressionExploration.firstDocumentsShownHelpText', + { + defaultMessage: 'Showing first {searchSize} documents for which predictions exist', + values: { searchSize: SEARCH_SIZE }, + } +); + interface Props { jobConfig: DataFrameAnalyticsConfig; jobStatus: DATA_FRAME_TASK_STATE; @@ -468,19 +483,12 @@ export const ResultsTable: FC = React.memo( )} {(columns.length > 0 || searchQuery !== defaultSearchQuery) && ( - {tableItems.length === SEARCH_SIZE && ( - - - - )} + + + + ; @@ -79,8 +85,32 @@ export const useExploreData = ( try { const resultsField = jobConfig.dest.results_field; + const searchQueryClone: ResultsSearchQuery = cloneDeep(searchQuery); + let query: ResultsSearchQuery; + + if (JSON.stringify(searchQuery) === JSON.stringify(defaultSearchQuery)) { + query = { + exists: { + field: resultsField, + }, + }; + } else if (isResultsSearchBoolQuery(searchQueryClone)) { + if (searchQueryClone.bool.must === undefined) { + searchQueryClone.bool.must = []; + } + + searchQueryClone.bool.must.push({ + exists: { + field: resultsField, + }, + }); + + query = searchQueryClone; + } else { + query = searchQueryClone; + } const body: SearchQuery = { - query: searchQuery, + query, }; if (field !== undefined) { diff --git a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx index 1f4ad65bd1879..86f1324cc0377 100644 --- a/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx +++ b/x-pack/legacy/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/columns.tsx @@ -6,12 +6,14 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { EuiBadge, EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiProgress, + EuiScreenReaderOnly, EuiText, EuiToolTip, RIGHT_ALIGNMENT, @@ -142,6 +144,16 @@ export const getColumns = ( // update possible column types to something like (FieldDataColumn | ComputedColumn | ActionsColumn)[] when they have been added to EUI const columns: any[] = [ { + name: ( + +

+ +

+
+ ), align: RIGHT_ALIGNMENT, width: '40px', isExpander: true, diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss index 2702817a55749..48aab16d85be6 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/_field_stats_card.scss @@ -17,7 +17,7 @@ border-color: $euiColorVis5; .field-type-icon-container { - background-color: rgba($euiColorVis5, 0.5); + background-color: rgba($euiColorVis5, 0.2); } } @@ -26,7 +26,7 @@ border-color: $euiColorVis7; .field-type-icon-container { - background-color: rgba($euiColorVis7, 0.5); + background-color: rgba($euiColorVis7, 0.2); } } @@ -35,7 +35,7 @@ border-color: $euiColorVis2; .field-type-icon-container { - background-color: rgba($euiColorVis2, 0.5); + background-color: rgba($euiColorVis2, 0.2); } } @@ -44,7 +44,7 @@ border-color: $euiColorVis8; .field-type-icon-container { - background-color: rgba($euiColorVis8, 0.5); + background-color: rgba($euiColorVis8, 0.2); } } @@ -53,7 +53,7 @@ border-color: $euiColorVis3; .field-type-icon-container { - background-color: rgba($euiColorVis3, 0.5); + background-color: rgba($euiColorVis3, 0.2); } } @@ -62,7 +62,7 @@ border-color: $euiColorVis0; .field-type-icon-container { - background-color: rgba($euiColorVis0, 0.5); + background-color: rgba($euiColorVis0, 0.2); } } @@ -71,7 +71,7 @@ border-color: $euiColorVis1; .field-type-icon-container { - background-color: rgba($euiColorVis1, 0.5); + background-color: rgba($euiColorVis1, 0.2); } } @@ -80,7 +80,7 @@ border-color: $euiColorVis9; .field-type-icon-container { - background-color: rgba($euiColorVis9, 0.5); + background-color: rgba($euiColorVis9, 0.2); } } @@ -90,7 +90,7 @@ border-color: $euiColorVis6; .field-type-icon-container { - background-color: rgba($euiColorVis6, 0.5); + background-color: rgba($euiColorVis6, 0.2); } } diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/fields_stats.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/fields_stats.js index 29051a45d719f..5dfae43f223b1 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/fields_stats.js +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/fields_stats/fields_stats.js @@ -32,7 +32,7 @@ export class FieldsStats extends Component {
{this.state.fields.map(f => ( - + ))} diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_field_data_card.scss b/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_field_data_card.scss index d517be0a9358d..6790b947f6f59 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_field_data_card.scss +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/components/field_data_card/_field_data_card.scss @@ -9,7 +9,7 @@ border-color: $euiColorVis5; .field-type-icon-container { - background-color: rgba($euiColorVis5, 0.5); + background-color: rgba($euiColorVis5, 0.2); } } @@ -18,7 +18,7 @@ border-color: $euiColorVis7; .field-type-icon-container { - background-color: rgba($euiColorVis7, 0.5); + background-color: rgba($euiColorVis7, 0.2); } } @@ -27,7 +27,7 @@ border-color: $euiColorVis2; .field-type-icon-container { - background-color: rgba($euiColorVis2, 0.5); + background-color: rgba($euiColorVis2, 0.2); } } @@ -36,7 +36,7 @@ border-color: $euiColorVis8; .field-type-icon-container { - background-color: rgba($euiColorVis8, 0.5); + background-color: rgba($euiColorVis8, 0.2); } } @@ -45,7 +45,7 @@ border-color: $euiColorVis3; .field-type-icon-container { - background-color: rgba($euiColorVis3, 0.5); + background-color: rgba($euiColorVis3, 0.2); } } @@ -54,7 +54,7 @@ border-color: $euiColorVis0; .field-type-icon-container { - background-color: rgba($euiColorVis0, 0.5); + background-color: rgba($euiColorVis0, 0.2); } } @@ -63,7 +63,7 @@ border-color: $euiColorVis1; .field-type-icon-container { - background-color: rgba($euiColorVis1, 0.5); + background-color: rgba($euiColorVis1, 0.2); } } @@ -72,7 +72,7 @@ border-color: $euiColorVis9; .field-type-icon-container { - background-color: rgba($euiColorVis9, 0.5); + background-color: rgba($euiColorVis9, 0.2); } } @@ -82,7 +82,7 @@ border-color: $euiColorVis6; .field-type-icon-container { - background-color: rgba($euiColorVis6, 0.5); + background-color: rgba($euiColorVis6, 0.2); } } diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js b/x-pack/legacy/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js index 818f1b79b3143..7a6a1c22e39c5 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js +++ b/x-pack/legacy/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js @@ -16,8 +16,8 @@ import { JobDescription } from './job_description'; import { JobIcon } from '../../../../components/job_message_icon'; import { getJobIdUrl } from '../utils'; -import { EuiBadge, EuiBasicTable, EuiButtonIcon, EuiLink } from '@elastic/eui'; -import { injectI18n } from '@kbn/i18n/react'; +import { EuiBadge, EuiBasicTable, EuiButtonIcon, EuiLink, EuiScreenReaderOnly } from '@elastic/eui'; +import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; const PAGE_SIZE = 10; @@ -124,7 +124,16 @@ class JobsListUI extends Component { // be updated if we move to always using props for width. const columns = [ { - name: '', + name: ( + +

+ +

+
+ ), render: item => ( this.toggleRow(item)} @@ -167,7 +176,16 @@ class JobsListUI extends Component { }, { field: 'auditMessage', - name: '', + name: ( + +

+ +

+
+ ), render: item => , }, { @@ -272,7 +290,16 @@ class JobsListUI extends Component { width: '15%', }); columns.push({ - name: '', + name: ( + +

+ +

+
+ ), actions: actionsMenuContent( this.props.showEditJobFlyout, this.props.showDeleteJobModal, diff --git a/x-pack/legacy/plugins/ml/public/application/util/chart_utils.js b/x-pack/legacy/plugins/ml/public/application/util/chart_utils.js index d54b1a9c9ac22..dfa896b3124c6 100644 --- a/x-pack/legacy/plugins/ml/public/application/util/chart_utils.js +++ b/x-pack/legacy/plugins/ml/public/application/util/chart_utils.js @@ -107,7 +107,7 @@ export function drawLineChartDots(data, lineChartGroup, lineChartValuesLine, rad } // this replicates Kibana's filterAxisLabels() behavior -// which can be found in ui/vislib/lib/axis/axis_labels.js +// which can be found in src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/axis/axis_labels.js // axis labels which overflow the chart's boundaries will be removed export function filterAxisLabels(selection, chartWidth) { if (selection === undefined || selection.selectAll === undefined) { diff --git a/x-pack/legacy/plugins/ml/public/application/util/url_state.test.ts b/x-pack/legacy/plugins/ml/public/application/util/url_state.test.ts new file mode 100644 index 0000000000000..91bbef2dba6c2 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/application/util/url_state.test.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { renderHook, act } from '@testing-library/react-hooks'; +import { getUrlState, useUrlState } from './url_state'; + +const mockHistoryPush = jest.fn(); + +jest.mock('react-router-dom', () => ({ + useHistory: () => ({ + push: mockHistoryPush, + }), + useLocation: () => ({ + search: + "?_a=(mlExplorerFilter:(),mlExplorerSwimlane:(viewByFieldName:action),query:(query_string:(analyze_wildcard:!t,query:'*')))&_g=(ml:(jobIds:!(dec-2)),refreshInterval:(display:Off,pause:!f,value:0),time:(from:'2019-01-01T00:03:40.000Z',mode:absolute,to:'2019-08-30T11:55:07.000Z'))&savedSearchId=571aaf70-4c88-11e8-b3d7-01146121b73d", + }), +})); + +describe('getUrlState', () => { + test('properly decode url with _g and _a', () => { + expect( + getUrlState( + "?_a=(mlExplorerFilter:(),mlExplorerSwimlane:(viewByFieldName:action),query:(query_string:(analyze_wildcard:!t,query:'*')))&_g=(ml:(jobIds:!(dec-2)),refreshInterval:(display:Off,pause:!f,value:0),time:(from:'2019-01-01T00:03:40.000Z',mode:absolute,to:'2019-08-30T11:55:07.000Z'))&savedSearchId=571aaf70-4c88-11e8-b3d7-01146121b73d" + ) + ).toEqual({ + _a: { + mlExplorerFilter: {}, + mlExplorerSwimlane: { + viewByFieldName: 'action', + }, + query: { + query_string: { + analyze_wildcard: true, + query: '*', + }, + }, + }, + _g: { + ml: { + jobIds: ['dec-2'], + }, + refreshInterval: { + display: 'Off', + pause: false, + value: 0, + }, + time: { + from: '2019-01-01T00:03:40.000Z', + mode: 'absolute', + to: '2019-08-30T11:55:07.000Z', + }, + }, + savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d', + }); + }); +}); + +describe('useUrlState', () => { + beforeEach(() => { + mockHistoryPush.mockClear(); + }); + + test('pushes a properly encoded search string to history', () => { + const { result } = renderHook(() => useUrlState('_a')); + + act(() => { + const [, setUrlState] = result.current; + setUrlState({ + query: {}, + }); + }); + + expect(mockHistoryPush).toHaveBeenCalledWith({ + search: + '_a=%28mlExplorerFilter%3A%28%29%2CmlExplorerSwimlane%3A%28viewByFieldName%3Aaction%29%2Cquery%3A%28%29%29&_g=%28ml%3A%28jobIds%3A%21%28dec-2%29%29%2CrefreshInterval%3A%28display%3AOff%2Cpause%3A%21f%2Cvalue%3A0%29%2Ctime%3A%28from%3A%272019-01-01T00%3A03%3A40.000Z%27%2Cmode%3Aabsolute%2Cto%3A%272019-08-30T11%3A55%3A07.000Z%27%29%29&savedSearchId=%27571aaf70-4c88-11e8-b3d7-01146121b73d%27', + }); + }); +}); diff --git a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts index 4402155815a5b..546944b1a33bf 100644 --- a/x-pack/legacy/plugins/ml/public/application/util/url_state.ts +++ b/x-pack/legacy/plugins/ml/public/application/util/url_state.ts @@ -18,13 +18,18 @@ import { getNestedProperty } from './object_utils'; export type SetUrlState = (attribute: string | Dictionary, value?: any) => void; export type UrlState = [Dictionary, SetUrlState]; -function getUrlState(search: string) { +const decodedParams = new Set(['_a', '_g']); +export function getUrlState(search: string): Dictionary { const urlState: Dictionary = {}; const parsedQueryString = queryString.parse(search); try { Object.keys(parsedQueryString).forEach(a => { - urlState[a] = decode(parsedQueryString[a]) as Dictionary; + if (decodedParams.has(a)) { + urlState[a] = decode(parsedQueryString[a]) as Dictionary; + } else { + urlState[a] = parsedQueryString[a]; + } }); } catch (error) { // eslint-disable-next-line no-console diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts index fef45f5a5eae8..ae603d93245a3 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts @@ -86,7 +86,6 @@ export const executeJobFactory: ExecuteJobFactory { - // @ts-ignore fieldFormatServiceFactory' does not exist on type 'ServerFacade TODO const fieldFormats = await server.fieldFormatServiceFactory(uiConfig); return fieldFormatMapFactory(indexPatternSavedObject, fieldFormats); })(), diff --git a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts index 7e12adefca38d..d39d2bbf08c9f 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts @@ -57,7 +57,7 @@ export async function generateCsvSearch( jobParams: JobParamsDiscoverCsv ): Promise { const { savedObjects, uiSettingsServiceFactory } = server; - const savedObjectsClient = savedObjects.getScopedSavedObjectsClient(req); + const savedObjectsClient = savedObjects.getScopedSavedObjectsClient(req.getRawRequest()); const { indexPatternSavedObjectId, timerange } = searchPanel; const savedSearchObjectAttr = searchPanel.attributes as SavedSearchObjectAttributes; const { indexPatternSavedObject } = await getDataSource( diff --git a/x-pack/legacy/plugins/reporting/index.ts b/x-pack/legacy/plugins/reporting/index.ts index ef0ab37738362..82d94422b70ce 100644 --- a/x-pack/legacy/plugins/reporting/index.ts +++ b/x-pack/legacy/plugins/reporting/index.ts @@ -6,6 +6,9 @@ import { resolve } from 'path'; import { i18n } from '@kbn/i18n'; +import { Legacy } from 'kibana'; +import { IUiSettingsClient } from 'src/core/server'; +import { XPackMainPlugin } from '../xpack_main/server/xpack_main'; import { PLUGIN_ID, UI_SETTINGS_CUSTOM_PDF_LOGO } from './common/constants'; // @ts-ignore untyped module defintition import { mirrorPluginStatus } from '../../server/lib/mirror_plugin_status'; @@ -16,16 +19,35 @@ import { getExportTypesRegistry, runValidations, } from './server/lib'; -import { config as reportingConfig } from './config'; -import { logConfiguration } from './log_configuration'; import { createBrowserDriverFactory } from './server/browsers'; import { registerReportingUsageCollector } from './server/usage'; -import { ReportingConfigOptions, ReportingPluginSpecOptions, ServerFacade } from './types.d'; +import { ReportingConfigOptions, ReportingPluginSpecOptions } from './types.d'; +import { config as reportingConfig } from './config'; +import { logConfiguration } from './log_configuration'; const kbToBase64Length = (kb: number) => { return Math.floor((kb * 1024 * 8) / 6); }; +type LegacyPlugins = Legacy.Server['plugins']; + +export interface ServerFacade { + config: Legacy.Server['config']; + info: Legacy.Server['info']; + log: Legacy.Server['log']; + plugins: { + elasticsearch: LegacyPlugins['elasticsearch']; + security: LegacyPlugins['security']; + xpack_main: XPackMainPlugin & { + status?: any; + }; + }; + route: Legacy.Server['route']; + savedObjects: Legacy.Server['savedObjects']; + uiSettingsServiceFactory: Legacy.Server['uiSettingsServiceFactory']; + fieldFormatServiceFactory: (uiConfig: IUiSettingsClient) => unknown; +} + export const reporting = (kibana: any) => { return new kibana.Plugin({ id: PLUGIN_ID, @@ -42,7 +64,7 @@ export const reporting = (kibana: any) => { embeddableActions: ['plugins/reporting/panel_actions/get_csv_panel_action'], home: ['plugins/reporting/register_feature'], managementSections: ['plugins/reporting/views/management'], - injectDefaultVars(server: ServerFacade, options?: ReportingConfigOptions) { + injectDefaultVars(server: Legacy.Server, options?: ReportingConfigOptions) { const config = server.config(); return { reportingPollConfig: options ? options.poll : {}, @@ -70,9 +92,22 @@ export const reporting = (kibana: any) => { }, }, - // TODO: Decouple Hapi: Build a server facade object based on the server to - // pass through to the libs. Do not pass server directly - async init(server: ServerFacade) { + async init(server: Legacy.Server) { + const serverFacade: ServerFacade = { + config: server.config, + info: server.info, + route: server.route.bind(server), + plugins: { + elasticsearch: server.plugins.elasticsearch, + xpack_main: server.plugins.xpack_main, + security: server.plugins.security, + }, + savedObjects: server.savedObjects, + uiSettingsServiceFactory: server.uiSettingsServiceFactory, + // @ts-ignore Property 'fieldFormatServiceFactory' does not exist on type 'Server'. + fieldFormatServiceFactory: server.fieldFormatServiceFactory, + log: server.log.bind(server), + }; const exportTypesRegistry = getExportTypesRegistry(); let isCollectorReady = false; @@ -80,18 +115,18 @@ export const reporting = (kibana: any) => { const { usageCollection } = server.newPlatform.setup.plugins; registerReportingUsageCollector( usageCollection, - server, + serverFacade, () => isCollectorReady, exportTypesRegistry ); - const logger = LevelLogger.createForServer(server, [PLUGIN_ID]); - const browserDriverFactory = await createBrowserDriverFactory(server); + const logger = LevelLogger.createForServer(serverFacade, [PLUGIN_ID]); + const browserDriverFactory = await createBrowserDriverFactory(serverFacade); - logConfiguration(server, logger); - runValidations(server, logger, browserDriverFactory); + logConfiguration(serverFacade, logger); + runValidations(serverFacade, logger, browserDriverFactory); - const { xpack_main: xpackMainPlugin } = server.plugins; + const { xpack_main: xpackMainPlugin } = serverFacade.plugins; mirrorPluginStatus(xpackMainPlugin, this); const checkLicense = checkLicenseFactory(exportTypesRegistry); (xpackMainPlugin as any).status.once('green', () => { @@ -104,7 +139,7 @@ export const reporting = (kibana: any) => { isCollectorReady = true; // Reporting routes - registerRoutes(server, exportTypesRegistry, browserDriverFactory, logger); + registerRoutes(serverFacade, exportTypesRegistry, browserDriverFactory, logger); }, deprecations({ unused }: any) { diff --git a/x-pack/legacy/plugins/reporting/server/lib/get_user.js b/x-pack/legacy/plugins/reporting/server/lib/get_user.ts similarity index 57% rename from x-pack/legacy/plugins/reporting/server/lib/get_user.js rename to x-pack/legacy/plugins/reporting/server/lib/get_user.ts index 04c9516cb99d4..e2921de795012 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/get_user.js +++ b/x-pack/legacy/plugins/reporting/server/lib/get_user.ts @@ -4,8 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -export function getUserFactory(server) { - return async request => { +import { Legacy } from 'kibana'; +import { ServerFacade } from '../../types'; + +export function getUserFactory(server: ServerFacade) { + /* + * Legacy.Request because this is called from routing middleware + */ + return async (request: Legacy.Request) => { if (!server.plugins.security) { return null; } @@ -13,7 +19,7 @@ export function getUserFactory(server) { try { return await server.plugins.security.getUser(request); } catch (err) { - server.log(['reporting', 'getUser', 'debug'], err); + server.log(['reporting', 'getUser', 'error'], err); return null; } }; diff --git a/x-pack/legacy/plugins/reporting/server/lib/level_logger.ts b/x-pack/legacy/plugins/reporting/server/lib/level_logger.ts index c67a9cd32d50d..839fa16a716b7 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/level_logger.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/level_logger.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -type ServerLog = (tags: string[], msg: string) => void; +import { ServerFacade } from '../../types'; const trimStr = (toTrim: string) => { return typeof toTrim === 'string' ? toTrim.trim() : toTrim; @@ -16,12 +16,12 @@ export class LevelLogger { public warn: (msg: string, tags?: string[]) => void; - static createForServer(server: any, tags: string[]) { - const serverLog: ServerLog = (tgs: string[], msg: string) => server.log(tgs, msg); + static createForServer(server: ServerFacade, tags: string[]) { + const serverLog: ServerFacade['log'] = (tgs: string[], msg: string) => server.log(tgs, msg); return new LevelLogger(serverLog, tags); } - constructor(logger: ServerLog, tags: string[]) { + constructor(logger: ServerFacade['log'], tags: string[]) { this._logger = logger; this._tags = tags; diff --git a/x-pack/legacy/plugins/reporting/server/lib/once_per_server.ts b/x-pack/legacy/plugins/reporting/server/lib/once_per_server.ts index d73a5b73fecd0..ae3636079a9bb 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/once_per_server.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/once_per_server.ts @@ -27,10 +27,6 @@ export function oncePerServer(fn: ServerFn) { throw new TypeError('This function expects to be called with a single argument'); } - if (!server || typeof server.expose !== 'function') { - throw new TypeError('This function expects to be passed the server'); - } - // @ts-ignore return fn.call(this, server); }); diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts index 128cc44db4dc4..c9225dfee6978 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts @@ -4,16 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Legacy } from 'kibana'; import boom from 'boom'; import Joi from 'joi'; import rison from 'rison-node'; import { API_BASE_URL } from '../../common/constants'; -import { ServerFacade, RequestFacade, ReportingResponseToolkit } from '../../types'; +import { ServerFacade, ReportingResponseToolkit } from '../../types'; import { getRouteConfigFactoryReportingPre, GetRouteConfigFactoryFn, RouteConfigFactory, } from './lib/route_config_factories'; +import { makeRequestFacade } from './lib/make_request_facade'; import { HandlerErrorFunction, HandlerFunction } from './types'; const BASE_GENERATE = `${API_BASE_URL}/generate`; @@ -54,7 +56,8 @@ export function registerGenerateFromJobParams( path: `${BASE_GENERATE}/{exportType}`, method: 'POST', options: getRouteConfig(), - handler: async (request: RequestFacade, h: ReportingResponseToolkit) => { + handler: async (legacyRequest: Legacy.Request, h: ReportingResponseToolkit) => { + const request = makeRequestFacade(legacyRequest); let jobParamsRison: string | null; if (request.payload) { @@ -80,7 +83,7 @@ export function registerGenerateFromJobParams( if (!jobParams) { throw new Error('missing jobParams!'); } - response = await handler(exportType, jobParams, request, h); + response = await handler(exportType, jobParams, legacyRequest, h); } catch (err) { throw boom.badRequest(`invalid rison: ${jobParamsRison}`); } diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts index 72c8055614065..2c509136b1b44 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Legacy } from 'kibana'; import { get } from 'lodash'; import { API_BASE_GENERATE_V1, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../common/constants'; -import { ServerFacade, RequestFacade, ReportingResponseToolkit } from '../../types'; +import { ServerFacade, ReportingResponseToolkit } from '../../types'; import { HandlerErrorFunction, HandlerFunction, QueuedJobPayload } from './types'; import { getRouteOptionsCsv } from './lib/route_config_factories'; +import { makeRequestFacade } from './lib/make_request_facade'; import { getJobParamsFromRequest } from '../../export_types/csv_from_savedobject/server/lib/get_job_params_from_request'; /* @@ -31,17 +33,18 @@ export function registerGenerateCsvFromSavedObject( path: `${API_BASE_GENERATE_V1}/csv/saved-object/{savedObjectType}:{savedObjectId}`, method: 'POST', options: routeOptions, - handler: async (request: RequestFacade, h: ReportingResponseToolkit) => { + handler: async (legacyRequest: Legacy.Request, h: ReportingResponseToolkit) => { + const requestFacade = makeRequestFacade(legacyRequest); + /* * 1. Build `jobParams` object: job data that execution will need to reference in various parts of the lifecycle * 2. Pass the jobParams and other common params to `handleRoute`, a shared function to enqueue the job with the params * 3. Ensure that details for a queued job were returned */ - let result: QueuedJobPayload; try { - const jobParams = getJobParamsFromRequest(request, { isImmediate: false }); - result = await handleRoute(CSV_FROM_SAVEDOBJECT_JOB_TYPE, jobParams, request, h); + const jobParams = getJobParamsFromRequest(requestFacade, { isImmediate: false }); + result = await handleRoute(CSV_FROM_SAVEDOBJECT_JOB_TYPE, jobParams, legacyRequest, h); // pass the original request because the handler will make the request facade on its own } catch (err) { throw handleRouteError(CSV_FROM_SAVEDOBJECT_JOB_TYPE, err); } diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts index bc96c27f64c10..8d1c84664cbe9 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Legacy } from 'kibana'; import { API_BASE_GENERATE_V1 } from '../../common/constants'; import { createJobFactory, executeJobFactory } from '../../export_types/csv_from_savedobject'; import { ServerFacade, - RequestFacade, ResponseFacade, HeadlessChromiumDriverFactory, ReportingResponseToolkit, @@ -16,8 +16,9 @@ import { JobDocOutputExecuted, } from '../../types'; import { JobDocPayloadPanelCsv } from '../../export_types/csv_from_savedobject/types'; -import { getRouteOptionsCsv } from './lib/route_config_factories'; import { getJobParamsFromRequest } from '../../export_types/csv_from_savedobject/server/lib/get_job_params_from_request'; +import { getRouteOptionsCsv } from './lib/route_config_factories'; +import { makeRequestFacade } from './lib/make_request_facade'; /* * This function registers API Endpoints for immediate Reporting jobs. The API inputs are: @@ -43,7 +44,8 @@ export function registerGenerateCsvFromSavedObjectImmediate( path: `${API_BASE_GENERATE_V1}/immediate/csv/saved-object/{savedObjectType}:{savedObjectId}`, method: 'POST', options: routeOptions, - handler: async (request: RequestFacade, h: ReportingResponseToolkit) => { + handler: async (legacyRequest: Legacy.Request, h: ReportingResponseToolkit) => { + const request = makeRequestFacade(legacyRequest); const logger = parentLogger.clone(['savedobject-csv']); const jobParams = getJobParamsFromRequest(request, { isImmediate: true }); diff --git a/x-pack/legacy/plugins/reporting/server/routes/generation.ts b/x-pack/legacy/plugins/reporting/server/routes/generation.ts index 73450b7641c8e..21af54ddf11e3 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generation.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generation.ts @@ -5,12 +5,12 @@ */ import boom from 'boom'; +import { Legacy } from 'kibana'; import { API_BASE_URL } from '../../common/constants'; import { ServerFacade, ExportTypesRegistry, HeadlessChromiumDriverFactory, - RequestFacade, ReportingResponseToolkit, Logger, } from '../../types'; @@ -18,6 +18,7 @@ import { registerGenerateFromJobParams } from './generate_from_jobparams'; import { registerGenerateCsvFromSavedObject } from './generate_from_savedobject'; import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate'; import { createQueueFactory, enqueueJobFactory } from '../lib'; +import { makeRequestFacade } from './lib/make_request_facade'; export function registerJobGenerationRoutes( server: ServerFacade, @@ -39,9 +40,10 @@ export function registerJobGenerationRoutes( async function handler( exportTypeId: string, jobParams: object, - request: RequestFacade, + legacyRequest: Legacy.Request, h: ReportingResponseToolkit ) { + const request = makeRequestFacade(legacyRequest); const user = request.pre.user; const headers = request.headers; diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts index fd5014911d262..a0be15d60f316 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts @@ -4,16 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Legacy } from 'kibana'; import boom from 'boom'; import { API_BASE_URL } from '../../common/constants'; import { ServerFacade, ExportTypesRegistry, Logger, - RequestFacade, ReportingResponseToolkit, JobDocOutput, JobSource, + ListQuery, } from '../../types'; // @ts-ignore import { jobsQueryFactory } from '../lib/jobs_query'; @@ -23,6 +24,7 @@ import { getRouteConfigFactoryDownloadPre, getRouteConfigFactoryManagementPre, } from './lib/route_config_factories'; +import { makeRequestFacade } from './lib/make_request_facade'; const MAIN_ENTRY = `${API_BASE_URL}/jobs`; @@ -40,8 +42,9 @@ export function registerJobInfoRoutes( path: `${MAIN_ENTRY}/list`, method: 'GET', options: getRouteConfig(), - handler: (request: RequestFacade) => { - const { page: queryPage, size: querySize, ids: queryIds } = request.query; + handler: (legacyRequest: Legacy.Request) => { + const request = makeRequestFacade(legacyRequest); + const { page: queryPage, size: querySize, ids: queryIds } = request.query as ListQuery; const page = parseInt(queryPage, 10) || 0; const size = Math.min(100, parseInt(querySize, 10) || 10); const jobIds = queryIds ? queryIds.split(',') : null; @@ -62,7 +65,8 @@ export function registerJobInfoRoutes( path: `${MAIN_ENTRY}/count`, method: 'GET', options: getRouteConfig(), - handler: (request: RequestFacade) => { + handler: (legacyRequest: Legacy.Request) => { + const request = makeRequestFacade(legacyRequest); const results = jobsQuery.count(request.pre.management.jobTypes, request.pre.user); return results; }, @@ -73,7 +77,8 @@ export function registerJobInfoRoutes( path: `${MAIN_ENTRY}/output/{docId}`, method: 'GET', options: getRouteConfig(), - handler: (request: RequestFacade) => { + handler: (legacyRequest: Legacy.Request) => { + const request = makeRequestFacade(legacyRequest); const { docId } = request.params; return jobsQuery.get(request.pre.user, docId, { includeContent: true }).then( @@ -98,7 +103,8 @@ export function registerJobInfoRoutes( path: `${MAIN_ENTRY}/info/{docId}`, method: 'GET', options: getRouteConfig(), - handler: (request: RequestFacade) => { + handler: (legacyRequest: Legacy.Request) => { + const request = makeRequestFacade(legacyRequest); const { docId } = request.params; return jobsQuery @@ -130,7 +136,8 @@ export function registerJobInfoRoutes( path: `${MAIN_ENTRY}/download/{docId}`, method: 'GET', options: getRouteConfigDownload(), - handler: async (request: RequestFacade, h: ReportingResponseToolkit) => { + handler: async (legacyRequest: Legacy.Request, h: ReportingResponseToolkit) => { + const request = makeRequestFacade(legacyRequest); const { docId } = request.params; let response = await jobResponseHandler( diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/make_request_facade.test.ts b/x-pack/legacy/plugins/reporting/server/routes/lib/make_request_facade.test.ts new file mode 100644 index 0000000000000..8cdb7b4c018d7 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/make_request_facade.test.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Legacy } from 'kibana'; +import { makeRequestFacade } from './make_request_facade'; + +describe('makeRequestFacade', () => { + test('creates a default object', () => { + const legacyRequest = ({ + getBasePath: () => 'basebase', + params: { + param1: 123, + }, + payload: { + payload1: 123, + }, + headers: { + user: 123, + }, + } as unknown) as Legacy.Request; + + expect(makeRequestFacade(legacyRequest)).toMatchInlineSnapshot(` + Object { + "getBasePath": [Function], + "getRawRequest": [Function], + "getSavedObjectsClient": undefined, + "headers": Object { + "user": 123, + }, + "params": Object { + "param1": 123, + }, + "payload": Object { + "payload1": 123, + }, + "pre": undefined, + "query": undefined, + "route": undefined, + } + `); + }); + + test('getRawRequest', () => { + const legacyRequest = ({ + getBasePath: () => 'basebase', + params: { + param1: 123, + }, + payload: { + payload1: 123, + }, + headers: { + user: 123, + }, + } as unknown) as Legacy.Request; + + expect(makeRequestFacade(legacyRequest).getRawRequest()).toBe(legacyRequest); + }); +}); diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/make_request_facade.ts b/x-pack/legacy/plugins/reporting/server/routes/lib/make_request_facade.ts new file mode 100644 index 0000000000000..fb8a2dbbff17b --- /dev/null +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/make_request_facade.ts @@ -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 { RequestQuery } from 'hapi'; +import { Legacy } from 'kibana'; +import { + RequestFacade, + ReportingRequestPayload, + ReportingRequestPre, + ReportingRequestQuery, +} from '../../../types'; + +export function makeRequestFacade(request: Legacy.Request): RequestFacade { + // This condition is for unit tests + const getSavedObjectsClient = request.getSavedObjectsClient + ? request.getSavedObjectsClient.bind(request) + : request.getSavedObjectsClient; + return { + getSavedObjectsClient, + headers: request.headers, + params: request.params, + payload: (request.payload as object) as ReportingRequestPayload, + query: ((request.query as RequestQuery) as object) as ReportingRequestQuery, + pre: (request.pre as Record) as ReportingRequestPre, + getBasePath: request.getBasePath, + route: request.route, + getRawRequest: () => request, + }; +} diff --git a/x-pack/legacy/plugins/reporting/server/routes/types.d.ts b/x-pack/legacy/plugins/reporting/server/routes/types.d.ts index b50d443ec00b9..f3660a22cbac1 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/types.d.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/types.d.ts @@ -4,12 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Legacy } from 'kibana'; import { RequestFacade, ReportingResponseToolkit, JobDocPayload } from '../../types'; export type HandlerFunction = ( exportType: string, jobParams: object, - request: RequestFacade, + request: Legacy.Request, h: ReportingResponseToolkit ) => any; diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index 5adabd785679c..f67f44860f14a 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -7,7 +7,6 @@ import { ResponseObject } from 'hapi'; import { EventEmitter } from 'events'; import { Legacy } from 'kibana'; -import { XPackMainPlugin } from '../xpack_main/server/xpack_main'; import { ElasticsearchPlugin, CallCluster, @@ -16,6 +15,7 @@ import { CancellationToken } from './common/cancellation_token'; import { LevelLogger } from './server/lib/level_logger'; import { HeadlessChromiumDriverFactory } from './server/browsers/chromium/driver_factory'; import { BrowserType } from './server/browsers/types'; +import { ServerFacade } from './index'; export type ReportingPlugin = object; // For Plugin contract @@ -53,7 +53,7 @@ export interface NetworkPolicy { rules: NetworkPolicyRule[]; } -interface ListQuery { +export interface ListQuery { page: string; size: string; ids?: string; // optional field forbids us from extending RequestQuery @@ -64,9 +64,6 @@ interface GenerateQuery { interface GenerateExportTypePayload { jobParams: string; } -interface DownloadParams { - docId: string; -} /* * Legacy System @@ -74,26 +71,6 @@ interface DownloadParams { export type ReportingPluginSpecOptions = Legacy.PluginSpecOptions; -export type ServerFacade = Legacy.Server & { - plugins: { - xpack_main?: XPackMainPlugin & { - status?: any; - }; - }; -}; - -interface ReportingRequest { - query: ListQuery & GenerateQuery; - params: DownloadParams; - payload: GenerateExportTypePayload; - pre: { - management: { - jobTypes: any; - }; - user: any; - }; -} - export type EnqueueJobFn = ( parentLogger: LevelLogger, exportTypeId: string, @@ -103,7 +80,27 @@ export type EnqueueJobFn = ( request: RequestFacade ) => Promise; -export type RequestFacade = ReportingRequest & Legacy.Request; +export type ReportingRequestPayload = GenerateExportTypePayload | JobParamPostPayload; +export type ReportingRequestQuery = ListQuery | GenerateQuery; + +export interface ReportingRequestPre { + management: { + jobTypes: any; + }; + user: any; // TODO import AuthenticatedUser from security/server +} + +export interface RequestFacade { + getBasePath: Legacy.Request['getBasePath']; + getSavedObjectsClient: Legacy.Request['getSavedObjectsClient']; + headers: Legacy.Request['headers']; + params: Legacy.Request['params']; + payload: JobParamPostPayload | GenerateExportTypePayload; + query: ReportingRequestQuery; + route: Legacy.Request['route']; + pre: ReportingRequestPre; + getRawRequest: () => Legacy.Request; +} export type ResponseFacade = ResponseObject & { isBoom: boolean; @@ -356,3 +353,5 @@ export interface InterceptedRequest { frameId: string; resourceType: string; } + +export { ServerFacade }; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/helpers.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/helpers.ts new file mode 100644 index 0000000000000..c431d0551b29a --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/helpers.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 { DEFAULT_TIMEOUT } from '../util/helpers'; + +import { INSPECT_BUTTON_ICON, InspectButtonMetadata } from './selectors'; + +export const openStatsAndTables = (table: InspectButtonMetadata) => { + if (table.tabId) { + cy.get(table.tabId).click({ force: true }); + } + cy.get(table.id, { timeout: DEFAULT_TIMEOUT }); + if (table.altInspectId) { + cy.get(table.altInspectId, { timeout: DEFAULT_TIMEOUT }).trigger('click', { + force: true, + }); + } else { + cy.get(`${table.id} ${INSPECT_BUTTON_ICON}`, { + timeout: DEFAULT_TIMEOUT, + }).trigger('click', { force: true }); + } +}; + +export const closesModal = () => { + cy.get('[data-test-subj="modal-inspect-close"]', { timeout: DEFAULT_TIMEOUT }).click(); +}; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/selectors.ts index 689c98de9598a..a6d4b37be9f00 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/selectors.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/inspect/selectors.ts @@ -4,95 +4,86 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HOSTS_PAGE, HOSTS_PAGE_TAB_URLS, NETWORK_PAGE, NETWORK_TAB_URLS } from '../urls'; - export const INSPECT_BUTTON_ICON = '[data-test-subj="inspect-icon-button"]'; export const INSPECT_MODAL = '[data-test-subj="modal-inspect-euiModal"]'; export const TIMELINE_SETTINGS_ICON = '[data-test-subj="settings-gear"]'; export const TIMELINE_INSPECT_BUTTON = '[data-test-subj="inspect-empty-button"]'; -interface InspectButtonMetadata { +export interface InspectButtonMetadata { altInspectId?: string; id: string; title: string; - url: string; + tabId?: string; } -export const INSPECT_BUTTONS_IN_SIEM: InspectButtonMetadata[] = [ +export const INSPECT_HOSTS_BUTTONS_IN_SIEM: InspectButtonMetadata[] = [ { id: '[data-test-subj="stat-hosts"]', title: 'Hosts Stat', - url: HOSTS_PAGE, }, { id: '[data-test-subj="stat-authentication"]', title: 'User Authentications Stat', - url: HOSTS_PAGE, }, { id: '[data-test-subj="stat-uniqueIps"]', title: 'Unique IPs Stat', - url: HOSTS_PAGE, }, + { + id: '[data-test-subj="table-allHosts-loading-false"]', + title: 'All Hosts Table', + tabId: '[data-test-subj="navigation-allHosts"]', + }, + { + id: '[data-test-subj="table-authentications-loading-false"]', + title: 'Authentications Table', + tabId: '[data-test-subj="navigation-authentications"]', + }, + { + id: '[data-test-subj="table-uncommonProcesses-loading-false"]', + title: 'Uncommon processes Table', + tabId: '[data-test-subj="navigation-uncommonProcesses"]', + }, + { + altInspectId: `[data-test-subj="events-viewer-panel"] ${INSPECT_BUTTON_ICON}`, + id: '[data-test-subj="events-container-loading-false"]', + title: 'Events Table', + tabId: '[data-test-subj="navigation-events"]', + }, +]; + +export const INSPECT_NETWORK_BUTTONS_IN_SIEM: InspectButtonMetadata[] = [ { id: '[data-test-subj="stat-networkEvents"]', title: 'Network events Stat', - url: NETWORK_PAGE, }, { id: '[data-test-subj="stat-dnsQueries"]', title: 'DNS queries Stat', - url: NETWORK_PAGE, }, { id: '[data-test-subj="stat-uniqueFlowId"]', title: 'Unique flow IDs Stat', - url: NETWORK_PAGE, }, { id: '[data-test-subj="stat-tlsHandshakes"]', title: 'TLS handshakes Stat', - url: NETWORK_PAGE, }, { id: '[data-test-subj="stat-UniqueIps"]', title: 'Unique private IPs Stat', - url: NETWORK_PAGE, }, { id: '[data-test-subj="table-topNFlowSource-loading-false"]', title: 'Source IPs Table', - url: NETWORK_PAGE, }, { id: '[data-test-subj="table-topNFlowDestination-loading-false"]', title: 'Destination IPs Table', - url: NETWORK_PAGE, }, { id: '[data-test-subj="table-dns-loading-false"]', title: 'Top DNS Domains Table', - url: NETWORK_TAB_URLS.dns, - }, - { - id: '[data-test-subj="table-allHosts-loading-false"]', - title: 'All Hosts Table', - url: HOSTS_PAGE_TAB_URLS.allHosts, - }, - { - id: '[data-test-subj="table-authentications-loading-false"]', - title: 'Authentications Table', - url: HOSTS_PAGE_TAB_URLS.authentications, - }, - { - id: '[data-test-subj="table-uncommonProcesses-loading-false"]', - title: 'Uncommon processes Table', - url: HOSTS_PAGE_TAB_URLS.uncommonProcesses, - }, - { - altInspectId: `[data-test-subj="events-viewer-panel"] ${INSPECT_BUTTON_ICON}`, - id: '[data-test-subj="events-container-loading-false"]', - title: 'Events Table', - url: HOSTS_PAGE_TAB_URLS.events, + tabId: '[data-test-subj="navigation-dns"]', }, ]; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/helpers.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/helpers.ts index 8fa1a03840e3b..ef2c19bd7e737 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/helpers.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/helpers.ts @@ -7,9 +7,12 @@ import { drag, drop } from '../drag_n_drop/helpers'; import { ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS } from '../hosts/selectors'; import { + CLOSE_TIMELINE_BTN, + CREATE_NEW_TIMELINE, SEARCH_OR_FILTER_CONTAINER, SERVER_SIDE_EVENT_COUNT, TIMELINE_DATA_PROVIDERS, + TIMELINE_SETTINGS, TIMELINE_TOGGLE_BUTTON, TOGGLE_TIMELINE_EXPAND_EVENT, } from './selectors'; @@ -19,12 +22,17 @@ import { DEFAULT_TIMEOUT } from '../util/helpers'; export const toggleTimelineVisibility = () => cy.get(TIMELINE_TOGGLE_BUTTON, { timeout: DEFAULT_TIMEOUT }).click(); +export const createNewTimeline = () => { + cy.get(TIMELINE_SETTINGS).click(); + cy.get(CREATE_NEW_TIMELINE).click(); + cy.get(CLOSE_TIMELINE_BTN).click({ force: true }); +}; + /** Drags and drops a host from the `All Hosts` widget on the `Hosts` page to the timeline */ export const dragFromAllHostsToTimeline = () => { cy.get(ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS) .first() .then(host => drag(host)); - cy.get(TIMELINE_DATA_PROVIDERS).then(dataProvidersDropArea => drop(dataProvidersDropArea)); }; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/selectors.ts index 7dc98072b52f8..0ec0c506cbb1a 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/selectors.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/timeline/selectors.ts @@ -36,3 +36,9 @@ export const TOGGLE_TIMELINE_EXPAND_EVENT = '[data-test-subj="expand-event"]'; /** The body of the timeline flyout */ export const TIMELINE_FLYOUT_BODY = '[data-test-subj="eui-flyout-body"]'; + +export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]'; + +export const TIMELINE_SETTINGS = '[data-test-subj="settings-gear"]'; + +export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts index ee25705a83989..7b0b10831c4a0 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts @@ -4,34 +4,52 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HOSTS_PAGE } from '../../lib/urls'; +import { HOSTS_PAGE, NETWORK_PAGE } from '../../lib/urls'; import { - INSPECT_BUTTON_ICON, INSPECT_MODAL, - INSPECT_BUTTONS_IN_SIEM, + INSPECT_NETWORK_BUTTONS_IN_SIEM, + INSPECT_HOSTS_BUTTONS_IN_SIEM, TIMELINE_SETTINGS_ICON, TIMELINE_INSPECT_BUTTON, } from '../../lib/inspect/selectors'; import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../lib/util/helpers'; import { executeKQL, hostExistsQuery, toggleTimelineVisibility } from '../../lib/timeline/helpers'; +import { closesModal, openStatsAndTables } from '../../lib/inspect/helpers'; describe('Inspect', () => { - describe('Hosts and network stats and tables', () => { - INSPECT_BUTTONS_IN_SIEM.map(table => + context('Hosts stats and tables', () => { + before(() => { + loginAndWaitForPage(HOSTS_PAGE); + }); + afterEach(() => { + closesModal(); + }); + + INSPECT_HOSTS_BUTTONS_IN_SIEM.forEach(table => + it(`inspects the ${table.title}`, () => { + openStatsAndTables(table); + cy.get(INSPECT_MODAL, { timeout: DEFAULT_TIMEOUT }).should('be.visible'); + }) + ); + }); + + context('Network stats and tables', () => { + before(() => { + loginAndWaitForPage(NETWORK_PAGE); + }); + afterEach(() => { + closesModal(); + }); + + INSPECT_NETWORK_BUTTONS_IN_SIEM.forEach(table => it(`inspects the ${table.title}`, () => { - loginAndWaitForPage(table.url); - cy.get(table.id, { timeout: DEFAULT_TIMEOUT }); - if (table.altInspectId) { - cy.get(table.altInspectId).trigger('click', { force: true }); - } else { - cy.get(`${table.id} ${INSPECT_BUTTON_ICON}`).trigger('click', { force: true }); - } + openStatsAndTables(table); cy.get(INSPECT_MODAL, { timeout: DEFAULT_TIMEOUT }).should('be.visible'); }) ); }); - describe('Timeline', () => { + context('Timeline', () => { it('inspects the timeline', () => { loginAndWaitForPage(HOSTS_PAGE); toggleTimelineVisibility(); diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts index 824e403185238..24c1974cf8343 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts @@ -9,7 +9,11 @@ import { TIMELINE_DROPPED_DATA_PROVIDERS, TIMELINE_DATA_PROVIDERS_EMPTY, } from '../../lib/timeline/selectors'; -import { dragFromAllHostsToTimeline, toggleTimelineVisibility } from '../../lib/timeline/helpers'; +import { + createNewTimeline, + dragFromAllHostsToTimeline, + toggleTimelineVisibility, +} from '../../lib/timeline/helpers'; import { ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS } from '../../lib/hosts/selectors'; import { HOSTS_PAGE } from '../../lib/urls'; import { waitForAllHostsWidget } from '../../lib/hosts/helpers'; @@ -17,15 +21,20 @@ import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../lib/util/helpers'; import { drag, dragWithoutDrop } from '../../lib/drag_n_drop/helpers'; describe('timeline data providers', () => { - beforeEach(() => { + before(() => { loginAndWaitForPage(HOSTS_PAGE); - }); - - it('renders the data provider of a host dragged from the All Hosts widget on the hosts page', () => { waitForAllHostsWidget(); + }); + beforeEach(() => { toggleTimelineVisibility(); + }); + + afterEach(() => { + createNewTimeline(); + }); + it('renders the data provider of a host dragged from the All Hosts widget on the hosts page', () => { dragFromAllHostsToTimeline(); cy.get(TIMELINE_DROPPED_DATA_PROVIDERS, { @@ -45,10 +54,6 @@ describe('timeline data providers', () => { }); it('sets the background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the data providers', () => { - waitForAllHostsWidget(); - - toggleTimelineVisibility(); - cy.get(ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS) .first() .then(host => drag(host)); @@ -61,10 +66,6 @@ describe('timeline data providers', () => { }); it('sets the background to euiColorSuccess with a 20% alpha channel when the user starts dragging a host AND is hovering over the data providers', () => { - waitForAllHostsWidget(); - - toggleTimelineVisibility(); - cy.get(ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS) .first() .then(host => drag(host)); @@ -81,10 +82,6 @@ describe('timeline data providers', () => { }); it('renders the dashed border color as euiColorSuccess when hovering over the data providers', () => { - waitForAllHostsWidget(); - - toggleTimelineVisibility(); - cy.get(ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS) .first() .then(host => drag(host)); diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts index 5b0ac03ae87dc..63fe56371a4cd 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts @@ -13,13 +13,23 @@ import { HOSTS_PAGE } from '../../lib/urls'; import { waitForAllHostsWidget } from '../../lib/hosts/helpers'; import { loginAndWaitForPage } from '../../lib/util/helpers'; import { drag } from '../../lib/drag_n_drop/helpers'; -import { toggleTimelineVisibility } from '../../lib/timeline/helpers'; +import { createNewTimeline, toggleTimelineVisibility } from '../../lib/timeline/helpers'; describe('timeline flyout button', () => { - beforeEach(() => { + before(() => { loginAndWaitForPage(HOSTS_PAGE); }); + afterEach(() => { + cy.get('[data-test-subj="kibanaChrome"]').then($page => { + if ($page.find('[data-test-subj="flyoutOverlay"]').length === 1) { + toggleTimelineVisibility(); + } + }); + + createNewTimeline(); + }); + it('toggles open the timeline', () => { toggleTimelineVisibility(); diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts index 9a915b0e77d44..fbf75e8a854c6 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/toggle_column.spec.ts @@ -6,15 +6,19 @@ import { drag, drop } from '../../lib/drag_n_drop/helpers'; import { populateTimeline } from '../../lib/fields_browser/helpers'; -import { toggleFirstTimelineEventDetails } from '../../lib/timeline/helpers'; +import { createNewTimeline, toggleFirstTimelineEventDetails } from '../../lib/timeline/helpers'; import { HOSTS_PAGE } from '../../lib/urls'; import { loginAndWaitForPage, DEFAULT_TIMEOUT } from '../../lib/util/helpers'; describe('toggle column in timeline', () => { - beforeEach(() => { + before(() => { loginAndWaitForPage(HOSTS_PAGE); }); + afterEach(() => { + createNewTimeline(); + }); + const timestampField = '@timestamp'; const idField = '_id'; diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx index 32dce0e2e5576..56ebbb06f3eb9 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect, useCallback, useMemo } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { ScaleType } from '@elastic/charts'; import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; @@ -26,14 +26,12 @@ import { HistogramAggregation, MatrixHistogramQueryProps, } from './types'; -import { generateTablePaginationOptions } from '../paginated_table/helpers'; import { ChartSeriesData } from '../charts/common'; import { InspectButtonContainer } from '../inspect'; export const MatrixHistogramComponent: React.FC = ({ activePage, - dataKey, defaultStackByOption, endDate, @@ -45,12 +43,10 @@ export const MatrixHistogramComponent: React.FC - activePage != null && limit != null - ? generateTablePaginationOptions(activePage, limit) - : undefined; const { data, loading, inspect, totalCount, refetch = noop } = useQuery<{}, HistogramAggregation>( { @@ -118,16 +110,13 @@ export const MatrixHistogramComponent: React.FC getPagination(), [activePage, limit]), stackByField: selectedStackByOption.value, } ); @@ -179,7 +168,7 @@ export const MatrixHistogramComponent: React.FC - {stackByOptions && ( + {stackByOptions?.length > 1 && ( void; - isEventsType?: boolean; errorMessage: string; headerChildren?: React.ReactNode; hideHistogramIfEmpty?: boolean; + isAlertsHistogram?: boolean; + isAnomaliesHistogram?: boolean; + isAuthenticationsHistogram?: boolean; id: string; + isDnsHistogram?: boolean; + isEventsHistogram?: boolean; legendPosition?: Position; mapping?: MatrixHistogramMappingTypes; query: Maybe; setQuery: SetQuery; + showLegend?: boolean; sourceId: string; stackByOptions: MatrixHistogramOption[]; subtitle?: string | GetSubTitle; diff --git a/x-pack/legacy/plugins/siem/public/containers/matrix_histogram/utils.ts b/x-pack/legacy/plugins/siem/public/containers/matrix_histogram/utils.ts index 1fc1fedae9f88..9cda9d8f6115f 100644 --- a/x-pack/legacy/plugins/siem/public/containers/matrix_histogram/utils.ts +++ b/x-pack/legacy/plugins/siem/public/containers/matrix_histogram/utils.ts @@ -16,7 +16,7 @@ import { useUiSetting$ } from '../../lib/kibana'; import { createFilter } from '../helpers'; import { useApolloClient } from '../../utils/apollo_context'; import { inputsModel } from '../../store'; -import { GetMatrixHistogramQuery, GetNetworkDnsQuery } from '../../graphql/types'; +import { GetMatrixHistogramQuery } from '../../graphql/types'; export const useQuery = ({ dataKey, @@ -26,15 +26,12 @@ export const useQuery = ({ isAlertsHistogram = false, isAnomaliesHistogram = false, isAuthenticationsHistogram = false, - isEventsType = false, - isDNSHistogram, - isPtrIncluded, + isEventsHistogram = false, + isDnsHistogram = false, isInspected, query, stackByField, startDate, - sort, - pagination, }: MatrixHistogramQueryProps) => { const [defaultIndex] = useUiSetting$(DEFAULT_INDEX_KEY); const [, dispatchToaster] = useStateToaster(); @@ -45,21 +42,7 @@ export const useQuery = ({ const [totalCount, setTotalCount] = useState(-1); const apolloClient = useApolloClient(); - const isDNSQuery = ( - variable: Pick< - MatrixHistogramQueryProps, - 'isDNSHistogram' | 'isPtrIncluded' | 'sort' | 'pagination' - > - ): variable is GetNetworkDnsQuery.Variables => { - return ( - !!isDNSHistogram && - variable.isDNSHistogram !== undefined && - variable.isPtrIncluded !== undefined && - variable.sort !== undefined && - variable.pagination !== undefined - ); - }; - const basicVariables = { + const matrixHistogramVariables: GetMatrixHistogramQuery.Variables = { filterQuery: createFilter(filterQuery), sourceId: 'default', timerange: { @@ -70,20 +53,11 @@ export const useQuery = ({ defaultIndex, inspect: isInspected, stackByField, - }; - const dnsVariables = { - ...basicVariables, - isDNSHistogram, - isPtrIncluded, - sort, - pagination, - }; - const matrixHistogramVariables: GetMatrixHistogramQuery.Variables = { - ...basicVariables, isAlertsHistogram, isAnomaliesHistogram, isAuthenticationsHistogram, - isEventsType, + isDnsHistogram, + isEventsHistogram, }; useEffect(() => { @@ -92,16 +66,13 @@ export const useQuery = ({ const abortSignal = abortCtrl.signal; async function fetchData() { - if (!apolloClient || (pagination != null && pagination.querySize < 0)) return null; + if (!apolloClient) return null; setLoading(true); return apolloClient - .query< - GetMatrixHistogramQuery.Query | GetNetworkDnsQuery.Query, - GetMatrixHistogramQuery.Variables | GetNetworkDnsQuery.Variables - >({ + .query({ query, fetchPolicy: 'cache-first', - variables: isDNSQuery(dnsVariables) ? dnsVariables : matrixHistogramVariables, + variables: matrixHistogramVariables, context: { fetchOptions: { abortSignal, @@ -145,11 +116,8 @@ export const useQuery = ({ query, filterQuery, isInspected, - isDNSHistogram, + isDnsHistogram, stackByField, - sort, - isPtrIncluded, - pagination, startDate, endDate, ]); diff --git a/x-pack/legacy/plugins/siem/public/containers/network_dns/index.gql_query.ts b/x-pack/legacy/plugins/siem/public/containers/network_dns/index.gql_query.ts index 9d82705e9524b..a81d112fa4c50 100644 --- a/x-pack/legacy/plugins/siem/public/containers/network_dns/index.gql_query.ts +++ b/x-pack/legacy/plugins/siem/public/containers/network_dns/index.gql_query.ts @@ -11,7 +11,6 @@ export const networkDnsQuery = gql` $defaultIndex: [String!]! $filterQuery: String $inspect: Boolean! - $isDNSHistogram: Boolean! $isPtrIncluded: Boolean! $pagination: PaginationInputPaginated! $sort: NetworkDnsSortField! @@ -31,7 +30,7 @@ export const networkDnsQuery = gql` stackByField: $stackByField ) { totalCount - edges @skip(if: $isDNSHistogram) { + edges { node { _id dnsBytesIn @@ -44,7 +43,7 @@ export const networkDnsQuery = gql` value } } - pageInfo @skip(if: $isDNSHistogram) { + pageInfo { activePage fakeTotalCount showMorePagesIndicator @@ -53,11 +52,6 @@ export const networkDnsQuery = gql` dsl response } - histogram @include(if: $isDNSHistogram) { - x - y - g - } } } } diff --git a/x-pack/legacy/plugins/siem/public/containers/network_dns/index.tsx b/x-pack/legacy/plugins/siem/public/containers/network_dns/index.tsx index 5c5552edcc4ba..04c8783c30a0f 100644 --- a/x-pack/legacy/plugins/siem/public/containers/network_dns/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/network_dns/index.tsx @@ -26,7 +26,7 @@ import { generateTablePaginationOptions } from '../../components/paginated_table import { createFilter, getDefaultFetchPolicy } from '../helpers'; import { QueryTemplatePaginated, QueryTemplatePaginatedProps } from '../query_template_paginated'; import { networkDnsQuery } from './index.gql_query'; -import { DEFAULT_TABLE_ACTIVE_PAGE } from '../../store/constants'; +import { DEFAULT_TABLE_ACTIVE_PAGE, DEFAULT_TABLE_LIMIT } from '../../store/constants'; import { MatrixHistogram } from '../../components/matrix_histogram'; import { MatrixHistogramOption, GetSubTitle } from '../../components/matrix_histogram/types'; import { UpdateDateRange } from '../../components/charts/common'; @@ -57,8 +57,7 @@ interface DnsHistogramOwnProps extends QueryTemplatePaginatedProps { dataKey: string | string[]; defaultStackByOption: MatrixHistogramOption; errorMessage: string; - isDNSHistogram?: boolean; - limit: number; + isDnsHistogram?: boolean; query: DocumentNode; scaleType: ScaleType; setQuery: SetQuery; @@ -105,7 +104,6 @@ export class NetworkDnsComponentQuery extends QueryTemplatePaginated< const variables: GetNetworkDnsQuery.Variables = { defaultIndex: kibana.services.uiSettings.get(DEFAULT_INDEX_KEY), filterQuery: createFilter(filterQuery), - isDNSHistogram: false, inspect: isInspected, isPtrIncluded, pagination: generateTablePaginationOptions(activePage, limit), @@ -186,12 +184,12 @@ const makeMapStateToProps = () => { const makeMapHistogramStateToProps = () => { const getNetworkDnsSelector = networkSelectors.dnsSelector(); const getQuery = inputsSelectors.globalQueryByIdSelector(); - const mapStateToProps = (state: State, { id = HISTOGRAM_ID, limit }: DnsHistogramOwnProps) => { + const mapStateToProps = (state: State, { id = HISTOGRAM_ID }: DnsHistogramOwnProps) => { const { isInspected } = getQuery(state, id); return { ...getNetworkDnsSelector(state), activePage: DEFAULT_TABLE_ACTIVE_PAGE, - limit, + limit: DEFAULT_TABLE_LIMIT, isInspected, id, }; diff --git a/x-pack/legacy/plugins/siem/public/graphql/introspection.json b/x-pack/legacy/plugins/siem/public/graphql/introspection.json index d73755fb92185..7b9842fa2c2bc 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/introspection.json +++ b/x-pack/legacy/plugins/siem/public/graphql/introspection.json @@ -1901,6 +1901,59 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "NetworkDnsHistogram", + "description": "", + "args": [ + { + "name": "filterQuery", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + }, + { + "name": "defaultIndex", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "String", "ofType": null } + } + } + }, + "defaultValue": null + }, + { + "name": "timerange", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "INPUT_OBJECT", "name": "TimerangeInput", "ofType": null } + }, + "defaultValue": null + }, + { + "name": "stackByField", + "description": "", + "type": { "kind": "SCALAR", "name": "String", "ofType": null }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "OBJECT", "name": "NetworkDsOverTimeData", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "NetworkHttp", "description": "", @@ -8744,6 +8797,61 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "NetworkDsOverTimeData", + "description": "", + "fields": [ + { + "name": "inspect", + "description": "", + "args": [], + "type": { "kind": "OBJECT", "name": "Inspect", "ofType": null }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "matrixHistogramData", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "MatrixOverTimeHistogramData", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "totalCount", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { "kind": "SCALAR", "name": "Float", "ofType": null } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "INPUT_OBJECT", "name": "NetworkHttpSortField", diff --git a/x-pack/legacy/plugins/siem/public/graphql/types.ts b/x-pack/legacy/plugins/siem/public/graphql/types.ts index 73049e26f1581..b13e295a8e168 100644 --- a/x-pack/legacy/plugins/siem/public/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/public/graphql/types.ts @@ -499,6 +499,8 @@ export interface Source { NetworkDns: NetworkDnsData; + NetworkDnsHistogram: NetworkDsOverTimeData; + NetworkHttp: NetworkHttpData; OverviewNetwork?: Maybe; @@ -1752,6 +1754,14 @@ export interface MatrixOverOrdinalHistogramData { g: string; } +export interface NetworkDsOverTimeData { + inspect?: Maybe; + + matrixHistogramData: MatrixOverTimeHistogramData[]; + + totalCount: number; +} + export interface NetworkHttpData { edges: NetworkHttpEdges[]; @@ -2430,6 +2440,15 @@ export interface NetworkDnsSourceArgs { defaultIndex: string[]; } +export interface NetworkDnsHistogramSourceArgs { + filterQuery?: Maybe; + + defaultIndex: string[]; + + timerange: TimerangeInput; + + stackByField?: Maybe; +} export interface NetworkHttpSourceArgs { id?: Maybe; @@ -3306,8 +3325,9 @@ export namespace GetMatrixHistogramQuery { isAlertsHistogram: boolean; isAnomaliesHistogram: boolean; isAuthenticationsHistogram: boolean; + isDnsHistogram: boolean; defaultIndex: string[]; - isEventsType: boolean; + isEventsHistogram: boolean; filterQuery?: Maybe; inspect: boolean; sourceId: string; @@ -3333,6 +3353,8 @@ export namespace GetMatrixHistogramQuery { AuthenticationsHistogram: AuthenticationsHistogram; EventsHistogram: EventsHistogram; + + NetworkDnsHistogram: NetworkDnsHistogram; }; export type AlertsHistogram = { @@ -3446,6 +3468,34 @@ export namespace GetMatrixHistogramQuery { response: string[]; }; + + export type NetworkDnsHistogram = { + __typename?: 'NetworkDsOverTimeData'; + + matrixHistogramData: ____MatrixHistogramData[]; + + totalCount: number; + + inspect: Maybe<____Inspect>; + }; + + export type ____MatrixHistogramData = { + __typename?: 'MatrixOverTimeHistogramData'; + + x: number; + + y: number; + + g: string; + }; + + export type ____Inspect = { + __typename?: 'Inspect'; + + dsl: string[]; + + response: string[]; + }; } export namespace GetNetworkDnsQuery { @@ -3453,7 +3503,6 @@ export namespace GetNetworkDnsQuery { defaultIndex: string[]; filterQuery?: Maybe; inspect: boolean; - isDNSHistogram: boolean; isPtrIncluded: boolean; pagination: PaginationInputPaginated; sort: NetworkDnsSortField; @@ -3486,8 +3535,6 @@ export namespace GetNetworkDnsQuery { pageInfo: PageInfo; inspect: Maybe; - - histogram: Maybe; }; export type Edges = { @@ -3537,16 +3584,6 @@ export namespace GetNetworkDnsQuery { response: string[]; }; - - export type Histogram = { - __typename?: 'MatrixOverOrdinalHistogramData'; - - x: string; - - y: number; - - g: string; - }; } export namespace GetNetworkHttpQuery { diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/events_query_tab_body.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/events_query_tab_body.tsx index df83ad056943a..a07cbc8484a1b 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/events_query_tab_body.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/navigation/events_query_tab_body.tsx @@ -52,7 +52,7 @@ export const EventsQueryTabBody = ({ defaultStackByOption={eventsStackByOptions[0]} deleteQuery={deleteQuery} endDate={endDate} - isEventsType={true} + isEventsHistogram={true} errorMessage={i18n.ERROR_FETCHING_EVENTS_DATA} filterQuery={filterQuery} query={MatrixHistogramGqlQuery} diff --git a/x-pack/legacy/plugins/siem/public/pages/network/navigation/dns_query_tab_body.tsx b/x-pack/legacy/plugins/siem/public/pages/network/navigation/dns_query_tab_body.tsx index 53d68e44f7843..35d6eb15416c9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/navigation/dns_query_tab_body.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/navigation/dns_query_tab_body.tsx @@ -4,31 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useEffect } from 'react'; +import React, { useEffect, useCallback } from 'react'; import { getOr } from 'lodash/fp'; import { EuiSpacer } from '@elastic/eui'; -import { ScaleType } from '@elastic/charts'; import { NetworkDnsTable } from '../../../components/page/network/network_dns_table'; -import { - NetworkDnsQuery, - NetworkDnsHistogramQuery, - HISTOGRAM_ID, -} from '../../../containers/network_dns'; +import { NetworkDnsQuery, HISTOGRAM_ID } from '../../../containers/network_dns'; import { manageQuery } from '../../../components/page/manage_query'; import { NetworkComponentQueryProps } from './types'; import { networkModel } from '../../../store'; + import { MatrixHistogramOption } from '../../../components/matrix_histogram/types'; -import { networkDnsQuery } from '../../../containers/network_dns/index.gql_query'; import * as i18n from '../translations'; -import { useFormatBytes } from '../../../components/formatted_bytes'; +import { MatrixHistogramGqlQuery } from '../../../containers/matrix_histogram/index.gql_query'; +import { MatrixHistogramContainer } from '../../../containers/matrix_histogram'; const NetworkDnsTableManage = manageQuery(NetworkDnsTable); const dnsStackByOptions: MatrixHistogramOption[] = [ { - text: i18n.NAVIGATION_DNS_STACK_BY_DOMAIN, + text: i18n.STACK_BY_DOMAIN, value: 'dns.question.registered_domain', }, ]; @@ -50,50 +46,52 @@ export const DnsQueryTabBody = ({ } }; }, [deleteQuery]); - const formatBytes = useFormatBytes(); + + const getTitle = useCallback( + (option: MatrixHistogramOption) => i18n.DOMAINS_COUNT_BY(option.text), + [] + ); return ( - - {({ - totalCount, - loading, - networkDns, - pageInfo, - loadPage, - id, - inspect, - isInspected, - refetch, - }) => ( - <> - - + <> + + + + {({ + totalCount, + loading, + networkDns, + pageInfo, + loadPage, + id, + inspect, + isInspected, + refetch, + }) => ( - - )} - + )} + + ); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/network/translations.ts b/x-pack/legacy/plugins/siem/public/pages/network/translations.ts index 00adce9b7ad8a..6446cde26796e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/network/translations.ts @@ -22,12 +22,9 @@ export const NAVIGATION_DNS_TITLE = i18n.translate('xpack.siem.network.navigatio defaultMessage: 'DNS', }); -export const NAVIGATION_DNS_STACK_BY_DOMAIN = i18n.translate( - 'xpack.siem.hosts.navigation.dns.stackByDomain', - { - defaultMessage: 'domain', - } -); +export const STACK_BY_DOMAIN = i18n.translate('xpack.siem.hosts.dns.stackByDomain', { + defaultMessage: 'unique domains', +}); export const ERROR_FETCHING_DNS_DATA = i18n.translate( 'xpack.siem.hosts.navigation.dns.histogram.errorFetchingDnsData', @@ -54,3 +51,9 @@ export const NAVIGATION_ANOMALIES_TITLE = i18n.translate( export const NAVIGATION_ALERTS_TITLE = i18n.translate('xpack.siem.network.navigation.alertsTitle', { defaultMessage: 'Alerts', }); + +export const DOMAINS_COUNT_BY = (groupByField: string) => + i18n.translate('xpack.siem.network.dns.stackByUniqueSubdomain', { + values: { groupByField }, + defaultMessage: 'Top domains by {groupByField}', + }); diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx index b4f945c802e56..52084c4bfc280 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/events_by_dataset/index.tsx @@ -108,7 +108,7 @@ export const EventsByDataset = React.memo( })} headerChildren={eventsCountViewEventsButton} id={ID} - isEventsType={true} + isEventsHistogram={true} legendPosition={'right'} query={MatrixHistogramGqlQuery} setQuery={setQuery} diff --git a/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts b/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts index 2dabd51c198f7..06d6b8c516d8b 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/network/resolvers.ts @@ -7,7 +7,7 @@ import { SourceResolvers } from '../../graphql/types'; import { AppResolverOf, ChildResolverOf } from '../../lib/framework'; import { Network } from '../../lib/network'; -import { createOptionsPaginated } from '../../utils/build_query/create_options'; +import { createOptionsPaginated, createOptions } from '../../utils/build_query/create_options'; import { QuerySourceResolver } from '../sources/resolvers'; type QueryNetworkTopCountriesResolver = ChildResolverOf< @@ -30,6 +30,10 @@ type QueryDnsResolver = ChildResolverOf< QuerySourceResolver >; +type QueryDnsHistogramResolver = ChildResolverOf< + AppResolverOf, + QuerySourceResolver +>; export interface NetworkResolversDeps { network: Network; } @@ -42,6 +46,7 @@ export const createNetworkResolvers = ( NetworkTopCountries: QueryNetworkTopCountriesResolver; NetworkTopNFlow: QueryNetworkTopNFlowResolver; NetworkDns: QueryDnsResolver; + NetworkDnsHistogram: QueryDnsHistogramResolver; }; } => ({ Source: { @@ -76,9 +81,15 @@ export const createNetworkResolvers = ( ...createOptionsPaginated(source, args, info), networkDnsSortField: args.sort, isPtrIncluded: args.isPtrIncluded, - stackByField: args.stackByField, }; return libs.network.getNetworkDns(req, options); }, + async NetworkDnsHistogram(source, args, { req }, info) { + const options = { + ...createOptions(source, args, info), + stackByField: args.stackByField, + }; + return libs.network.getNetworkDnsHistogramData(req, options); + }, }, }); diff --git a/x-pack/legacy/plugins/siem/server/graphql/network/schema.gql.ts b/x-pack/legacy/plugins/siem/server/graphql/network/schema.gql.ts index a5bca68fb30b9..15e2d832a73c9 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/network/schema.gql.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/network/schema.gql.ts @@ -196,6 +196,12 @@ export const networkSchema = gql` inspect: Inspect } + type NetworkDsOverTimeData { + inspect: Inspect + matrixHistogramData: [MatrixOverTimeHistogramData!]! + totalCount: Float! + } + extend type Source { NetworkTopCountries( id: String @@ -227,6 +233,12 @@ export const networkSchema = gql` timerange: TimerangeInput! defaultIndex: [String!]! ): NetworkDnsData! + NetworkDnsHistogram( + filterQuery: String + defaultIndex: [String!]! + timerange: TimerangeInput! + stackByField: String + ): NetworkDsOverTimeData! NetworkHttp( id: String filterQuery: String diff --git a/x-pack/legacy/plugins/siem/server/graphql/types.ts b/x-pack/legacy/plugins/siem/server/graphql/types.ts index 48ca32874dda2..4a2119b6f7631 100644 --- a/x-pack/legacy/plugins/siem/server/graphql/types.ts +++ b/x-pack/legacy/plugins/siem/server/graphql/types.ts @@ -501,6 +501,8 @@ export interface Source { NetworkDns: NetworkDnsData; + NetworkDnsHistogram: NetworkDsOverTimeData; + NetworkHttp: NetworkHttpData; OverviewNetwork?: Maybe; @@ -1754,6 +1756,14 @@ export interface MatrixOverOrdinalHistogramData { g: string; } +export interface NetworkDsOverTimeData { + inspect?: Maybe; + + matrixHistogramData: MatrixOverTimeHistogramData[]; + + totalCount: number; +} + export interface NetworkHttpData { edges: NetworkHttpEdges[]; @@ -2432,6 +2442,15 @@ export interface NetworkDnsSourceArgs { defaultIndex: string[]; } +export interface NetworkDnsHistogramSourceArgs { + filterQuery?: Maybe; + + defaultIndex: string[]; + + timerange: TimerangeInput; + + stackByField?: Maybe; +} export interface NetworkHttpSourceArgs { id?: Maybe; @@ -2930,6 +2949,8 @@ export namespace SourceResolvers { NetworkDns?: NetworkDnsResolver; + NetworkDnsHistogram?: NetworkDnsHistogramResolver; + NetworkHttp?: NetworkHttpResolver; OverviewNetwork?: OverviewNetworkResolver, TypeParent, TContext>; @@ -3281,6 +3302,21 @@ export namespace SourceResolvers { defaultIndex: string[]; } + export type NetworkDnsHistogramResolver< + R = NetworkDsOverTimeData, + Parent = Source, + TContext = SiemContext + > = Resolver; + export interface NetworkDnsHistogramArgs { + filterQuery?: Maybe; + + defaultIndex: string[]; + + timerange: TimerangeInput; + + stackByField?: Maybe; + } + export type NetworkHttpResolver< R = NetworkHttpData, Parent = Source, @@ -7547,6 +7583,36 @@ export namespace MatrixOverOrdinalHistogramDataResolvers { > = Resolver; } +export namespace NetworkDsOverTimeDataResolvers { + export interface Resolvers { + inspect?: InspectResolver, TypeParent, TContext>; + + matrixHistogramData?: MatrixHistogramDataResolver< + MatrixOverTimeHistogramData[], + TypeParent, + TContext + >; + + totalCount?: TotalCountResolver; + } + + export type InspectResolver< + R = Maybe, + Parent = NetworkDsOverTimeData, + TContext = SiemContext + > = Resolver; + export type MatrixHistogramDataResolver< + R = MatrixOverTimeHistogramData[], + Parent = NetworkDsOverTimeData, + TContext = SiemContext + > = Resolver; + export type TotalCountResolver< + R = number, + Parent = NetworkDsOverTimeData, + TContext = SiemContext + > = Resolver; +} + export namespace NetworkHttpDataResolvers { export interface Resolvers { edges?: EdgesResolver; @@ -9227,6 +9293,7 @@ export type IResolvers = { NetworkDnsEdges?: NetworkDnsEdgesResolvers.Resolvers; NetworkDnsItem?: NetworkDnsItemResolvers.Resolvers; MatrixOverOrdinalHistogramData?: MatrixOverOrdinalHistogramDataResolvers.Resolvers; + NetworkDsOverTimeData?: NetworkDsOverTimeDataResolvers.Resolvers; NetworkHttpData?: NetworkHttpDataResolvers.Resolvers; NetworkHttpEdges?: NetworkHttpEdgesResolvers.Resolvers; NetworkHttpItem?: NetworkHttpItemResolvers.Resolvers; diff --git a/x-pack/legacy/plugins/siem/server/kibana.index.ts b/x-pack/legacy/plugins/siem/server/kibana.index.ts index 9ceb8e02864ec..a488db3f0c3d7 100644 --- a/x-pack/legacy/plugins/siem/server/kibana.index.ts +++ b/x-pack/legacy/plugins/siem/server/kibana.index.ts @@ -28,6 +28,7 @@ import { deleteRulesBulkRoute } from './lib/detection_engine/routes/rules/delete import { importRulesRoute } from './lib/detection_engine/routes/rules/import_rules_route'; import { exportRulesRoute } from './lib/detection_engine/routes/rules/export_rules_route'; import { findRulesStatusesRoute } from './lib/detection_engine/routes/rules/find_rules_status_route'; +import { getPrepackagedRulesStatusRoute } from './lib/detection_engine/routes/rules/get_prepackaged_rules_status_route'; const APP_ID = 'siem'; @@ -49,12 +50,16 @@ export const initServerWithKibana = (context: PluginInitializerContext, __legacy updateRulesRoute(__legacy); deleteRulesRoute(__legacy); findRulesRoute(__legacy); + addPrepackedRulesRoute(__legacy); + getPrepackagedRulesStatusRoute(__legacy); createRulesBulkRoute(__legacy); updateRulesBulkRoute(__legacy); deleteRulesBulkRoute(__legacy); + importRulesRoute(__legacy); exportRulesRoute(__legacy); + findRulesStatusesRoute(__legacy); // Detection Engine Signals routes that have the REST endpoints of /api/detection_engine/signals diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/_mock_server.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/_mock_server.ts index 1e475f2014fa2..8bddd4a1ef456 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/_mock_server.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/_mock_server.ts @@ -10,9 +10,12 @@ import { ElasticsearchPlugin } from 'src/legacy/core_plugins/elasticsearch'; import { savedObjectsClientMock } from '../../../../../../../../../src/core/server/mocks'; import { alertsClientMock } from '../../../../../../alerting/server/alerts_client.mock'; import { actionsClientMock } from '../../../../../../actions/server/actions_client.mock'; +import { APP_ID, SIGNALS_INDEX_KEY } from '../../../../../common/constants'; +import { ServerFacade } from '../../../../types'; const defaultConfig = { 'kibana.index': '.kibana', + [`xpack.${APP_ID}.${SIGNALS_INDEX_KEY}`]: '.siem-signals', }; const isKibanaConfig = (config: unknown): config is KibanaConfig => @@ -58,10 +61,10 @@ export const createMockServer = (config: Record = defaultConfig) server.decorate('request', 'getBasePath', () => '/s/default'); server.decorate('request', 'getActionsClient', () => actionsClient); server.plugins.elasticsearch = (elasticsearch as unknown) as ElasticsearchPlugin; + server.plugins.spaces = { getSpaceId: () => 'default' }; server.decorate('request', 'getSavedObjectsClient', () => savedObjectsClient); - return { - server, + server: server as ServerFacade & Hapi.Server, alertsClient, actionsClient, elasticsearch, @@ -82,7 +85,10 @@ export const createMockServerWithoutAlertClientDecoration = ( serverWithoutAlertClient.decorate('request', 'getBasePath', () => '/s/default'); serverWithoutAlertClient.decorate('request', 'getActionsClient', () => actionsClient); - return { serverWithoutAlertClient, actionsClient }; + return { + serverWithoutAlertClient: serverWithoutAlertClient as ServerFacade & Hapi.Server, + actionsClient, + }; }; export const createMockServerWithoutActionClientDecoration = ( @@ -98,7 +104,10 @@ export const createMockServerWithoutActionClientDecoration = ( serverWithoutActionClient.decorate('request', 'getBasePath', () => '/s/default'); serverWithoutActionClient.decorate('request', 'getAlertsClient', () => alertsClient); - return { serverWithoutActionClient, alertsClient }; + return { + serverWithoutActionClient: serverWithoutActionClient as ServerFacade & Hapi.Server, + alertsClient, + }; }; export const createMockServerWithoutActionOrAlertClientDecoration = ( @@ -111,6 +120,22 @@ export const createMockServerWithoutActionOrAlertClientDecoration = ( serverWithoutActionOrAlertClient.config = () => createMockKibanaConfig(config); return { - serverWithoutActionOrAlertClient, + serverWithoutActionOrAlertClient: serverWithoutActionOrAlertClient as ServerFacade & + Hapi.Server, }; }; + +export const getMockIndexName = () => + jest.fn().mockImplementation(() => ({ + callWithRequest: jest.fn().mockImplementationOnce(() => 'index-name'), + })); + +export const getMockEmptyIndex = () => + jest.fn().mockImplementation(() => ({ + callWithRequest: jest.fn().mockImplementation(() => ({ _shards: { total: 0 } })), + })); + +export const getMockNonEmptyIndex = () => + jest.fn().mockImplementation(() => ({ + callWithRequest: jest.fn().mockImplementation(() => ({ _shards: { total: 1 } })), + })); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 01f30a3ebbdea..30a8d9d935128 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -15,6 +15,7 @@ import { DETECTION_ENGINE_QUERY_SIGNALS_URL, INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY, + DETECTION_ENGINE_PREPACKAGED_URL, } from '../../../../../common/constants'; import { RuleAlertType, IRuleSavedAttributesSavedObjectAttributes } from '../../rules/types'; import { RuleAlertParamsRest } from '../../types'; @@ -157,7 +158,17 @@ export const getDeleteAsPostBulkRequest = (): ServerInjectOptions => ({ export const getPrivilegeRequest = (): ServerInjectOptions => ({ method: 'GET', - url: `${DETECTION_ENGINE_PRIVILEGES_URL}`, + url: DETECTION_ENGINE_PRIVILEGES_URL, +}); + +export const addPrepackagedRulesRequest = (): ServerInjectOptions => ({ + method: 'PUT', + url: DETECTION_ENGINE_PREPACKAGED_URL, +}); + +export const getPrepackagedRulesStatusRequest = (): ServerInjectOptions => ({ + method: 'GET', + url: `${DETECTION_ENGINE_PREPACKAGED_URL}/_status`, }); export interface FindHit { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts index 54872f80a4c6d..1ea681afb7949 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts @@ -7,7 +7,6 @@ import { createMockServer } from '../__mocks__/_mock_server'; import { getPrivilegeRequest, getMockPrivileges } from '../__mocks__/request_responses'; import { readPrivilegesRoute } from './read_privileges_route'; -import { ServerFacade } from '../../../../types'; import * as myUtils from '../utils'; describe('read_privileges', () => { @@ -19,7 +18,7 @@ describe('read_privileges', () => { elasticsearch.getCluster = jest.fn(() => ({ callWithRequest: jest.fn(() => getMockPrivileges()), })); - readPrivilegesRoute((server as unknown) as ServerFacade); + readPrivilegesRoute(server); }); afterEach(() => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts new file mode 100644 index 0000000000000..ed193b6473a9e --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.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 { + createMockServer, + createMockServerWithoutActionClientDecoration, + createMockServerWithoutAlertClientDecoration, + createMockServerWithoutActionOrAlertClientDecoration, + getMockEmptyIndex, + getMockNonEmptyIndex, +} from '../__mocks__/_mock_server'; +import { createRulesRoute } from './create_rules_route'; +import { + getFindResult, + getResult, + createActionResult, + addPrepackagedRulesRequest, + getFindResultWithSingleHit, +} from '../__mocks__/request_responses'; + +jest.mock('../../rules/get_prepackaged_rules', () => { + return { + getPrepackagedRules: () => { + return [ + { + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + version: 2, // set one higher than the mocks which is set to 1 to trigger updates + }, + ]; + }, + }; +}); + +import { addPrepackedRulesRoute } from './add_prepackaged_rules_route'; + +describe('add_prepackaged_rules_route', () => { + let { server, alertsClient, actionsClient, elasticsearch } = createMockServer(); + + beforeEach(() => { + jest.resetAllMocks(); + ({ server, alertsClient, actionsClient, elasticsearch } = createMockServer()); + elasticsearch.getCluster = getMockNonEmptyIndex(); + + addPrepackedRulesRoute(server); + }); + + describe('status codes with actionClient and alertClient', () => { + test('returns 200 when creating a with a valid actionClient and alertClient', async () => { + alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.get.mockResolvedValue(getResult()); + actionsClient.create.mockResolvedValue(createActionResult()); + alertsClient.create.mockResolvedValue(getResult()); + const { statusCode } = await server.inject(addPrepackagedRulesRequest()); + expect(statusCode).toBe(200); + }); + + test('returns 404 if actionClient is not available on the route', async () => { + const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); + createRulesRoute(serverWithoutActionClient); + const { statusCode } = await serverWithoutActionClient.inject(addPrepackagedRulesRequest()); + expect(statusCode).toBe(404); + }); + + test('returns 404 if alertClient is not available on the route', async () => { + const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); + createRulesRoute(serverWithoutAlertClient); + const { statusCode } = await serverWithoutAlertClient.inject(addPrepackagedRulesRequest()); + expect(statusCode).toBe(404); + }); + + test('returns 404 if alertClient and actionClient are both not available on the route', async () => { + const { + serverWithoutActionOrAlertClient, + } = createMockServerWithoutActionOrAlertClientDecoration(); + createRulesRoute(serverWithoutActionOrAlertClient); + const { statusCode } = await serverWithoutActionOrAlertClient.inject( + addPrepackagedRulesRequest() + ); + expect(statusCode).toBe(404); + }); + }); + + describe('validation', () => { + test('it returns a 400 if the index does not exist', async () => { + elasticsearch.getCluster = getMockEmptyIndex(); + alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.get.mockResolvedValue(getResult()); + actionsClient.create.mockResolvedValue(createActionResult()); + alertsClient.create.mockResolvedValue(getResult()); + const { payload } = await server.inject(addPrepackagedRulesRequest()); + expect(JSON.parse(payload)).toEqual({ + error: 'Bad Request', + message: + 'Pre-packaged rules cannot be installed until the space index is created: .siem-signals-default', + statusCode: 400, + }); + }); + }); + + describe('payload', () => { + test('1 rule is installed and 0 are updated when find results are empty', async () => { + alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.get.mockResolvedValue(getResult()); + actionsClient.create.mockResolvedValue(createActionResult()); + alertsClient.create.mockResolvedValue(getResult()); + const { payload } = await server.inject(addPrepackagedRulesRequest()); + expect(JSON.parse(payload)).toEqual({ + rules_installed: 1, + rules_updated: 0, + }); + }); + + test('1 rule is updated and 0 are installed when we return a single find and the versions are different', async () => { + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + alertsClient.get.mockResolvedValue(getResult()); + actionsClient.create.mockResolvedValue(createActionResult()); + alertsClient.create.mockResolvedValue(getResult()); + const { payload } = await server.inject(addPrepackagedRulesRequest()); + expect(JSON.parse(payload)).toEqual({ + rules_installed: 0, + rules_updated: 1, + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts index 922b70e87467e..5ceecdb058e5f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -45,15 +45,15 @@ export const createAddPrepackedRulesRoute = (server: ServerFacade): Hapi.ServerR const callWithRequest = callWithRequestFactory(request, server); const rulesFromFileSystem = getPrepackagedRules(); - const prepackedRules = await getExistingPrepackagedRules({ alertsClient }); - const rulesToInstall = getRulesToInstall(rulesFromFileSystem, prepackedRules); - const rulesToUpdate = getRulesToUpdate(rulesFromFileSystem, prepackedRules); + const prepackagedRules = await getExistingPrepackagedRules({ alertsClient }); + const rulesToInstall = getRulesToInstall(rulesFromFileSystem, prepackagedRules); + const rulesToUpdate = getRulesToUpdate(rulesFromFileSystem, prepackagedRules); const spaceIndex = getIndex(request, server); if (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0) { const spaceIndexExists = await getIndexExists(callWithRequest, spaceIndex); if (!spaceIndexExists) { - throw new Boom( + return Boom.badRequest( `Pre-packaged rules cannot be installed until the space index is created: ${spaceIndex}` ); } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts index d5650b078e678..0931e941f8e46 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts @@ -9,10 +9,10 @@ import { createMockServerWithoutActionClientDecoration, createMockServerWithoutAlertClientDecoration, createMockServerWithoutActionOrAlertClientDecoration, + getMockEmptyIndex, } from '../__mocks__/_mock_server'; import { createRulesRoute } from './create_rules_route'; import { ServerInjectOptions } from 'hapi'; -import { ServerFacade } from '../../../../types'; import { getFindResult, getResult, @@ -29,11 +29,7 @@ describe('create_rules_bulk', () => { beforeEach(() => { jest.resetAllMocks(); ({ server, alertsClient, actionsClient, elasticsearch } = createMockServer()); - elasticsearch.getCluster = jest.fn().mockImplementation(() => ({ - callWithRequest: jest.fn().mockImplementation(() => true), - })); - - createRulesBulkRoute((server as unknown) as ServerFacade); + createRulesBulkRoute(server); }); describe('status codes with actionClient and alertClient', () => { @@ -48,14 +44,14 @@ describe('create_rules_bulk', () => { test('returns 404 if actionClient is not available on the route', async () => { const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); - createRulesRoute((serverWithoutActionClient as unknown) as ServerFacade); + createRulesRoute(serverWithoutActionClient); const { statusCode } = await serverWithoutActionClient.inject(getReadBulkRequest()); expect(statusCode).toBe(404); }); test('returns 404 if alertClient is not available on the route', async () => { const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - createRulesRoute((serverWithoutAlertClient as unknown) as ServerFacade); + createRulesRoute(serverWithoutAlertClient); const { statusCode } = await serverWithoutAlertClient.inject(getReadBulkRequest()); expect(statusCode).toBe(404); }); @@ -64,13 +60,32 @@ describe('create_rules_bulk', () => { const { serverWithoutActionOrAlertClient, } = createMockServerWithoutActionOrAlertClientDecoration(); - createRulesRoute((serverWithoutActionOrAlertClient as unknown) as ServerFacade); + createRulesRoute(serverWithoutActionOrAlertClient); const { statusCode } = await serverWithoutActionOrAlertClient.inject(getReadBulkRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { + test('it gets a 409 if the index does not exist', async () => { + elasticsearch.getCluster = getMockEmptyIndex(); + alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.get.mockResolvedValue(getResult()); + actionsClient.create.mockResolvedValue(createActionResult()); + alertsClient.create.mockResolvedValue(getResult()); + const { payload } = await server.inject(getReadBulkRequest()); + expect(JSON.parse(payload)).toEqual([ + { + error: { + message: + 'To create a rule, the index must exist first. Index .siem-signals does not exist', + status_code: 400, + }, + rule_id: 'rule-1', + }, + ]); + }); + test('returns 200 if rule_id is not given as the id is auto generated from the alert framework', async () => { alertsClient.find.mockResolvedValue(getFindResult()); alertsClient.get.mockResolvedValue(getResult()); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index 15e3361986ab9..9c18f9040008c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -87,7 +87,7 @@ export const createCreateRulesBulkRoute = (server: ServerFacade): Hapi.ServerRou if (!indexExists) { return createBulkErrorObject({ ruleId: ruleIdOrUuid, - statusCode: 409, + statusCode: 400, message: `To create a rule, the index must exist first. Index ${finalIndex} does not exist`, }); } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts index 466d150d58466..77c6f6f3b4840 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.test.ts @@ -9,10 +9,11 @@ import { createMockServerWithoutActionClientDecoration, createMockServerWithoutAlertClientDecoration, createMockServerWithoutActionOrAlertClientDecoration, + getMockNonEmptyIndex, + getMockEmptyIndex, } from '../__mocks__/_mock_server'; import { createRulesRoute } from './create_rules_route'; import { ServerInjectOptions } from 'hapi'; -import { ServerFacade } from '../../../../types'; import { getFindResult, @@ -42,13 +43,8 @@ describe('create_rules', () => { elasticsearch, savedObjectsClient, } = createMockServer()); - elasticsearch.getCluster = jest.fn().mockImplementation(() => ({ - callWithRequest: jest - .fn() - .mockImplementation((endpoint, params) => ({ _shards: { total: 1 } })), - })); - - createRulesRoute((server as unknown) as ServerFacade); + elasticsearch.getCluster = getMockNonEmptyIndex(); + createRulesRoute(server); }); describe('status codes with actionClient and alertClient', () => { @@ -64,14 +60,14 @@ describe('create_rules', () => { test('returns 404 if actionClient is not available on the route', async () => { const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); - createRulesRoute((serverWithoutActionClient as unknown) as ServerFacade); + createRulesRoute(serverWithoutActionClient); const { statusCode } = await serverWithoutActionClient.inject(getCreateRequest()); expect(statusCode).toBe(404); }); test('returns 404 if alertClient is not available on the route', async () => { const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - createRulesRoute((serverWithoutAlertClient as unknown) as ServerFacade); + createRulesRoute(serverWithoutAlertClient); const { statusCode } = await serverWithoutAlertClient.inject(getCreateRequest()); expect(statusCode).toBe(404); }); @@ -80,13 +76,27 @@ describe('create_rules', () => { const { serverWithoutActionOrAlertClient, } = createMockServerWithoutActionOrAlertClientDecoration(); - createRulesRoute((serverWithoutActionOrAlertClient as unknown) as ServerFacade); + createRulesRoute(serverWithoutActionOrAlertClient); const { statusCode } = await serverWithoutActionOrAlertClient.inject(getCreateRequest()); expect(statusCode).toBe(404); }); }); describe('validation', () => { + test('it returns a 400 if the index does not exist', async () => { + elasticsearch.getCluster = getMockEmptyIndex(); + alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.get.mockResolvedValue(getResult()); + actionsClient.create.mockResolvedValue(createActionResult()); + alertsClient.create.mockResolvedValue(getResult()); + const { payload } = await server.inject(getCreateRequest()); + expect(JSON.parse(payload)).toEqual({ + error: 'Bad Request', + message: 'To create a rule, the index must exist first. Index .siem-signals does not exist', + statusCode: 400, + }); + }); + test('returns 200 if rule_id is not given as the id is auto generated from the alert framework', async () => { alertsClient.find.mockResolvedValue(getFindResult()); alertsClient.get.mockResolvedValue(getResult()); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts index 3e173e358557a..aa535d325f4b9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -78,17 +78,14 @@ export const createCreateRulesRoute = (server: ServerFacade): Hapi.ServerRoute = const callWithRequest = callWithRequestFactory(request, server); const indexExists = await getIndexExists(callWithRequest, finalIndex); if (!indexExists) { - return new Boom( - `To create a rule, the index must exist first. Index ${finalIndex} does not exist`, - { - statusCode: 400, - } + return Boom.badRequest( + `To create a rule, the index must exist first. Index ${finalIndex} does not exist` ); } if (ruleId != null) { const rule = await readRules({ alertsClient, ruleId }); if (rule != null) { - return new Boom(`rule_id: "${ruleId}" already exists`, { statusCode: 409 }); + return Boom.conflict(`rule_id: "${ruleId}" already exists`); } } const createdRule = await createRules({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts index 3c429c3e38f3a..7b8496b2fe725 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts @@ -22,7 +22,6 @@ import { getDeleteAsPostBulkRequestById, getFindResultStatus, } from '../__mocks__/request_responses'; -import { ServerFacade } from '../../../../types'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; import { deleteRulesBulkRoute } from './delete_rules_bulk_route'; @@ -33,7 +32,7 @@ describe('delete_rules', () => { beforeEach(() => { ({ server, alertsClient, savedObjectsClient } = createMockServer()); - deleteRulesBulkRoute((server as unknown) as ServerFacade); + deleteRulesBulkRoute(server); }); afterEach(() => { @@ -100,14 +99,14 @@ describe('delete_rules', () => { test('returns 404 if actionClient is not available on the route', async () => { const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); - deleteRulesBulkRoute((serverWithoutActionClient as unknown) as ServerFacade); + deleteRulesBulkRoute(serverWithoutActionClient); const { statusCode } = await serverWithoutActionClient.inject(getDeleteBulkRequest()); expect(statusCode).toBe(404); }); test('returns 404 if alertClient is not available on the route', async () => { const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - deleteRulesBulkRoute((serverWithoutAlertClient as unknown) as ServerFacade); + deleteRulesBulkRoute(serverWithoutAlertClient); const { statusCode } = await serverWithoutAlertClient.inject(getDeleteBulkRequest()); expect(statusCode).toBe(404); }); @@ -116,7 +115,7 @@ describe('delete_rules', () => { const { serverWithoutActionOrAlertClient, } = createMockServerWithoutActionOrAlertClientDecoration(); - deleteRulesBulkRoute((serverWithoutActionOrAlertClient as unknown) as ServerFacade); + deleteRulesBulkRoute(serverWithoutActionOrAlertClient); const { statusCode } = await serverWithoutActionOrAlertClient.inject(getDeleteBulkRequest()); expect(statusCode).toBe(404); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts index ee4edada52b6a..2854312246c5f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/delete_rules_route.test.ts @@ -13,7 +13,6 @@ import { import { deleteRulesRoute } from './delete_rules_route'; import { ServerInjectOptions } from 'hapi'; -import { ServerFacade } from '../../../../types'; import { getFindResult, @@ -30,7 +29,7 @@ describe('delete_rules', () => { beforeEach(() => { ({ server, alertsClient, savedObjectsClient } = createMockServer()); - deleteRulesRoute((server as unknown) as ServerFacade); + deleteRulesRoute(server); }); afterEach(() => { @@ -70,14 +69,14 @@ describe('delete_rules', () => { test('returns 404 if actionClient is not available on the route', async () => { const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); - deleteRulesRoute((serverWithoutActionClient as unknown) as ServerFacade); + deleteRulesRoute(serverWithoutActionClient); const { statusCode } = await serverWithoutActionClient.inject(getDeleteRequest()); expect(statusCode).toBe(404); }); test('returns 404 if alertClient is not available on the route', async () => { const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - deleteRulesRoute((serverWithoutAlertClient as unknown) as ServerFacade); + deleteRulesRoute(serverWithoutAlertClient); const { statusCode } = await serverWithoutAlertClient.inject(getDeleteRequest()); expect(statusCode).toBe(404); }); @@ -86,7 +85,7 @@ describe('delete_rules', () => { const { serverWithoutActionOrAlertClient, } = createMockServerWithoutActionOrAlertClientDecoration(); - deleteRulesRoute((serverWithoutActionOrAlertClient as unknown) as ServerFacade); + deleteRulesRoute(serverWithoutActionOrAlertClient); const { statusCode } = await serverWithoutActionOrAlertClient.inject(getDeleteRequest()); expect(statusCode).toBe(404); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts index 639e4877f7f72..0aab02281a536 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/find_rules_route.test.ts @@ -13,7 +13,6 @@ import { import { findRulesRoute } from './find_rules_route'; import { ServerInjectOptions } from 'hapi'; -import { ServerFacade } from '../../../../types'; import { getFindResult, getResult, getFindRequest } from '../__mocks__/request_responses'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; @@ -23,7 +22,7 @@ describe('find_rules', () => { beforeEach(() => { ({ server, alertsClient, actionsClient } = createMockServer()); - findRulesRoute((server as unknown) as ServerFacade); + findRulesRoute(server); }); afterEach(() => { @@ -46,14 +45,14 @@ describe('find_rules', () => { test('returns 404 if actionClient is not available on the route', async () => { const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); - findRulesRoute((serverWithoutActionClient as unknown) as ServerFacade); + findRulesRoute(serverWithoutActionClient); const { statusCode } = await serverWithoutActionClient.inject(getFindRequest()); expect(statusCode).toBe(404); }); test('returns 404 if alertClient is not available on the route', async () => { const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - findRulesRoute((serverWithoutAlertClient as unknown) as ServerFacade); + findRulesRoute(serverWithoutAlertClient); const { statusCode } = await serverWithoutAlertClient.inject(getFindRequest()); expect(statusCode).toBe(404); }); @@ -62,7 +61,7 @@ describe('find_rules', () => { const { serverWithoutActionOrAlertClient, } = createMockServerWithoutActionOrAlertClientDecoration(); - findRulesRoute((serverWithoutActionOrAlertClient as unknown) as ServerFacade); + findRulesRoute(serverWithoutActionOrAlertClient); const { statusCode } = await serverWithoutActionOrAlertClient.inject(getFindRequest()); expect(statusCode).toBe(404); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rule_status_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rule_status_route.test.ts new file mode 100644 index 0000000000000..1ae9e87b8eefe --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rule_status_route.test.ts @@ -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 { + createMockServer, + createMockServerWithoutActionClientDecoration, + createMockServerWithoutAlertClientDecoration, + createMockServerWithoutActionOrAlertClientDecoration, + getMockNonEmptyIndex, +} from '../__mocks__/_mock_server'; +import { createRulesRoute } from './create_rules_route'; +import { + getFindResult, + getResult, + createActionResult, + getFindResultWithSingleHit, + getPrepackagedRulesStatusRequest, +} from '../__mocks__/request_responses'; + +jest.mock('../../rules/get_prepackaged_rules', () => { + return { + getPrepackagedRules: () => { + return [ + { + rule_id: 'rule-1', + output_index: '.siem-signals', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + version: 2, // set one higher than the mocks which is set to 1 to trigger updates + }, + ]; + }, + }; +}); + +import { getPrepackagedRulesStatusRoute } from './get_prepackaged_rules_status_route'; + +describe('get_prepackaged_rule_status_route', () => { + let { server, alertsClient, actionsClient, elasticsearch } = createMockServer(); + + beforeEach(() => { + jest.resetAllMocks(); + ({ server, alertsClient, actionsClient, elasticsearch } = createMockServer()); + elasticsearch.getCluster = getMockNonEmptyIndex(); + getPrepackagedRulesStatusRoute(server); + }); + + describe('status codes with actionClient and alertClient', () => { + test('returns 200 when creating a with a valid actionClient and alertClient', async () => { + alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.get.mockResolvedValue(getResult()); + actionsClient.create.mockResolvedValue(createActionResult()); + alertsClient.create.mockResolvedValue(getResult()); + const { statusCode } = await server.inject(getPrepackagedRulesStatusRequest()); + expect(statusCode).toBe(200); + }); + + test('returns 404 if actionClient is not available on the route', async () => { + const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); + createRulesRoute(serverWithoutActionClient); + const { statusCode } = await serverWithoutActionClient.inject( + getPrepackagedRulesStatusRequest() + ); + expect(statusCode).toBe(404); + }); + + test('returns 404 if alertClient is not available on the route', async () => { + const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); + createRulesRoute(serverWithoutAlertClient); + const { statusCode } = await serverWithoutAlertClient.inject( + getPrepackagedRulesStatusRequest() + ); + expect(statusCode).toBe(404); + }); + + test('returns 404 if alertClient and actionClient are both not available on the route', async () => { + const { + serverWithoutActionOrAlertClient, + } = createMockServerWithoutActionOrAlertClientDecoration(); + createRulesRoute(serverWithoutActionOrAlertClient); + const { statusCode } = await serverWithoutActionOrAlertClient.inject( + getPrepackagedRulesStatusRequest() + ); + expect(statusCode).toBe(404); + }); + }); + + describe('payload', () => { + test('0 rules installed, 1 rules not installed, and 1 rule not updated', async () => { + alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.get.mockResolvedValue(getResult()); + actionsClient.create.mockResolvedValue(createActionResult()); + alertsClient.create.mockResolvedValue(getResult()); + const { payload } = await server.inject(getPrepackagedRulesStatusRequest()); + expect(JSON.parse(payload)).toEqual({ + rules_installed: 0, + rules_not_installed: 1, + rules_not_updated: 0, + }); + }); + + test('1 rule installed, 0 rules not installed, and 1 rule to not updated', async () => { + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + alertsClient.get.mockResolvedValue(getResult()); + actionsClient.create.mockResolvedValue(createActionResult()); + alertsClient.create.mockResolvedValue(getResult()); + const { payload } = await server.inject(getPrepackagedRulesStatusRequest()); + expect(JSON.parse(payload)).toEqual({ + rules_installed: 1, + rules_not_installed: 0, + rules_not_updated: 1, + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts new file mode 100644 index 0000000000000..99e29242bced0 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Hapi from 'hapi'; +import { isFunction } from 'lodash/fp'; + +import { DETECTION_ENGINE_PREPACKAGED_URL } from '../../../../../common/constants'; +import { ServerFacade, RequestFacade } from '../../../../types'; +import { transformError } from '../utils'; +import { getPrepackagedRules } from '../../rules/get_prepackaged_rules'; +import { getRulesToInstall } from '../../rules/get_rules_to_install'; +import { getRulesToUpdate } from '../../rules/get_rules_to_update'; +import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules'; + +export const createGetPrepackagedRulesStatusRoute = (): Hapi.ServerRoute => { + return { + method: 'GET', + path: `${DETECTION_ENGINE_PREPACKAGED_URL}/_status`, + options: { + tags: ['access:siem'], + validate: { + options: { + abortEarly: false, + }, + }, + }, + async handler(request: RequestFacade, headers) { + const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; + const actionsClient = isFunction(request.getActionsClient) + ? request.getActionsClient() + : null; + + if (!alertsClient || !actionsClient) { + return headers.response().code(404); + } + + try { + const rulesFromFileSystem = getPrepackagedRules(); + const prepackagedRules = await getExistingPrepackagedRules({ alertsClient }); + const rulesToInstall = getRulesToInstall(rulesFromFileSystem, prepackagedRules); + const rulesToUpdate = getRulesToUpdate(rulesFromFileSystem, prepackagedRules); + return { + rules_installed: prepackagedRules.length, + rules_not_installed: rulesToInstall.length, + rules_not_updated: rulesToUpdate.length, + }; + } catch (err) { + return transformError(err); + } + }, + }; +}; + +export const getPrepackagedRulesStatusRoute = (server: ServerFacade): void => { + server.route(createGetPrepackagedRulesStatusRoute()); +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts index 92db4be1c9ff9..4190225bea1f1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/read_rules_route.test.ts @@ -13,7 +13,6 @@ import { import { readRulesRoute } from './read_rules_route'; import { ServerInjectOptions } from 'hapi'; -import { ServerFacade } from '../../../../types'; import { getFindResult, @@ -29,7 +28,7 @@ describe('read_signals', () => { beforeEach(() => { ({ server, alertsClient, savedObjectsClient } = createMockServer()); - readRulesRoute((server as unknown) as ServerFacade); + readRulesRoute(server); }); afterEach(() => { @@ -47,14 +46,14 @@ describe('read_signals', () => { test('returns 404 if actionClient is not available on the route', async () => { const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); - readRulesRoute((serverWithoutActionClient as unknown) as ServerFacade); + readRulesRoute(serverWithoutActionClient); const { statusCode } = await serverWithoutActionClient.inject(getReadRequest()); expect(statusCode).toBe(404); }); test('returns 404 if alertClient is not available on the route', async () => { const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - readRulesRoute((serverWithoutAlertClient as unknown) as ServerFacade); + readRulesRoute(serverWithoutAlertClient); const { statusCode } = await serverWithoutAlertClient.inject(getReadRequest()); expect(statusCode).toBe(404); }); @@ -63,7 +62,7 @@ describe('read_signals', () => { const { serverWithoutActionOrAlertClient, } = createMockServerWithoutActionOrAlertClientDecoration(); - readRulesRoute((serverWithoutActionOrAlertClient as unknown) as ServerFacade); + readRulesRoute(serverWithoutActionOrAlertClient); const { statusCode } = await serverWithoutActionOrAlertClient.inject(getReadRequest()); expect(statusCode).toBe(404); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts index 2ab2610834195..cc41800671d7d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk.test.ts @@ -13,7 +13,6 @@ import { import { updateRulesRoute } from './update_rules_route'; import { ServerInjectOptions } from 'hapi'; -import { ServerFacade } from '../../../../types'; import { getFindResult, @@ -33,7 +32,7 @@ describe('update_rules_bulk', () => { beforeEach(() => { jest.resetAllMocks(); ({ server, alertsClient, actionsClient } = createMockServer()); - updateRulesBulkRoute((server as unknown) as ServerFacade); + updateRulesBulkRoute(server); }); describe('status codes with actionClient and alertClient', () => { @@ -73,14 +72,14 @@ describe('update_rules_bulk', () => { test('returns 404 if actionClient is not available on the route', async () => { const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); - updateRulesRoute((serverWithoutActionClient as unknown) as ServerFacade); + updateRulesRoute(serverWithoutActionClient); const { statusCode } = await serverWithoutActionClient.inject(getUpdateBulkRequest()); expect(statusCode).toBe(404); }); test('returns 404 if alertClient is not available on the route', async () => { const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - updateRulesRoute((serverWithoutAlertClient as unknown) as ServerFacade); + updateRulesRoute(serverWithoutAlertClient); const { statusCode } = await serverWithoutAlertClient.inject(getUpdateBulkRequest()); expect(statusCode).toBe(404); }); @@ -89,7 +88,7 @@ describe('update_rules_bulk', () => { const { serverWithoutActionOrAlertClient, } = createMockServerWithoutActionOrAlertClientDecoration(); - updateRulesRoute((serverWithoutActionOrAlertClient as unknown) as ServerFacade); + updateRulesRoute(serverWithoutActionOrAlertClient); const { statusCode } = await serverWithoutActionOrAlertClient.inject(getUpdateBulkRequest()); expect(statusCode).toBe(404); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts index d33989b8a7ce5..a7e8f1b1c0a7e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.test.ts @@ -13,7 +13,6 @@ import { import { updateRulesRoute } from './update_rules_route'; import { ServerInjectOptions } from 'hapi'; -import { ServerFacade } from '../../../../types'; import { getFindResult, @@ -32,7 +31,7 @@ describe('update_rules', () => { beforeEach(() => { jest.resetAllMocks(); ({ server, alertsClient, actionsClient, savedObjectsClient } = createMockServer()); - updateRulesRoute((server as unknown) as ServerFacade); + updateRulesRoute(server); }); describe('status codes with actionClient and alertClient', () => { @@ -58,14 +57,14 @@ describe('update_rules', () => { test('returns 404 if actionClient is not available on the route', async () => { const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); - updateRulesRoute((serverWithoutActionClient as unknown) as ServerFacade); + updateRulesRoute(serverWithoutActionClient); const { statusCode } = await serverWithoutActionClient.inject(getUpdateRequest()); expect(statusCode).toBe(404); }); test('returns 404 if alertClient is not available on the route', async () => { const { serverWithoutAlertClient } = createMockServerWithoutAlertClientDecoration(); - updateRulesRoute((serverWithoutAlertClient as unknown) as ServerFacade); + updateRulesRoute(serverWithoutAlertClient); const { statusCode } = await serverWithoutAlertClient.inject(getUpdateRequest()); expect(statusCode).toBe(404); }); @@ -74,7 +73,7 @@ describe('update_rules', () => { const { serverWithoutActionOrAlertClient, } = createMockServerWithoutActionOrAlertClientDecoration(); - updateRulesRoute((serverWithoutActionOrAlertClient as unknown) as ServerFacade); + updateRulesRoute(serverWithoutActionOrAlertClient); const { statusCode } = await serverWithoutActionOrAlertClient.inject(getUpdateRequest()); expect(statusCode).toBe(404); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts index 7d4fb43b58ef1..ae79b571b2b62 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/utils.ts @@ -34,11 +34,11 @@ export const getIdError = ({ ruleId: string | undefined | null; }) => { if (id != null) { - return new Boom(`id: "${id}" not found`, { statusCode: 404 }); + return Boom.notFound(`id: "${id}" not found`); } else if (ruleId != null) { - return new Boom(`rule_id: "${ruleId}" not found`, { statusCode: 404 }); + return Boom.notFound(`rule_id: "${ruleId}" not found`); } else { - return new Boom(`id or rule_id should have been defined`, { statusCode: 404 }); + return Boom.notFound('id or rule_id should have been defined'); } }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts index 74eb4d6c8e918..1993948808ef4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts @@ -205,8 +205,8 @@ describe('add prepackaged rules schema', () => { query: 'some query', language: 'kuery', version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('"output_index" is not allowed'); }); test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, version] does validate', () => { @@ -345,6 +345,48 @@ describe('add prepackaged rules schema', () => { ).toEqual(true); }); + test('immutable cannot be false', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + immutable: false, + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some-query', + language: 'kuery', + version: 1, + }).error.message + ).toEqual('child "immutable" fails because ["immutable" must be one of [true]]'); + }); + + test('immutable can be true', () => { + expect( + addPrepackagedRulesSchema.validate>({ + rule_id: 'rule-1', + risk_score: 50, + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + immutable: true, + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some-query', + language: 'kuery', + version: 1, + }).error + ).toBeFalsy(); + }); + test('defaults enabled to false', () => { expect( addPrepackagedRulesSchema.validate>({ @@ -380,8 +422,8 @@ describe('add prepackaged rules schema', () => { query: 'some-query', language: 'kuery', version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "rule_id" fails because ["rule_id" is required]'); }); test('references cannot be numbers', () => { @@ -403,8 +445,10 @@ describe('add prepackaged rules schema', () => { language: 'kuery', references: [5], version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "references" fails because ["references" at position 0 fails because ["0" must be a string]]' + ); }); test('indexes cannot be numbers', () => { @@ -425,8 +469,10 @@ describe('add prepackaged rules schema', () => { query: 'some-query', language: 'kuery', version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "index" fails because ["index" at position 0 fails because ["0" must be a string]]' + ); }); test('defaults interval to 5 min', () => { @@ -478,8 +524,8 @@ describe('add prepackaged rules schema', () => { interval: '5m', type: 'saved_query', version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "saved_id" fails because ["saved_id" is required]'); }); test('saved_id is required when type is saved_query and validates with it', () => { @@ -539,8 +585,8 @@ describe('add prepackaged rules schema', () => { saved_id: 'some id', filters: 'some string', version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "filters" fails because ["filters" must be an array]'); }); test('language validates with kuery', () => { @@ -602,8 +648,8 @@ describe('add prepackaged rules schema', () => { query: 'some query', language: 'something-made-up', version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "language" fails because ["language" must be one of [kuery, lucene]]'); }); test('max_signals cannot be negative', () => { @@ -624,8 +670,8 @@ describe('add prepackaged rules schema', () => { language: 'kuery', max_signals: -1, version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "max_signals" fails because ["max_signals" must be greater than 0]'); }); test('max_signals cannot be zero', () => { @@ -646,8 +692,8 @@ describe('add prepackaged rules schema', () => { language: 'kuery', max_signals: 0, version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "max_signals" fails because ["max_signals" must be greater than 0]'); }); test('max_signals can be 1', () => { @@ -716,8 +762,10 @@ describe('add prepackaged rules schema', () => { max_signals: 1, tags: [0, 1, 2], version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "tags" fails because ["tags" at position 0 fails because ["0" must be a string]]' + ); }); test('You cannot send in an array of threats that are missing "framework"', () => { @@ -758,9 +806,12 @@ describe('add prepackaged rules schema', () => { }, ], version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "framework" fails because ["framework" is required]]]' + ); }); + test('You cannot send in an array of threats that are missing "tactic"', () => { expect( addPrepackagedRulesSchema.validate< @@ -795,9 +846,12 @@ describe('add prepackaged rules schema', () => { }, ], version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' + ); }); + test('You cannot send in an array of threats that are missing "techniques"', () => { expect( addPrepackagedRulesSchema.validate< @@ -830,8 +884,10 @@ describe('add prepackaged rules schema', () => { }, ], version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "techniques" fails because ["techniques" is required]]]' + ); }); test('You can optionally send in an array of false positives', () => { @@ -878,8 +934,10 @@ describe('add prepackaged rules schema', () => { language: 'kuery', max_signals: 1, version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "false_positives" fails because ["false_positives" at position 0 fails because ["0" must be a string]]' + ); }); test('You can optionally set the immutable to be true', () => { @@ -926,8 +984,8 @@ describe('add prepackaged rules schema', () => { language: 'kuery', max_signals: 1, version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "immutable" fails because ["immutable" must be a boolean]'); }); test('You cannot set the risk_score to 101', () => { @@ -949,8 +1007,8 @@ describe('add prepackaged rules schema', () => { language: 'kuery', max_signals: 1, version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "risk_score" fails because ["risk_score" must be less than 101]'); }); test('You cannot set the risk_score to -1', () => { @@ -972,8 +1030,8 @@ describe('add prepackaged rules schema', () => { language: 'kuery', max_signals: 1, version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "risk_score" fails because ["risk_score" must be greater than -1]'); }); test('You can set the risk_score to 0', () => { @@ -1070,8 +1128,8 @@ describe('add prepackaged rules schema', () => { max_signals: 1, meta: 'should not work', version: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "meta" fails because ["meta" must be an object]'); }); test('You can omit the query string when filters are present', () => { @@ -1140,8 +1198,8 @@ describe('add prepackaged rules schema', () => { max_signals: 1, version: 1, timeline_id: 'timeline-id', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" is required]'); }); test('You cannot have a null value for timeline_title when timeline_id is present', () => { @@ -1165,8 +1223,8 @@ describe('add prepackaged rules schema', () => { version: 1, timeline_id: 'timeline-id', timeline_title: null, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" must be a string]'); }); test('You cannot have empty string for timeline_title when timeline_id is present', () => { @@ -1190,8 +1248,8 @@ describe('add prepackaged rules schema', () => { version: 1, timeline_id: 'timeline-id', timeline_title: '', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" is not allowed to be empty]'); }); test('You cannot have timeline_title with an empty timeline_id', () => { @@ -1215,8 +1273,8 @@ describe('add prepackaged rules schema', () => { version: 1, timeline_id: '', timeline_title: 'some-title', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_id" fails because ["timeline_id" is not allowed to be empty]'); }); test('You cannot have timeline_title without timeline_id', () => { @@ -1239,7 +1297,7 @@ describe('add prepackaged rules schema', () => { max_signals: 1, version: 1, timeline_title: 'some-title', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" is not allowed]'); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts index 49907b4a975e6..9311371d630f7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.ts @@ -42,7 +42,7 @@ import { DEFAULT_MAX_SIGNALS } from '../../../../../common/constants'; * Big differences between this schema and the createRulesSchema * - rule_id is required here * - output_index is not allowed (and instead the space index must be used) - * - immutable defaults to true instead of to false + * - immutable defaults to true instead of to false and if it is there can only be true * - enabled defaults to false instead of true * - version is a required field that must exist */ @@ -53,7 +53,7 @@ export const addPrepackagedRulesSchema = Joi.object({ filters, from: from.required(), rule_id: rule_id.required(), - immutable: immutable.default(true), + immutable: immutable.default(true).valid(true), index, interval: interval.default('5m'), query: query.allow('').default(''), diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts index 87916bea60649..15f4fa7f05648 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts @@ -238,6 +238,7 @@ describe('create rules schema', () => { }).error ).toBeFalsy(); }); + test('You can send in an empty array to threats', () => { expect( createRulesSchema.validate>({ @@ -260,6 +261,7 @@ describe('create rules schema', () => { }).error ).toBeFalsy(); }); + test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threats] does validate', () => { expect( createRulesSchema.validate>({ @@ -355,8 +357,10 @@ describe('create rules schema', () => { query: 'some-query', language: 'kuery', references: [5], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "references" fails because ["references" at position 0 fails because ["0" must be a string]]' + ); }); test('indexes cannot be numbers', () => { @@ -377,8 +381,10 @@ describe('create rules schema', () => { query: 'some-query', language: 'kuery', } - ).error - ).toBeTruthy(); + ).error.message + ).toEqual( + 'child "index" fails because ["index" at position 0 fails because ["0" must be a string]]' + ); }); test('defaults interval to 5 min', () => { @@ -430,8 +436,8 @@ describe('create rules schema', () => { severity: 'severity', interval: '5m', type: 'saved_query', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "saved_id" fails because ["saved_id" is required]'); }); test('saved_id is required when type is saved_query and validates with it', () => { @@ -491,8 +497,8 @@ describe('create rules schema', () => { type: 'saved_query', saved_id: 'some id', filters: 'some string', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "filters" fails because ["filters" must be an array]'); }); test('language validates with kuery', () => { @@ -554,8 +560,8 @@ describe('create rules schema', () => { references: ['index-1'], query: 'some query', language: 'something-made-up', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "language" fails because ["language" must be one of [kuery, lucene]]'); }); test('max_signals cannot be negative', () => { @@ -576,8 +582,8 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', max_signals: -1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "max_signals" fails because ["max_signals" must be greater than 0]'); }); test('max_signals cannot be zero', () => { @@ -598,8 +604,8 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', max_signals: 0, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "max_signals" fails because ["max_signals" must be greater than 0]'); }); test('max_signals can be 1', () => { @@ -666,8 +672,10 @@ describe('create rules schema', () => { language: 'kuery', max_signals: 1, tags: [0, 1, 2], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "tags" fails because ["tags" at position 0 fails because ["0" must be a string]]' + ); }); test('You cannot send in an array of threats that are missing "framework"', () => { @@ -708,9 +716,12 @@ describe('create rules schema', () => { ], }, ], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "framework" fails because ["framework" is required]]]' + ); }); + test('You cannot send in an array of threats that are missing "tactic"', () => { expect( createRulesSchema.validate< @@ -745,9 +756,12 @@ describe('create rules schema', () => { ], }, ], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' + ); }); + test('You cannot send in an array of threats that are missing "techniques"', () => { expect( createRulesSchema.validate< @@ -780,8 +794,10 @@ describe('create rules schema', () => { }, }, ], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "techniques" fails because ["techniques" is required]]]' + ); }); test('You can optionally send in an array of false positives', () => { @@ -828,34 +844,13 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "false_positives" fails because ["false_positives" at position 0 fails because ["0" must be a string]]' + ); }); - test('You can optionally set the immutable to be true', () => { - expect( - createRulesSchema.validate>({ - rule_id: 'rule-1', - output_index: '.siem-signals', - risk_score: 50, - description: 'some description', - from: 'now-5m', - to: 'now', - immutable: true, - index: ['index-1'], - name: 'some-name', - severity: 'severity', - interval: '5m', - type: 'query', - references: ['index-1'], - query: 'some query', - language: 'kuery', - max_signals: 1, - }).error - ).toBeFalsy(); - }); - - test('You cannot set the immutable to be a number', () => { + test('You cannot set the immutable when trying to create a rule', () => { expect( createRulesSchema.validate< Partial> & { immutable: number } @@ -876,8 +871,8 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('"immutable" is not allowed'); }); test('You cannot set the risk_score to 101', () => { @@ -899,8 +894,8 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "risk_score" fails because ["risk_score" must be less than 101]'); }); test('You cannot set the risk_score to -1', () => { @@ -922,8 +917,8 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "risk_score" fails because ["risk_score" must be greater than -1]'); }); test('You can set the risk_score to 0', () => { @@ -935,7 +930,6 @@ describe('create rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'severity', @@ -958,7 +952,6 @@ describe('create rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'severity', @@ -981,7 +974,6 @@ describe('create rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'severity', @@ -1018,8 +1010,8 @@ describe('create rules schema', () => { language: 'kuery', max_signals: 1, meta: 'should not work', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "meta" fails because ["meta" must be an object]'); }); test('You can omit the query string when filters are present', () => { @@ -1031,7 +1023,6 @@ describe('create rules schema', () => { description: 'some description', from: 'now-5m', to: 'now', - immutable: true, index: ['index-1'], name: 'some-name', severity: 'severity', @@ -1086,8 +1077,8 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', timeline_id: 'some_id', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" is required]'); }); test('You cannot have a null value for timeline_title when timeline_id is present', () => { @@ -1109,8 +1100,8 @@ describe('create rules schema', () => { language: 'kuery', timeline_id: 'some_id', timeline_title: null, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" must be a string]'); }); test('You cannot have empty string for timeline_title when timeline_id is present', () => { @@ -1132,8 +1123,8 @@ describe('create rules schema', () => { language: 'kuery', timeline_id: 'some_id', timeline_title: '', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" is not allowed to be empty]'); }); test('You cannot have timeline_title with an empty timeline_id', () => { @@ -1155,8 +1146,8 @@ describe('create rules schema', () => { language: 'kuery', timeline_id: '', timeline_title: 'some-title', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_id" fails because ["timeline_id" is not allowed to be empty]'); }); test('You cannot have timeline_title without timeline_id', () => { @@ -1177,7 +1168,7 @@ describe('create rules schema', () => { query: 'some query', language: 'kuery', timeline_title: 'some-title', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" is not allowed]'); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts index df5c1694d6c78..5d9972453fb1a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.ts @@ -13,7 +13,6 @@ import { false_positives, filters, from, - immutable, index, rule_id, interval, @@ -46,7 +45,6 @@ export const createRulesSchema = Joi.object({ filters, from: from.required(), rule_id, - immutable: immutable.default(false), index, interval: interval.default('5m'), query: query.allow('').default(''), diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/export_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/export_rules_schema.test.ts index 7850e3a733f09..dd3adf53f503b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/export_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/export_rules_schema.test.ts @@ -37,8 +37,10 @@ describe('create rules schema', () => { expect( exportRulesSchema.validate>({ objects: [{ id: 'test-1' }], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "objects" fails because ["objects" at position 0 fails because ["id" is not allowed]]' + ); }); }); @@ -70,8 +72,8 @@ describe('create rules schema', () => { Partial & { file_name: number }> >({ file_name: 5, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "file_name" fails because ["file_name" must be a string]'); }); test('exclude_export_details validates with a boolean true', () => { @@ -92,8 +94,10 @@ describe('create rules schema', () => { > >({ exclude_export_details: 'blah', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "exclude_export_details" fails because ["exclude_export_details" must be a boolean]' + ); }); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/find_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/find_rules_schema.test.ts index 14b3bdb298739..339874e19c33a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/find_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/find_rules_schema.test.ts @@ -69,8 +69,10 @@ describe('find rules schema', () => { expect( findRulesSchema.validate> & { fields: number[] }>({ fields: [5], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "fields" fails because ["fields" at position 0 fails because ["0" must be a string]]' + ); }); test('per page has a default of 20', () => { @@ -93,16 +95,16 @@ describe('find rules schema', () => { expect( findRulesSchema.validate> & { filter: number }>({ filter: 5, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "filter" fails because ["filter" must be a string]'); }); test('sort_order requires sort_field to work', () => { expect( findRulesSchema.validate>({ sort_order: 'asc', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "sort_field" fails because ["sort_field" is required]'); }); test('sort_order and sort_field validate together', () => { @@ -130,7 +132,7 @@ describe('find rules schema', () => { >({ sort_order: 'some other string', sort_field: 'some field', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "sort_order" fails because ["sort_order" must be one of [asc, desc]]'); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts index 09bc7a70711ec..bed64cc6e7a02 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts @@ -243,6 +243,7 @@ describe('import rules schema', () => { }).error ).toBeFalsy(); }); + test('You can send in an empty array to threats', () => { expect( importRulesSchema.validate>({ @@ -265,6 +266,7 @@ describe('import rules schema', () => { }).error ).toBeFalsy(); }); + test('[rule_id, description, from, to, index, name, severity, interval, type, filter, risk_score, output_index, threats] does validate', () => { expect( importRulesSchema.validate>({ @@ -360,8 +362,10 @@ describe('import rules schema', () => { query: 'some-query', language: 'kuery', references: [5], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "references" fails because ["references" at position 0 fails because ["0" must be a string]]' + ); }); test('indexes cannot be numbers', () => { @@ -382,8 +386,10 @@ describe('import rules schema', () => { type: 'query', query: 'some-query', language: 'kuery', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "index" fails because ["index" at position 0 fails because ["0" must be a string]]' + ); }); test('defaults interval to 5 min', () => { @@ -435,8 +441,8 @@ describe('import rules schema', () => { severity: 'severity', interval: '5m', type: 'saved_query', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "saved_id" fails because ["saved_id" is required]'); }); test('saved_id is required when type is saved_query and validates with it', () => { @@ -496,8 +502,8 @@ describe('import rules schema', () => { type: 'saved_query', saved_id: 'some id', filters: 'some string', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "filters" fails because ["filters" must be an array]'); }); test('language validates with kuery', () => { @@ -559,8 +565,8 @@ describe('import rules schema', () => { references: ['index-1'], query: 'some query', language: 'something-made-up', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "language" fails because ["language" must be one of [kuery, lucene]]'); }); test('max_signals cannot be negative', () => { @@ -581,8 +587,8 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', max_signals: -1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "max_signals" fails because ["max_signals" must be greater than 0]'); }); test('max_signals cannot be zero', () => { @@ -603,8 +609,8 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', max_signals: 0, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "max_signals" fails because ["max_signals" must be greater than 0]'); }); test('max_signals can be 1', () => { @@ -673,8 +679,10 @@ describe('import rules schema', () => { max_signals: 1, tags: [0, 1, 2], } - ).error - ).toBeTruthy(); + ).error.message + ).toEqual( + 'child "tags" fails because ["tags" at position 0 fails because ["0" must be a string]]' + ); }); test('You cannot send in an array of threats that are missing "framework"', () => { @@ -715,9 +723,12 @@ describe('import rules schema', () => { ], }, ], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "framework" fails because ["framework" is required]]]' + ); }); + test('You cannot send in an array of threats that are missing "tactic"', () => { expect( importRulesSchema.validate< @@ -752,9 +763,12 @@ describe('import rules schema', () => { ], }, ], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' + ); }); + test('You cannot send in an array of threats that are missing "techniques"', () => { expect( importRulesSchema.validate< @@ -787,8 +801,10 @@ describe('import rules schema', () => { }, }, ], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "techniques" fails because ["techniques" is required]]]' + ); }); test('You can optionally send in an array of false positives', () => { @@ -835,8 +851,10 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "false_positives" fails because ["false_positives" at position 0 fails because ["0" must be a string]]' + ); }); test('You can optionally set the immutable to be true', () => { @@ -883,8 +901,8 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "immutable" fails because ["immutable" must be a boolean]'); }); test('You cannot set the risk_score to 101', () => { @@ -906,8 +924,8 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "risk_score" fails because ["risk_score" must be less than 101]'); }); test('You cannot set the risk_score to -1', () => { @@ -929,8 +947,8 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', max_signals: 1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "risk_score" fails because ["risk_score" must be greater than -1]'); }); test('You can set the risk_score to 0', () => { @@ -1025,8 +1043,8 @@ describe('import rules schema', () => { language: 'kuery', max_signals: 1, meta: 'should not work', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "meta" fails because ["meta" must be an object]'); }); test('You can omit the query string when filters are present', () => { @@ -1093,8 +1111,8 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', timeline_id: 'some_id', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" is required]'); }); test('You cannot have a null value for timeline_title when timeline_id is present', () => { @@ -1116,8 +1134,8 @@ describe('import rules schema', () => { language: 'kuery', timeline_id: 'some_id', timeline_title: null, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" must be a string]'); }); test('You cannot have empty string for timeline_title when timeline_id is present', () => { @@ -1139,8 +1157,10 @@ describe('import rules schema', () => { language: 'kuery', timeline_id: 'some_id', timeline_title: '', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "timeline_title" fails because ["timeline_title" is not allowed to be empty]' + ); }); test('You cannot have timeline_title with an empty timeline_id', () => { @@ -1162,8 +1182,8 @@ describe('import rules schema', () => { language: 'kuery', timeline_id: '', timeline_title: 'some-title', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_id" fails because ["timeline_id" is not allowed to be empty]'); }); test('You cannot have timeline_title without timeline_id', () => { @@ -1184,8 +1204,8 @@ describe('import rules schema', () => { query: 'some query', language: 'kuery', timeline_title: 'some-title', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" is not allowed]'); }); test('rule_id is required and you cannot get by with just id', () => { @@ -1205,8 +1225,8 @@ describe('import rules schema', () => { references: ['index-1'], query: 'some query', language: 'kuery', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "rule_id" fails because ["rule_id" is required]'); }); test('it validates with created_at, updated_at, created_by, updated_by values', () => { @@ -1255,8 +1275,8 @@ describe('import rules schema', () => { updated_at: '2020-01-09T06:15:24.749Z', created_by: 'Braden Hassanabad', updated_by: 'Evan Hassanabad', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "created_at" fails because ["created_at" must be a valid ISO 8601 date]'); }); test('it does not validate with epoch strings for updated_at', () => { @@ -1280,8 +1300,8 @@ describe('import rules schema', () => { updated_at: '1578550728650', created_by: 'Braden Hassanabad', updated_by: 'Evan Hassanabad', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "updated_at" fails because ["updated_at" must be a valid ISO 8601 date]'); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/query_rules_bulk_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/query_rules_bulk_schema.test.ts index 6450da37699d8..ab1ffaab49165 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/query_rules_bulk_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/query_rules_bulk_schema.test.ts @@ -24,8 +24,10 @@ describe('query_rules_bulk_schema', () => { rule_id: '1', id: '1', }, - ]).error - ).toBeTruthy(); + ]).error.message + ).toEqual( + '"value" at position 0 fails because ["value" contains a conflict between exclusive peers [id, rule_id]]' + ); }); test('both rule_id and id being supplied do not validate if one array element works but the second does not', () => { @@ -38,8 +40,10 @@ describe('query_rules_bulk_schema', () => { rule_id: '1', id: '1', }, - ]).error - ).toBeTruthy(); + ]).error.message + ).toEqual( + '"value" at position 1 fails because ["value" contains a conflict between exclusive peers [id, rule_id]]' + ); }); test('only id validates', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/query_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/query_rules_schema.test.ts index 6c4e96abd2b98..c89d60e773a77 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/query_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/query_rules_schema.test.ts @@ -12,10 +12,11 @@ describe('queryRulesSchema', () => { expect(queryRulesSchema.validate>({}).error).toBeTruthy(); }); - test('both rule_id and id being supplied dot not validate', () => { + test('both rule_id and id being supplied do not validate', () => { expect( queryRulesSchema.validate>({ rule_id: '1', id: '1' }).error - ).toBeTruthy(); + .message + ).toEqual('"value" contains a conflict between exclusive peers [id, rule_id]'); }); test('only id validates', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/set_signal_status_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/set_signal_status_schema.test.ts index 792c7afad05b1..a6ba9b19a9d7d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/set_signal_status_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/set_signal_status_schema.test.ts @@ -30,24 +30,24 @@ describe('set signal status schema', () => { expect( setSignalsStatusSchema.validate>({ signal_ids: ['somefakeid'], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "status" fails because ["status" is required]'); }); test('query and missing status is invalid', () => { expect( setSignalsStatusSchema.validate>({ query: {}, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "status" fails because ["status" is required]'); }); test('status is present but query or signal_ids is missing is invalid', () => { expect( setSignalsStatusSchema.validate>({ status: 'closed', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('"value" must contain at least one of [signal_ids, query]'); }); test('signal_ids is present but status has wrong value', () => { @@ -60,7 +60,7 @@ describe('set signal status schema', () => { > >({ status: 'fakeVal', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "status" fails because ["status" must be one of [open, closed]]'); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts index f713840ab43f9..823ebb90a3b3c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts @@ -442,8 +442,10 @@ describe('update rules schema', () => { query: 'some-query', language: 'kuery', references: [5], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "references" fails because ["references" at position 0 fails because ["0" must be a string]]' + ); }); test('indexes cannot be numbers', () => { @@ -462,8 +464,10 @@ describe('update rules schema', () => { type: 'query', query: 'some-query', language: 'kuery', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "index" fails because ["index" at position 0 fails because ["0" must be a string]]' + ); }); test('saved_id is not required when type is saved_query and will validate without it', () => { @@ -570,8 +574,8 @@ describe('update rules schema', () => { references: ['index-1'], query: 'some query', language: 'something-made-up', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "language" fails because ["language" must be one of [kuery, lucene]]'); }); test('max_signals cannot be negative', () => { @@ -590,8 +594,8 @@ describe('update rules schema', () => { query: 'some query', language: 'kuery', max_signals: -1, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "max_signals" fails because ["max_signals" must be greater than 0]'); }); test('max_signals cannot be zero', () => { @@ -610,8 +614,8 @@ describe('update rules schema', () => { query: 'some query', language: 'kuery', max_signals: 0, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "max_signals" fails because ["max_signals" must be greater than 0]'); }); test('max_signals can be 1', () => { @@ -643,15 +647,15 @@ describe('update rules schema', () => { ).toBeFalsy(); }); - test('You update meta as a string', () => { + test('You cannot update meta as a string', () => { expect( updateRulesSchema.validate< Partial & { meta: string }> >({ id: 'rule-1', meta: 'should not work', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "meta" fails because ["meta" must be an object]'); }); test('filters cannot be a string', () => { @@ -662,8 +666,8 @@ describe('update rules schema', () => { rule_id: 'rule-1', type: 'query', filters: 'some string', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "filters" fails because ["filters" must be an array]'); }); test('threats is not defaulted to empty array on update', () => { @@ -706,6 +710,7 @@ describe('update rules schema', () => { }).value.threats ).toMatchObject([]); }); + test('threats is valid when updated with all sub-objects', () => { const expected: ThreatParams[] = [ { @@ -759,6 +764,7 @@ describe('update rules schema', () => { }).value.threats ).toMatchObject(expected); }); + test('threats is invalid when updated with missing property framework', () => { expect( updateRulesSchema.validate< @@ -795,9 +801,12 @@ describe('update rules schema', () => { ], }, ], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "framework" fails because ["framework" is required]]]' + ); }); + test('threats is invalid when updated with missing tactic sub-object', () => { expect( updateRulesSchema.validate< @@ -830,9 +839,12 @@ describe('update rules schema', () => { ], }, ], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "tactic" fails because ["tactic" is required]]]' + ); }); + test('threats is invalid when updated with missing techniques', () => { expect( updateRulesSchema.validate< @@ -863,8 +875,10 @@ describe('update rules schema', () => { }, }, ], - }).error - ).toBeTruthy(); + }).error.message + ).toEqual( + 'child "threats" fails because ["threats" at position 0 fails because [child "techniques" fails because ["techniques" is required]]]' + ); }); test('validates with timeline_id and timeline_title', () => { @@ -900,8 +914,8 @@ describe('update rules schema', () => { type: 'saved_query', saved_id: 'some id', timeline_id: 'some-id', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" is required]'); }); test('You cannot have a null value for timeline_title when timeline_id is present', () => { @@ -919,8 +933,8 @@ describe('update rules schema', () => { saved_id: 'some id', timeline_id: 'timeline-id', timeline_title: null, - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" must be a string]'); }); test('You cannot have empty string for timeline_title when timeline_id is present', () => { @@ -938,8 +952,8 @@ describe('update rules schema', () => { saved_id: 'some id', timeline_id: 'some-id', timeline_title: '', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" is not allowed to be empty]'); }); test('You cannot have timeline_title with an empty timeline_id', () => { @@ -957,8 +971,8 @@ describe('update rules schema', () => { saved_id: 'some id', timeline_id: '', timeline_title: 'some-title', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_id" fails because ["timeline_id" is not allowed to be empty]'); }); test('You cannot have timeline_title without timeline_id', () => { @@ -975,7 +989,7 @@ describe('update rules schema', () => { type: 'saved_query', saved_id: 'some id', timeline_title: 'some-title', - }).error - ).toBeTruthy(); + }).error.message + ).toEqual('child "timeline_title" fails because ["timeline_title" is not allowed]'); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts index 9c3188738faea..d363bfca98466 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.ts @@ -13,7 +13,6 @@ import { false_positives, filters, from, - immutable, index, rule_id, interval, @@ -46,7 +45,6 @@ export const updateRulesSchema = Joi.object({ from, rule_id, id, - immutable, index, interval, query: query.allow(''), diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts index 2b7ee443880e5..35e1e5933af64 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals.test.ts @@ -8,7 +8,6 @@ import { createMockServer } from '../__mocks__/_mock_server'; import { setSignalsStatusRoute } from './open_close_signals_route'; import * as myUtils from '../utils'; import { ServerInjectOptions } from 'hapi'; -import { ServerFacade } from '../../../../types'; import { getSetSignalStatusByIdsRequest, @@ -29,7 +28,7 @@ describe('set signal status', () => { elasticsearch.getCluster = jest.fn(() => ({ callWithRequest: jest.fn(() => true), })); - setSignalsStatusRoute((server as unknown) as ServerFacade); + setSignalsStatusRoute(server); }); describe('status on signal', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts index aef499675b884..5b86d0a4b36c0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.test.ts @@ -8,7 +8,6 @@ import { createMockServer } from '../__mocks__/_mock_server'; import { querySignalsRoute } from './query_signals_route'; import * as myUtils from '../utils'; import { ServerInjectOptions } from 'hapi'; -import { ServerFacade } from '../../../../types'; import { getSignalsQueryRequest, @@ -28,7 +27,7 @@ describe('query for signal', () => { elasticsearch.getCluster = jest.fn(() => ({ callWithRequest: jest.fn(() => true), })); - querySignalsRoute((server as unknown) as ServerFacade); + querySignalsRoute(server); }); describe('query and agg on signals index', () => { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_internet_explorer.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_internet_explorer.json deleted file mode 100644 index bb9d8c60040f6..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_internet_explorer.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "description": "Command shell started by Internet Explorer", - "enabled": false, - "filters": [ - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "key": "process.name", - "negate": false, - "params": { - "query": "cmd.exe" - }, - "type": "phrase", - "value": "cmd.exe" - }, - "query": { - "match": { - "process.name": { - "query": "cmd.exe", - "type": "phrase" - } - } - } - }, - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", - "key": "event.action", - "negate": false, - "params": { - "query": "Process Create (rule: ProcessCreate)" - }, - "type": "phrase", - "value": "Process Create (rule: ProcessCreate)" - }, - "query": { - "match": { - "event.action": { - "query": "Process Create (rule: ProcessCreate)", - "type": "phrase" - } - } - } - } - ], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Command shell started by Internet Explorer", - "query": "process.parent.name:iexplore.exe", - "risk_score": 50, - "rule_id": "a0b554d2-85ed-4998-ada3-4ca58b508b35", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_powershell.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_powershell.json deleted file mode 100644 index d9820f90c55ee..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_powershell.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "description": "Command shell started by Powershell", - "enabled": false, - "filters": [ - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "key": "process.name", - "negate": false, - "params": { - "query": "cmd.exe" - }, - "type": "phrase", - "value": "cmd.exe" - }, - "query": { - "match": { - "process.name": { - "query": "cmd.exe", - "type": "phrase" - } - } - } - }, - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", - "key": "event.action", - "negate": false, - "params": { - "query": "Process Create (rule: ProcessCreate)" - }, - "type": "phrase", - "value": "Process Create (rule: ProcessCreate)" - }, - "query": { - "match": { - "event.action": { - "query": "Process Create (rule: ProcessCreate)", - "type": "phrase" - } - } - } - } - ], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Command shell started by Powershell", - "query": "process.parent.name:powershell.exe", - "risk_score": 50, - "rule_id": "ab4bbfa5-4127-40bf-852f-bdc6afdb2a06", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_svchost.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_svchost.json deleted file mode 100644 index a11f69fc3048f..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/command_shell_started_by_svchost.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "description": "Command shell started by Svchost", - "enabled": false, - "filters": [ - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "key": "process.name", - "negate": false, - "params": { - "query": "cmd.exe" - }, - "type": "phrase", - "value": "cmd.exe" - }, - "query": { - "match": { - "process.name": { - "query": "cmd.exe", - "type": "phrase" - } - } - } - }, - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", - "key": "event.action", - "negate": false, - "params": { - "query": "Process Create (rule: ProcessCreate)" - }, - "type": "phrase", - "value": "Process Create (rule: ProcessCreate)" - }, - "query": { - "match": { - "event.action": { - "query": "Process Create (rule: ProcessCreate)", - "type": "phrase" - } - } - } - } - ], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Command shell started by Svchost", - "query": "process.parent.name:svchost.exe", - "risk_score": 50, - "rule_id": "2e4f8a5e-ce68-44e0-9243-1f57d44c4f30", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_network_detect_large_outbound_icmp_packets.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_network_detect_large_outbound_icmp_packets.json deleted file mode 100644 index faa1c97e4bada..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_network_detect_large_outbound_icmp_packets.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Network - Detect Large Outbound ICMP Packets", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Network - Detect Large Outbound ICMP Packets", - "query": "network.transport:icmp and network.bytes>1000 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", - "risk_score": 50, - "rule_id": "4fce2a7e-0e11-4f17-bae3-8873c5ae62be", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_network_detect_long_dns_txt_record_response.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_network_detect_long_dns_txt_record_response.json deleted file mode 100644 index f034e4999107f..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_network_detect_long_dns_txt_record_response.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Network - Detect Long DNS TXT Record Response", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Network - Detect Long DNS TXT Record Response", - "query": "network.protocol:dns and server.bytes>100 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16 and not destination.ip:169.254.169.254 and not destination.ip:127.0.0.53", - "risk_score": 50, - "rule_id": "cc28f445-318e-4850-8b0d-5ad53eaded74", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_network_protocols_passing_authentication_in_cleartext.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_network_protocols_passing_authentication_in_cleartext.json deleted file mode 100644 index d1b5f6be75040..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_network_protocols_passing_authentication_in_cleartext.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Network - Protocols passing authentication in cleartext", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Network - Protocols passing authentication in cleartext", - "query": "destination.port:(21 or 23 or 110 or 143) and network.transport:tcp", - "risk_score": 50, - "rule_id": "31f32b3c-415a-4a18-b60f-5748a337246b", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_child_processes_of_spoolsvexe.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_child_processes_of_spoolsvexe.json deleted file mode 100644 index 60d5ffe918585..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_child_processes_of_spoolsvexe.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Windows - Child Processes of Spoolsv.exe", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Windows - Child Processes of Spoolsv.exe", - "query": "process.parent.name:spoolsv.exe and not process.name:regsvr32.exe ", - "risk_score": 50, - "rule_id": "dcc45d35-f42e-4f97-81e8-90b0597ea0d1", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_detect_new_local_admin_account.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_detect_new_local_admin_account.json deleted file mode 100644 index ca27234b0d8ae..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_detect_new_local_admin_account.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Windows - Detect New Local Admin account", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Windows - Detect New Local Admin account", - "query": "event.code:(4720 or 4732) and winlog.event_data.TargetUserName:Administrators", - "risk_score": 50, - "rule_id": "461db51b-b1a1-49de-ac63-e1bcbd445602", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_detect_psexec_with_accepteula_flag.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_detect_psexec_with_accepteula_flag.json deleted file mode 100644 index 25dcd8234e092..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_detect_psexec_with_accepteula_flag.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Windows - Detect PsExec With accepteula Flag", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Windows - Detect PsExec With accepteula Flag", - "query": "process.name:PsExec.exe and process.args:\"-accepteula\"", - "risk_score": 50, - "rule_id": "304b0e0c-bd06-46f8-aeda-2e719ae434d1", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_detect_use_of_cmdexe_to_launch_script_interpreters.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_detect_use_of_cmdexe_to_launch_script_interpreters.json deleted file mode 100644 index 70d06ca9a4777..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_detect_use_of_cmdexe_to_launch_script_interpreters.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Windows - Detect Use of cmd.exe to Launch Script Interpreters", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Windows - Detect Use of cmd.exe to Launch Script Interpreters", - "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:(\"wscript.exe\" or \"cscript.exe\") and process.parent.name:\"cmd.exe\"", - "risk_score": 50, - "rule_id": "b17c215e-8fa5-4087-b8d1-87761a90d710", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_new_external_device.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_new_external_device.json deleted file mode 100644 index 9dbc8d7cbb7ed..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_new_external_device.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Windows - New External Device Attached", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Windows - New External Device Attached", - "query": "event.code:6416", - "risk_score": 50, - "rule_id": "c0747553-5763-5d85-cd97-898f2daa2bde", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_processes_created_by_netsh.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_processes_created_by_netsh.json deleted file mode 100644 index 3f4e1a6243a96..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_processes_created_by_netsh.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Windows - Processes created by netsh", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Windows - Processes created by netsh", - "query": "process.parent.name:netsh.exe", - "risk_score": 50, - "rule_id": "e312dd9e-4760-4a71-a241-9b9a835a51c4", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_processes_launching_netsh.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_processes_launching_netsh.json deleted file mode 100644 index 34d08d7596e11..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_processes_launching_netsh.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Windows - Processes launching netsh", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Windows - Processes launching netsh", - "query": "process.name:netsh.exe and event.action:\"Process Create (rule: ProcessCreate)\" ", - "risk_score": 50, - "rule_id": "3b8db8aa-5734-405e-8dda-703129078a35", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_windows_event_log_cleared.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_windows_event_log_cleared.json deleted file mode 100644 index bd82247203f00..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/ece_windows_windows_event_log_cleared.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Windows - Windows Event Log Cleared", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Windows - Windows Event Log Cleared", - "query": "event.code:(1102 or 1100)", - "risk_score": 50, - "rule_id": "b94b5177-ca7f-468a-9a1d-aef39c30a3ae", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/index.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/index.ts index 8a353e4b2b301..6ef81addd846e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/index.ts @@ -10,342 +10,306 @@ import rule1 from './403_response_to_a_post.json'; import rule2 from './405_response_method_not_allowed.json'; import rule3 from './500_response_on_admin_page.json'; -import rule4 from './command_shell_started_by_internet_explorer.json'; -import rule5 from './command_shell_started_by_powershell.json'; -import rule6 from './command_shell_started_by_svchost.json'; -import rule7 from './ece_network_detect_large_outbound_icmp_packets.json'; -import rule8 from './ece_network_detect_long_dns_txt_record_response.json'; -import rule9 from './ece_network_protocols_passing_authentication_in_cleartext.json'; -import rule10 from './ece_windows_child_processes_of_spoolsvexe.json'; -import rule11 from './ece_windows_detect_new_local_admin_account.json'; -import rule12 from './ece_windows_detect_psexec_with_accepteula_flag.json'; -import rule13 from './ece_windows_detect_use_of_cmdexe_to_launch_script_interpreters.json'; -import rule14 from './ece_windows_new_external_device.json'; -import rule15 from './ece_windows_processes_created_by_netsh.json'; -import rule16 from './ece_windows_processes_launching_netsh.json'; -import rule17 from './ece_windows_windows_event_log_cleared.json'; -import rule18 from './eql_adding_the_hidden_file_attribute_with_via_attribexe.json'; -import rule19 from './eql_adobe_hijack_persistence.json'; -import rule20 from './eql_audio_capture_via_powershell.json'; -import rule21 from './eql_audio_capture_via_soundrecorder.json'; -import rule22 from './eql_bypass_uac_event_viewer.json'; -import rule23 from './eql_bypass_uac_via_cmstp.json'; -import rule24 from './eql_bypass_uac_via_sdclt.json'; -import rule25 from './eql_clearing_windows_event_logs.json'; -import rule26 from './eql_delete_volume_usn_journal_with_fsutil.json'; -import rule27 from './eql_deleting_backup_catalogs_with_wbadmin.json'; -import rule28 from './eql_direct_outbound_smb_connection.json'; -import rule29 from './eql_disable_windows_firewall_rules_with_netsh.json'; -import rule30 from './eql_dll_search_order_hijack.json'; -import rule31 from './eql_encoding_or_decoding_files_via_certutil.json'; -import rule32 from './eql_local_scheduled_task_commands.json'; -import rule33 from './eql_local_service_commands.json'; -import rule34 from './eql_modification_of_boot_configuration.json'; -import rule35 from './eql_msbuild_making_network_connections.json'; -import rule36 from './eql_mshta_making_network_connections.json'; -import rule37 from './eql_msxsl_making_network_connections.json'; -import rule38 from './eql_psexec_lateral_movement_command.json'; -import rule39 from './eql_suspicious_ms_office_child_process.json'; -import rule40 from './eql_suspicious_ms_outlook_child_process.json'; -import rule41 from './eql_suspicious_pdf_reader_child_process.json'; -import rule42 from './eql_system_shells_via_services.json'; -import rule43 from './eql_unusual_network_connection_via_rundll32.json'; -import rule44 from './eql_unusual_parentchild_relationship.json'; -import rule45 from './eql_unusual_process_network_connection.json'; -import rule46 from './eql_user_account_creation.json'; -import rule47 from './eql_user_added_to_administrator_group.json'; -import rule48 from './eql_volume_shadow_copy_deletion_via_vssadmin.json'; -import rule49 from './eql_volume_shadow_copy_deletion_via_wmic.json'; -import rule50 from './eql_windows_script_executing_powershell.json'; -import rule51 from './eql_wmic_command_lateral_movement.json'; -import rule52 from './linux_hping_activity.json'; -import rule53 from './linux_iodine_activity.json'; -import rule54 from './linux_java_process_connecting_to_the_internet.json'; -import rule55 from './linux_kernel_module_activity.json'; -import rule56 from './linux_ldso_process_activity.json'; -import rule57 from './linux_lzop_activity.json'; -import rule58 from './linux_lzop_activity_possible_julianrunnels.json'; -import rule59 from './linux_mknod_activity.json'; -import rule60 from './linux_netcat_network_connection.json'; -import rule61 from './linux_network_anomalous_process_using_https_ports.json'; -import rule62 from './linux_nmap_activity.json'; -import rule63 from './linux_nping_activity.json'; -import rule64 from './linux_process_started_in_temp_directory.json'; -import rule65 from './linux_ptrace_activity.json'; -import rule66 from './linux_rawshark_activity.json'; -import rule67 from './linux_shell_activity_by_web_server.json'; -import rule68 from './linux_socat_activity.json'; -import rule69 from './linux_ssh_forwarding.json'; -import rule70 from './linux_strace_activity.json'; -import rule71 from './linux_tcpdump_activity.json'; -import rule72 from './linux_unusual_shell_activity.json'; -import rule73 from './linux_web_download.json'; -import rule74 from './linux_whoami_commmand.json'; -import rule75 from './network_dns_directly_to_the_internet.json'; -import rule76 from './network_ftp_file_transfer_protocol_activity_to_the_internet.json'; -import rule77 from './network_irc_internet_relay_chat_protocol_activity_to_the_internet.json'; -import rule78 from './network_nat_traversal_port_activity.json'; -import rule79 from './network_port_26_activity.json'; -import rule80 from './network_port_8000_activity.json'; -import rule81 from './network_port_8000_activity_to_the_internet.json'; -import rule82 from './network_pptp_point_to_point_tunneling_protocol_activity.json'; -import rule83 from './network_proxy_port_activity_to_the_internet.json'; -import rule84 from './network_rdp_remote_desktop_protocol_from_the_internet.json'; -import rule85 from './network_rdp_remote_desktop_protocol_to_the_internet.json'; -import rule86 from './network_rpc_remote_procedure_call_from_the_internet.json'; -import rule87 from './network_rpc_remote_procedure_call_to_the_internet.json'; -import rule88 from './network_smb_windows_file_sharing_activity_to_the_internet.json'; -import rule89 from './network_smtp_to_the_internet.json'; -import rule90 from './network_sql_server_port_activity_to_the_internet.json'; -import rule91 from './network_ssh_secure_shell_from_the_internet.json'; -import rule92 from './network_ssh_secure_shell_to_the_internet.json'; -import rule93 from './network_telnet_port_activity.json'; -import rule94 from './network_tor_activity_to_the_internet.json'; -import rule95 from './network_vnc_virtual_network_computing_from_the_internet.json'; -import rule96 from './network_vnc_virtual_network_computing_to_the_internet.json'; -import rule97 from './null_user_agent.json'; -import rule98 from './powershell_network_connection.json'; -import rule99 from './process_execution_via_wmi.json'; -import rule100 from './process_started_by_acrobat_reader_possible_payload.json'; -import rule101 from './process_started_by_ms_office_program_possible_payload.json'; -import rule102 from './process_started_by_windows_defender.json'; -import rule103 from './psexec_activity.json'; -import rule104 from './search_windows_10.json'; -import rule105 from './splunk_child_processes_of_spoolsvexe.json'; -import rule106 from './splunk_detect_large_outbound_icmp_packets.json'; -import rule107 from './splunk_detect_long_dns_txt_record_response.json'; -import rule108 from './splunk_detect_new_local_admin_account.json'; -import rule109 from './splunk_detect_psexec_with_accepteula_flag.json'; -import rule110 from './splunk_detect_use_of_cmdexe_to_launch_script_interpreters.json'; -import rule111 from './splunk_processes_created_by_netsh.json'; -import rule112 from './splunk_processes_launching_netsh.json'; -import rule113 from './splunk_protocols_passing_authentication_in_cleartext.json'; -import rule114 from './splunk_windows_event_log_cleared.json'; -import rule115 from './sqlmap_user_agent.json'; -import rule116 from './suricata_base64_encoded_invokecommand_powershell_execution.json'; -import rule117 from './suricata_base64_encoded_newobject_powershell_execution.json'; -import rule118 from './suricata_base64_encoded_startprocess_powershell_execution.json'; -import rule119 from './suricata_category_a_suspicious_string_was_detected.json'; -import rule120 from './suricata_category_attempted_administrator_privilege_gain.json'; -import rule121 from './suricata_category_attempted_denial_of_service.json'; -import rule122 from './suricata_category_attempted_information_leak.json'; -import rule123 from './suricata_category_attempted_login_with_suspicious_username.json'; -import rule124 from './suricata_category_attempted_user_privilege_gain.json'; -import rule125 from './suricata_category_client_using_unusual_port.json'; -import rule126 from './suricata_category_crypto_currency_mining_activity.json'; -import rule127 from './suricata_category_decode_of_an_rpc_query.json'; -import rule128 from './suricata_category_default_username_and_password_login_attempt.json'; -import rule129 from './suricata_category_denial_of_service.json'; -import rule130 from './suricata_category_denial_of_service_attack.json'; -import rule131 from './suricata_category_executable_code_was_detected.json'; -import rule132 from './suricata_category_exploit_kit_activity.json'; -import rule133 from './suricata_category_external_ip_address_retrieval.json'; -import rule134 from './suricata_category_generic_icmp_event.json'; -import rule135 from './suricata_category_generic_protocol_command_decode.json'; -import rule136 from './suricata_category_information_leak.json'; -import rule137 from './suricata_category_large_scale_information_leak.json'; -import rule138 from './suricata_category_malware_command_and_control_activity.json'; -import rule139 from './suricata_category_misc_activity.json'; -import rule140 from './suricata_category_misc_attack.json'; -import rule141 from './suricata_category_network_scan_detected.json'; -import rule142 from './suricata_category_network_trojan_detected.json'; -import rule143 from './suricata_category_nonstandard_protocol_or_event.json'; -import rule144 from './suricata_category_not_suspicious_traffic.json'; -import rule145 from './suricata_category_observed_c2_domain.json'; -import rule146 from './suricata_category_possible_social_engineering_attempted.json'; -import rule147 from './suricata_category_possibly_unwanted_program.json'; -import rule148 from './suricata_category_potential_corporate_privacy_violation.json'; -import rule149 from './suricata_category_potentially_bad_traffic.json'; -import rule150 from './suricata_category_potentially_vulnerable_web_application_access.json'; -import rule151 from './suricata_category_successful_administrator_privilege_gain.json'; -import rule152 from './suricata_category_successful_credential_theft.json'; -import rule153 from './suricata_category_successful_user_privilege_gain.json'; -import rule154 from './suricata_category_suspicious_filename_detected.json'; -import rule155 from './suricata_category_system_call_detected.json'; -import rule156 from './suricata_category_targeted_malicious_activity.json'; -import rule157 from './suricata_category_tcp_connection_detected.json'; -import rule158 from './suricata_category_unknown_traffic.json'; -import rule159 from './suricata_category_unsuccessful_user_privilege_gain.json'; -import rule160 from './suricata_category_web_application_attack.json'; -import rule161 from './suricata_cobaltstrike_artifact_in_an_dns_request.json'; -import rule162 from './suricata_commonly_abused_dns_domain_detected.json'; -import rule163 from './suricata_directory_reversal_characters_in_an_http_request.json'; -import rule164 from './suricata_directory_traversal_characters_in_an_http_request.json'; -import rule165 from './suricata_directory_traversal_characters_in_http_response.json'; -import rule166 from './suricata_directory_traversal_in_downloaded_zip_file.json'; -import rule167 from './suricata_dns_traffic_on_unusual_tcp_port.json'; -import rule168 from './suricata_dns_traffic_on_unusual_udp_port.json'; -import rule169 from './suricata_double_encoded_characters_in_a_uri.json'; -import rule170 from './suricata_double_encoded_characters_in_an_http_post.json'; -import rule171 from './suricata_double_encoded_characters_in_http_request.json'; -import rule172 from './suricata_eval_php_function_in_an_http_request.json'; -import rule173 from './suricata_exploit_cve_2018_1000861.json'; -import rule174 from './suricata_exploit_cve_2019_0227.json'; -import rule175 from './suricata_exploit_cve_2019_0232.json'; -import rule176 from './suricata_exploit_cve_2019_0604.json'; -import rule177 from './suricata_exploit_cve_2019_0708.json'; -import rule178 from './suricata_exploit_cve_2019_0752.json'; -import rule179 from './suricata_exploit_cve_2019_1003000.json'; -import rule180 from './suricata_exploit_cve_2019_10149.json'; -import rule181 from './suricata_exploit_cve_2019_11043.json'; -import rule182 from './suricata_exploit_cve_2019_11510.json'; -import rule183 from './suricata_exploit_cve_2019_11580.json'; -import rule184 from './suricata_exploit_cve_2019_11581.json'; -import rule185 from './suricata_exploit_cve_2019_13450.json'; -import rule186 from './suricata_exploit_cve_2019_13505.json'; -import rule187 from './suricata_exploit_cve_2019_15107.json'; -import rule188 from './suricata_exploit_cve_2019_15846.json'; -import rule189 from './suricata_exploit_cve_2019_16072.json'; -import rule190 from './suricata_exploit_cve_2019_1652.json'; -import rule191 from './suricata_exploit_cve_2019_16662.json'; -import rule192 from './suricata_exploit_cve_2019_16759.json'; -import rule193 from './suricata_exploit_cve_2019_16928.json'; -import rule194 from './suricata_exploit_cve_2019_17270.json'; -import rule195 from './suricata_exploit_cve_2019_1821.json'; -import rule196 from './suricata_exploit_cve_2019_19781.json'; -import rule197 from './suricata_exploit_cve_2019_2618.json'; -import rule198 from './suricata_exploit_cve_2019_2725.json'; -import rule199 from './suricata_exploit_cve_2019_3396.json'; -import rule200 from './suricata_exploit_cve_2019_3929.json'; -import rule201 from './suricata_exploit_cve_2019_5533.json'; -import rule202 from './suricata_exploit_cve_2019_6340.json'; -import rule203 from './suricata_exploit_cve_2019_7256.json'; -import rule204 from './suricata_exploit_cve_2019_9978.json'; -import rule205 from './suricata_ftp_traffic_on_unusual_port_internet_destination.json'; -import rule206 from './suricata_http_traffic_on_unusual_port_internet_destination.json'; -import rule207 from './suricata_imap_traffic_on_unusual_port_internet_destination.json'; -import rule208 from './suricata_lazagne_artifact_in_an_http_post.json'; -import rule209 from './suricata_mimikatz_artifacts_in_an_http_post.json'; -import rule210 from './suricata_mimikatz_string_detected_in_http_response.json'; -import rule211 from './suricata_nondns_traffic_on_tcp_port_53.json'; -import rule212 from './suricata_nondns_traffic_on_udp_port_53.json'; -import rule213 from './suricata_nonftp_traffic_on_port_21.json'; -import rule214 from './suricata_nonhttp_traffic_on_tcp_port_80.json'; -import rule215 from './suricata_nonimap_traffic_on_port_1443_imap.json'; -import rule216 from './suricata_nonsmb_traffic_on_tcp_port_139_smb.json'; -import rule217 from './suricata_nonssh_traffic_on_port_22.json'; -import rule218 from './suricata_nontls_on_tls_port.json'; -import rule219 from './suricata_possible_cobalt_strike_malleable_c2_null_response.json'; -import rule220 from './suricata_possible_sql_injection_sql_commands_in_http_transactions.json'; -import rule221 from './suricata_rpc_traffic_on_http_ports.json'; -import rule222 from './suricata_serialized_php_detected.json'; -import rule223 from './suricata_shell_exec_php_function_in_an_http_post.json'; -import rule224 from './suricata_ssh_traffic_not_on_port_22_internet_destination.json'; -import rule225 from './suricata_tls_traffic_on_unusual_port_internet_destination.json'; -import rule226 from './suricata_windows_executable_served_by_jpeg_web_content.json'; -import rule227 from './suspicious_process_started_by_a_script.json'; -import rule228 from './windows_background_intelligent_transfer_service_bits_connecting_to_the_internet.json'; -import rule229 from './windows_burp_ce_activity.json'; -import rule230 from './windows_certutil_connecting_to_the_internet.json'; -import rule231 from './windows_command_prompt_connecting_to_the_internet.json'; -import rule232 from './windows_command_shell_started_by_internet_explorer.json'; -import rule233 from './windows_command_shell_started_by_powershell.json'; -import rule234 from './windows_command_shell_started_by_svchost.json'; -import rule235 from './windows_credential_dumping_commands.json'; -import rule236 from './windows_credential_dumping_via_imageload.json'; -import rule237 from './windows_credential_dumping_via_registry_save.json'; -import rule238 from './windows_data_compression_using_powershell.json'; -import rule239 from './windows_defense_evasion_decoding_using_certutil.json'; -import rule240 from './windows_defense_evasion_or_persistence_via_hidden_files.json'; -import rule241 from './windows_defense_evasion_via_filter_manager.json'; -import rule242 from './windows_defense_evasion_via_windows_event_log_tools.json'; -import rule243 from './windows_execution_via_compiled_html_file.json'; -import rule244 from './windows_execution_via_connection_manager.json'; -import rule245 from './windows_execution_via_microsoft_html_application_hta.json'; -import rule246 from './windows_execution_via_net_com_assemblies.json'; -import rule247 from './windows_execution_via_regsvr32.json'; -import rule248 from './windows_execution_via_trusted_developer_utilities.json'; -import rule249 from './windows_html_help_executable_program_connecting_to_the_internet.json'; -import rule250 from './windows_image_load_from_a_temp_directory.json'; -import rule251 from './windows_indirect_command_execution.json'; -import rule252 from './windows_iodine_activity.json'; -import rule253 from './windows_management_instrumentation_wmi_execution.json'; -import rule254 from './windows_microsoft_html_application_hta_connecting_to_the_internet.json'; -import rule255 from './windows_mimikatz_activity.json'; -import rule256 from './windows_misc_lolbin_connecting_to_the_internet.json'; -import rule257 from './windows_net_command_activity_by_the_system_account.json'; -import rule258 from './windows_net_user_command_activity.json'; -import rule259 from './windows_netcat_activity.json'; -import rule260 from './windows_netcat_network_activity.json'; -import rule261 from './windows_network_anomalous_windows_process_using_https_ports.json'; -import rule262 from './windows_nmap_activity.json'; -import rule263 from './windows_nmap_scan_activity.json'; -import rule264 from './windows_payload_obfuscation_via_certutil.json'; -import rule265 from './windows_persistence_or_priv_escalation_via_hooking.json'; -import rule266 from './windows_persistence_via_application_shimming.json'; -import rule267 from './windows_persistence_via_bits_jobs.json'; -import rule268 from './windows_persistence_via_modification_of_existing_service.json'; -import rule269 from './windows_persistence_via_netshell_helper_dll.json'; -import rule270 from './windows_powershell_connecting_to_the_internet.json'; -import rule271 from './windows_priv_escalation_via_accessibility_features.json'; -import rule272 from './windows_process_discovery_via_tasklist_command.json'; -import rule273 from './windows_process_execution_via_wmi.json'; -import rule274 from './windows_process_started_by_acrobat_reader_possible_payload.json'; -import rule275 from './windows_process_started_by_ms_office_program_possible_payload.json'; -import rule276 from './windows_process_started_by_the_java_runtime.json'; -import rule277 from './windows_psexec_activity.json'; -import rule278 from './windows_register_server_program_connecting_to_the_internet.json'; -import rule279 from './windows_registry_query_local.json'; -import rule280 from './windows_registry_query_network.json'; -import rule281 from './windows_remote_management_execution.json'; -import rule282 from './windows_scheduled_task_activity.json'; -import rule283 from './windows_script_interpreter_connecting_to_the_internet.json'; -import rule284 from './windows_signed_binary_proxy_execution.json'; -import rule285 from './windows_signed_binary_proxy_execution_download.json'; -import rule286 from './windows_suspicious_process_started_by_a_script.json'; -import rule287 from './windows_whoami_command_activity.json'; -import rule288 from './windows_windump_activity.json'; -import rule289 from './windows_wireshark_activity.json'; -import rule290 from './windump_activity.json'; -import rule291 from './zeek_notice_capturelosstoo_much_loss.json'; -import rule292 from './zeek_notice_conncontent_gap.json'; -import rule293 from './zeek_notice_connretransmission_inconsistency.json'; -import rule294 from './zeek_notice_dnsexternal_name.json'; -import rule295 from './zeek_notice_ftpbruteforcing.json'; -import rule296 from './zeek_notice_ftpsite_exec_success.json'; -import rule297 from './zeek_notice_heartbleedssl_heartbeat_attack.json'; -import rule298 from './zeek_notice_heartbleedssl_heartbeat_attack_success.json'; -import rule299 from './zeek_notice_heartbleedssl_heartbeat_many_requests.json'; -import rule300 from './zeek_notice_heartbleedssl_heartbeat_odd_length.json'; -import rule301 from './zeek_notice_httpsql_injection_attacker.json'; -import rule302 from './zeek_notice_httpsql_injection_victim.json'; -import rule303 from './zeek_notice_intelnotice.json'; -import rule304 from './zeek_notice_noticetally.json'; -import rule305 from './zeek_notice_packetfiltercannot_bpf_shunt_conn.json'; -import rule306 from './zeek_notice_packetfiltercompile_failure.json'; -import rule307 from './zeek_notice_packetfilterdropped_packets.json'; -import rule308 from './zeek_notice_packetfilterinstall_failure.json'; -import rule309 from './zeek_notice_packetfilterno_more_conn_shunts_available.json'; -import rule310 from './zeek_notice_packetfiltertoo_long_to_compile_filter.json'; -import rule311 from './zeek_notice_protocoldetectorprotocol_found.json'; -import rule312 from './zeek_notice_protocoldetectorserver_found.json'; -import rule313 from './zeek_notice_scanaddress_scan.json'; -import rule314 from './zeek_notice_scanport_scan.json'; -import rule315 from './zeek_notice_signaturescount_signature.json'; -import rule316 from './zeek_notice_signaturesmultiple_sig_responders.json'; -import rule317 from './zeek_notice_signaturesmultiple_signatures.json'; -import rule318 from './zeek_notice_signaturessensitive_signature.json'; -import rule319 from './zeek_notice_signaturessignature_summary.json'; -import rule320 from './zeek_notice_smtpblocklist_blocked_host.json'; -import rule321 from './zeek_notice_smtpblocklist_error_message.json'; -import rule322 from './zeek_notice_smtpsuspicious_origination.json'; -import rule323 from './zeek_notice_softwaresoftware_version_change.json'; -import rule324 from './zeek_notice_softwarevulnerable_version.json'; -import rule325 from './zeek_notice_sshinteresting_hostname_login.json'; -import rule326 from './zeek_notice_sshlogin_by_password_guesser.json'; -import rule327 from './zeek_notice_sshpassword_guessing.json'; -import rule328 from './zeek_notice_sshwatched_country_login.json'; -import rule329 from './zeek_notice_sslcertificate_expired.json'; -import rule330 from './zeek_notice_sslcertificate_expires_soon.json'; -import rule331 from './zeek_notice_sslcertificate_not_valid_yet.json'; -import rule332 from './zeek_notice_sslinvalid_ocsp_response.json'; -import rule333 from './zeek_notice_sslinvalid_server_cert.json'; -import rule334 from './zeek_notice_sslold_version.json'; -import rule335 from './zeek_notice_sslweak_cipher.json'; -import rule336 from './zeek_notice_sslweak_key.json'; -import rule337 from './zeek_notice_teamcymrumalwarehashregistrymatch.json'; -import rule338 from './zeek_notice_traceroutedetected.json'; -import rule339 from './zeek_notice_weirdactivity.json'; +import rule4 from './eql_adding_the_hidden_file_attribute_with_via_attribexe.json'; +import rule5 from './eql_adobe_hijack_persistence.json'; +import rule6 from './eql_audio_capture_via_powershell.json'; +import rule7 from './eql_audio_capture_via_soundrecorder.json'; +import rule8 from './eql_bypass_uac_event_viewer.json'; +import rule9 from './eql_bypass_uac_via_cmstp.json'; +import rule10 from './eql_bypass_uac_via_sdclt.json'; +import rule11 from './eql_clearing_windows_event_logs.json'; +import rule12 from './eql_delete_volume_usn_journal_with_fsutil.json'; +import rule13 from './eql_deleting_backup_catalogs_with_wbadmin.json'; +import rule14 from './eql_direct_outbound_smb_connection.json'; +import rule15 from './eql_disable_windows_firewall_rules_with_netsh.json'; +import rule16 from './eql_dll_search_order_hijack.json'; +import rule17 from './eql_encoding_or_decoding_files_via_certutil.json'; +import rule18 from './eql_local_scheduled_task_commands.json'; +import rule19 from './eql_local_service_commands.json'; +import rule20 from './eql_modification_of_boot_configuration.json'; +import rule21 from './eql_msbuild_making_network_connections.json'; +import rule22 from './eql_mshta_making_network_connections.json'; +import rule23 from './eql_msxsl_making_network_connections.json'; +import rule24 from './eql_psexec_lateral_movement_command.json'; +import rule25 from './eql_suspicious_ms_office_child_process.json'; +import rule26 from './eql_suspicious_ms_outlook_child_process.json'; +import rule27 from './eql_suspicious_pdf_reader_child_process.json'; +import rule28 from './eql_system_shells_via_services.json'; +import rule29 from './eql_unusual_network_connection_via_rundll32.json'; +import rule30 from './eql_unusual_parentchild_relationship.json'; +import rule31 from './eql_unusual_process_network_connection.json'; +import rule32 from './eql_user_account_creation.json'; +import rule33 from './eql_user_added_to_administrator_group.json'; +import rule34 from './eql_volume_shadow_copy_deletion_via_vssadmin.json'; +import rule35 from './eql_volume_shadow_copy_deletion_via_wmic.json'; +import rule36 from './eql_windows_script_executing_powershell.json'; +import rule37 from './eql_wmic_command_lateral_movement.json'; +import rule38 from './linux_hping_activity.json'; +import rule39 from './linux_iodine_activity.json'; +import rule40 from './linux_kernel_module_activity.json'; +import rule41 from './linux_ldso_process_activity.json'; +import rule42 from './linux_lzop_activity.json'; +import rule43 from './linux_mknod_activity.json'; +import rule44 from './linux_netcat_network_connection.json'; +import rule45 from './linux_network_anomalous_process_using_https_ports.json'; +import rule46 from './linux_nmap_activity.json'; +import rule47 from './linux_nping_activity.json'; +import rule48 from './linux_process_started_in_temp_directory.json'; +import rule49 from './linux_ptrace_activity.json'; +import rule50 from './linux_rawshark_activity.json'; +import rule51 from './linux_shell_activity_by_web_server.json'; +import rule52 from './linux_socat_activity.json'; +import rule53 from './linux_ssh_forwarding.json'; +import rule54 from './linux_strace_activity.json'; +import rule55 from './linux_tcpdump_activity.json'; +import rule56 from './linux_web_download.json'; +import rule57 from './linux_whoami_commmand.json'; +import rule58 from './network_dns_directly_to_the_internet.json'; +import rule59 from './network_ftp_file_transfer_protocol_activity_to_the_internet.json'; +import rule60 from './network_irc_internet_relay_chat_protocol_activity_to_the_internet.json'; +import rule61 from './network_nat_traversal_port_activity.json'; +import rule62 from './network_port_26_activity.json'; +import rule63 from './network_port_8000_activity.json'; +import rule64 from './network_port_8000_activity_to_the_internet.json'; +import rule65 from './network_pptp_point_to_point_tunneling_protocol_activity.json'; +import rule66 from './network_proxy_port_activity_to_the_internet.json'; +import rule67 from './network_rdp_remote_desktop_protocol_from_the_internet.json'; +import rule68 from './network_rdp_remote_desktop_protocol_to_the_internet.json'; +import rule69 from './network_rpc_remote_procedure_call_from_the_internet.json'; +import rule70 from './network_rpc_remote_procedure_call_to_the_internet.json'; +import rule71 from './network_smb_windows_file_sharing_activity_to_the_internet.json'; +import rule72 from './network_smtp_to_the_internet.json'; +import rule73 from './network_sql_server_port_activity_to_the_internet.json'; +import rule74 from './network_ssh_secure_shell_from_the_internet.json'; +import rule75 from './network_ssh_secure_shell_to_the_internet.json'; +import rule76 from './network_telnet_port_activity.json'; +import rule77 from './network_tor_activity_to_the_internet.json'; +import rule78 from './network_vnc_virtual_network_computing_from_the_internet.json'; +import rule79 from './network_vnc_virtual_network_computing_to_the_internet.json'; +import rule80 from './null_user_agent.json'; +import rule81 from './sqlmap_user_agent.json'; +import rule82 from './suricata_base64_encoded_invokecommand_powershell_execution.json'; +import rule83 from './suricata_base64_encoded_newobject_powershell_execution.json'; +import rule84 from './suricata_base64_encoded_startprocess_powershell_execution.json'; +import rule85 from './suricata_category_a_suspicious_string_was_detected.json'; +import rule86 from './suricata_category_attempted_administrator_privilege_gain.json'; +import rule87 from './suricata_category_attempted_denial_of_service.json'; +import rule88 from './suricata_category_attempted_information_leak.json'; +import rule89 from './suricata_category_attempted_login_with_suspicious_username.json'; +import rule90 from './suricata_category_attempted_user_privilege_gain.json'; +import rule91 from './suricata_category_client_using_unusual_port.json'; +import rule92 from './suricata_category_crypto_currency_mining_activity.json'; +import rule93 from './suricata_category_decode_of_an_rpc_query.json'; +import rule94 from './suricata_category_default_username_and_password_login_attempt.json'; +import rule95 from './suricata_category_denial_of_service.json'; +import rule96 from './suricata_category_denial_of_service_attack.json'; +import rule97 from './suricata_category_executable_code_was_detected.json'; +import rule98 from './suricata_category_exploit_kit_activity.json'; +import rule99 from './suricata_category_external_ip_address_retrieval.json'; +import rule100 from './suricata_category_generic_icmp_event.json'; +import rule101 from './suricata_category_generic_protocol_command_decode.json'; +import rule102 from './suricata_category_information_leak.json'; +import rule103 from './suricata_category_large_scale_information_leak.json'; +import rule104 from './suricata_category_malware_command_and_control_activity.json'; +import rule105 from './suricata_category_misc_activity.json'; +import rule106 from './suricata_category_misc_attack.json'; +import rule107 from './suricata_category_network_scan_detected.json'; +import rule108 from './suricata_category_network_trojan_detected.json'; +import rule109 from './suricata_category_nonstandard_protocol_or_event.json'; +import rule110 from './suricata_category_not_suspicious_traffic.json'; +import rule111 from './suricata_category_observed_c2_domain.json'; +import rule112 from './suricata_category_possible_social_engineering_attempted.json'; +import rule113 from './suricata_category_possibly_unwanted_program.json'; +import rule114 from './suricata_category_potential_corporate_privacy_violation.json'; +import rule115 from './suricata_category_potentially_bad_traffic.json'; +import rule116 from './suricata_category_potentially_vulnerable_web_application_access.json'; +import rule117 from './suricata_category_successful_administrator_privilege_gain.json'; +import rule118 from './suricata_category_successful_credential_theft.json'; +import rule119 from './suricata_category_successful_user_privilege_gain.json'; +import rule120 from './suricata_category_suspicious_filename_detected.json'; +import rule121 from './suricata_category_system_call_detected.json'; +import rule122 from './suricata_category_targeted_malicious_activity.json'; +import rule123 from './suricata_category_tcp_connection_detected.json'; +import rule124 from './suricata_category_unknown_traffic.json'; +import rule125 from './suricata_category_unsuccessful_user_privilege_gain.json'; +import rule126 from './suricata_category_web_application_attack.json'; +import rule127 from './suricata_cobaltstrike_artifact_in_an_dns_request.json'; +import rule128 from './suricata_commonly_abused_dns_domain_detected.json'; +import rule129 from './suricata_directory_reversal_characters_in_an_http_request.json'; +import rule130 from './suricata_directory_traversal_characters_in_an_http_request.json'; +import rule131 from './suricata_directory_traversal_characters_in_http_response.json'; +import rule132 from './suricata_directory_traversal_in_downloaded_zip_file.json'; +import rule133 from './suricata_dns_traffic_on_unusual_tcp_port.json'; +import rule134 from './suricata_dns_traffic_on_unusual_udp_port.json'; +import rule135 from './suricata_double_encoded_characters_in_a_uri.json'; +import rule136 from './suricata_double_encoded_characters_in_an_http_post.json'; +import rule137 from './suricata_double_encoded_characters_in_http_request.json'; +import rule138 from './suricata_eval_php_function_in_an_http_request.json'; +import rule139 from './suricata_exploit_cve_2018_1000861.json'; +import rule140 from './suricata_exploit_cve_2019_0227.json'; +import rule141 from './suricata_exploit_cve_2019_0232.json'; +import rule142 from './suricata_exploit_cve_2019_0604.json'; +import rule143 from './suricata_exploit_cve_2019_0708.json'; +import rule144 from './suricata_exploit_cve_2019_0752.json'; +import rule145 from './suricata_exploit_cve_2019_1003000.json'; +import rule146 from './suricata_exploit_cve_2019_10149.json'; +import rule147 from './suricata_exploit_cve_2019_11043.json'; +import rule148 from './suricata_exploit_cve_2019_11510.json'; +import rule149 from './suricata_exploit_cve_2019_11580.json'; +import rule150 from './suricata_exploit_cve_2019_11581.json'; +import rule151 from './suricata_exploit_cve_2019_13450.json'; +import rule152 from './suricata_exploit_cve_2019_13505.json'; +import rule153 from './suricata_exploit_cve_2019_15107.json'; +import rule154 from './suricata_exploit_cve_2019_15846.json'; +import rule155 from './suricata_exploit_cve_2019_16072.json'; +import rule156 from './suricata_exploit_cve_2019_1652.json'; +import rule157 from './suricata_exploit_cve_2019_16662.json'; +import rule158 from './suricata_exploit_cve_2019_16759.json'; +import rule159 from './suricata_exploit_cve_2019_16928.json'; +import rule160 from './suricata_exploit_cve_2019_17270.json'; +import rule161 from './suricata_exploit_cve_2019_1821.json'; +import rule162 from './suricata_exploit_cve_2019_19781.json'; +import rule163 from './suricata_exploit_cve_2019_2618.json'; +import rule164 from './suricata_exploit_cve_2019_2725.json'; +import rule165 from './suricata_exploit_cve_2019_3396.json'; +import rule166 from './suricata_exploit_cve_2019_3929.json'; +import rule167 from './suricata_exploit_cve_2019_5533.json'; +import rule168 from './suricata_exploit_cve_2019_6340.json'; +import rule169 from './suricata_exploit_cve_2019_7256.json'; +import rule170 from './suricata_exploit_cve_2019_9978.json'; +import rule171 from './suricata_ftp_traffic_on_unusual_port_internet_destination.json'; +import rule172 from './suricata_http_traffic_on_unusual_port_internet_destination.json'; +import rule173 from './suricata_imap_traffic_on_unusual_port_internet_destination.json'; +import rule174 from './suricata_lazagne_artifact_in_an_http_post.json'; +import rule175 from './suricata_mimikatz_artifacts_in_an_http_post.json'; +import rule176 from './suricata_mimikatz_string_detected_in_http_response.json'; +import rule177 from './suricata_nondns_traffic_on_tcp_port_53.json'; +import rule178 from './suricata_nondns_traffic_on_udp_port_53.json'; +import rule179 from './suricata_nonftp_traffic_on_port_21.json'; +import rule180 from './suricata_nonhttp_traffic_on_tcp_port_80.json'; +import rule181 from './suricata_nonimap_traffic_on_port_1443_imap.json'; +import rule182 from './suricata_nonsmb_traffic_on_tcp_port_139_smb.json'; +import rule183 from './suricata_nonssh_traffic_on_port_22.json'; +import rule184 from './suricata_nontls_on_tls_port.json'; +import rule185 from './suricata_possible_cobalt_strike_malleable_c2_null_response.json'; +import rule186 from './suricata_possible_sql_injection_sql_commands_in_http_transactions.json'; +import rule187 from './suricata_rpc_traffic_on_http_ports.json'; +import rule188 from './suricata_serialized_php_detected.json'; +import rule189 from './suricata_shell_exec_php_function_in_an_http_post.json'; +import rule190 from './suricata_ssh_traffic_not_on_port_22_internet_destination.json'; +import rule191 from './suricata_tls_traffic_on_unusual_port_internet_destination.json'; +import rule192 from './suricata_windows_executable_served_by_jpeg_web_content.json'; +import rule193 from './windows_background_intelligent_transfer_service_bits_connecting_to_the_internet.json'; +import rule194 from './windows_burp_ce_activity.json'; +import rule195 from './windows_certutil_connecting_to_the_internet.json'; +import rule196 from './windows_command_prompt_connecting_to_the_internet.json'; +import rule197 from './windows_command_shell_started_by_internet_explorer.json'; +import rule198 from './windows_command_shell_started_by_powershell.json'; +import rule199 from './windows_command_shell_started_by_svchost.json'; +import rule200 from './windows_credential_dumping_commands.json'; +import rule201 from './windows_credential_dumping_via_imageload.json'; +import rule202 from './windows_credential_dumping_via_registry_save.json'; +import rule203 from './windows_data_compression_using_powershell.json'; +import rule204 from './windows_defense_evasion_decoding_using_certutil.json'; +import rule205 from './windows_defense_evasion_or_persistence_via_hidden_files.json'; +import rule206 from './windows_defense_evasion_via_filter_manager.json'; +import rule207 from './windows_defense_evasion_via_windows_event_log_tools.json'; +import rule208 from './windows_execution_via_compiled_html_file.json'; +import rule209 from './windows_execution_via_connection_manager.json'; +import rule210 from './windows_execution_via_microsoft_html_application_hta.json'; +import rule211 from './windows_execution_via_net_com_assemblies.json'; +import rule212 from './windows_execution_via_regsvr32.json'; +import rule213 from './windows_execution_via_trusted_developer_utilities.json'; +import rule214 from './windows_html_help_executable_program_connecting_to_the_internet.json'; +import rule215 from './windows_image_load_from_a_temp_directory.json'; +import rule216 from './windows_indirect_command_execution.json'; +import rule217 from './windows_iodine_activity.json'; +import rule218 from './windows_management_instrumentation_wmi_execution.json'; +import rule219 from './windows_microsoft_html_application_hta_connecting_to_the_internet.json'; +import rule220 from './windows_mimikatz_activity.json'; +import rule221 from './windows_misc_lolbin_connecting_to_the_internet.json'; +import rule222 from './windows_net_command_activity_by_the_system_account.json'; +import rule223 from './windows_net_user_command_activity.json'; +import rule224 from './windows_netcat_activity.json'; +import rule225 from './windows_netcat_network_activity.json'; +import rule226 from './windows_network_anomalous_windows_process_using_https_ports.json'; +import rule227 from './windows_nmap_activity.json'; +import rule228 from './windows_nmap_scan_activity.json'; +import rule229 from './windows_payload_obfuscation_via_certutil.json'; +import rule230 from './windows_persistence_or_priv_escalation_via_hooking.json'; +import rule231 from './windows_persistence_via_application_shimming.json'; +import rule232 from './windows_persistence_via_bits_jobs.json'; +import rule233 from './windows_persistence_via_modification_of_existing_service.json'; +import rule234 from './windows_persistence_via_netshell_helper_dll.json'; +import rule235 from './windows_powershell_connecting_to_the_internet.json'; +import rule236 from './windows_priv_escalation_via_accessibility_features.json'; +import rule237 from './windows_process_discovery_via_tasklist_command.json'; +import rule238 from './windows_process_execution_via_wmi.json'; +import rule239 from './windows_process_started_by_acrobat_reader_possible_payload.json'; +import rule240 from './windows_process_started_by_ms_office_program_possible_payload.json'; +import rule241 from './windows_process_started_by_the_java_runtime.json'; +import rule242 from './windows_psexec_activity.json'; +import rule243 from './windows_register_server_program_connecting_to_the_internet.json'; +import rule244 from './windows_registry_query_local.json'; +import rule245 from './windows_registry_query_network.json'; +import rule246 from './windows_remote_management_execution.json'; +import rule247 from './windows_scheduled_task_activity.json'; +import rule248 from './windows_script_interpreter_connecting_to_the_internet.json'; +import rule249 from './windows_signed_binary_proxy_execution.json'; +import rule250 from './windows_signed_binary_proxy_execution_download.json'; +import rule251 from './windows_suspicious_process_started_by_a_script.json'; +import rule252 from './windows_whoami_command_activity.json'; +import rule253 from './windows_windump_activity.json'; +import rule254 from './windows_wireshark_activity.json'; +import rule255 from './zeek_notice_capturelosstoo_much_loss.json'; +import rule256 from './zeek_notice_conncontent_gap.json'; +import rule257 from './zeek_notice_connretransmission_inconsistency.json'; +import rule258 from './zeek_notice_dnsexternal_name.json'; +import rule259 from './zeek_notice_ftpbruteforcing.json'; +import rule260 from './zeek_notice_ftpsite_exec_success.json'; +import rule261 from './zeek_notice_heartbleedssl_heartbeat_attack.json'; +import rule262 from './zeek_notice_heartbleedssl_heartbeat_attack_success.json'; +import rule263 from './zeek_notice_heartbleedssl_heartbeat_many_requests.json'; +import rule264 from './zeek_notice_heartbleedssl_heartbeat_odd_length.json'; +import rule265 from './zeek_notice_httpsql_injection_attacker.json'; +import rule266 from './zeek_notice_httpsql_injection_victim.json'; +import rule267 from './zeek_notice_intelnotice.json'; +import rule268 from './zeek_notice_noticetally.json'; +import rule269 from './zeek_notice_packetfiltercannot_bpf_shunt_conn.json'; +import rule270 from './zeek_notice_packetfiltercompile_failure.json'; +import rule271 from './zeek_notice_packetfilterdropped_packets.json'; +import rule272 from './zeek_notice_packetfilterinstall_failure.json'; +import rule273 from './zeek_notice_packetfilterno_more_conn_shunts_available.json'; +import rule274 from './zeek_notice_packetfiltertoo_long_to_compile_filter.json'; +import rule275 from './zeek_notice_protocoldetectorprotocol_found.json'; +import rule276 from './zeek_notice_protocoldetectorserver_found.json'; +import rule277 from './zeek_notice_scanaddress_scan.json'; +import rule278 from './zeek_notice_scanport_scan.json'; +import rule279 from './zeek_notice_signaturescount_signature.json'; +import rule280 from './zeek_notice_signaturesmultiple_sig_responders.json'; +import rule281 from './zeek_notice_signaturesmultiple_signatures.json'; +import rule282 from './zeek_notice_signaturessensitive_signature.json'; +import rule283 from './zeek_notice_signaturessignature_summary.json'; +import rule284 from './zeek_notice_smtpblocklist_blocked_host.json'; +import rule285 from './zeek_notice_smtpblocklist_error_message.json'; +import rule286 from './zeek_notice_smtpsuspicious_origination.json'; +import rule287 from './zeek_notice_softwaresoftware_version_change.json'; +import rule288 from './zeek_notice_softwarevulnerable_version.json'; +import rule289 from './zeek_notice_sshinteresting_hostname_login.json'; +import rule290 from './zeek_notice_sshlogin_by_password_guesser.json'; +import rule291 from './zeek_notice_sshpassword_guessing.json'; +import rule292 from './zeek_notice_sshwatched_country_login.json'; +import rule293 from './zeek_notice_sslcertificate_expired.json'; +import rule294 from './zeek_notice_sslcertificate_expires_soon.json'; +import rule295 from './zeek_notice_sslcertificate_not_valid_yet.json'; +import rule296 from './zeek_notice_sslinvalid_ocsp_response.json'; +import rule297 from './zeek_notice_sslinvalid_server_cert.json'; +import rule298 from './zeek_notice_sslold_version.json'; +import rule299 from './zeek_notice_sslweak_cipher.json'; +import rule300 from './zeek_notice_sslweak_key.json'; +import rule301 from './zeek_notice_teamcymrumalwarehashregistrymatch.json'; +import rule302 from './zeek_notice_traceroutedetected.json'; +import rule303 from './zeek_notice_weirdactivity.json'; export const rawRules = [ rule1, rule2, @@ -650,40 +614,4 @@ export const rawRules = [ rule301, rule302, rule303, - rule304, - rule305, - rule306, - rule307, - rule308, - rule309, - rule310, - rule311, - rule312, - rule313, - rule314, - rule315, - rule316, - rule317, - rule318, - rule319, - rule320, - rule321, - rule322, - rule323, - rule324, - rule325, - rule326, - rule327, - rule328, - rule329, - rule330, - rule331, - rule332, - rule333, - rule334, - rule335, - rule336, - rule337, - rule338, - rule339, ]; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_java_process_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_java_process_connecting_to_the_internet.json deleted file mode 100644 index 57f37e34ad4d5..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_java_process_connecting_to_the_internet.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "description": "Linux: Java Process Connecting to the Internet", - "enabled": false, - "filters": [ - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "key": "process.name", - "negate": false, - "params": { - "query": "java" - }, - "type": "phrase", - "value": "java" - }, - "query": { - "match": { - "process.name": { - "query": "java", - "type": "phrase" - } - } - } - }, - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", - "key": "event.action", - "negate": false, - "params": { - "query": "socket_opened" - }, - "type": "phrase", - "value": "socket_opened" - }, - "query": { - "match": { - "event.action": { - "query": "socket_opened", - "type": "phrase" - } - } - } - }, - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index", - "key": "destination.ip", - "negate": true, - "params": { - "query": "127.0.0.1" - }, - "type": "phrase", - "value": "127.0.0.1" - }, - "query": { - "match": { - "destination.ip": { - "query": "127.0.0.1", - "type": "phrase" - } - } - } - }, - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[3].meta.index", - "key": "destination.ip", - "negate": true, - "params": { - "query": "::1" - }, - "type": "phrase", - "value": "::1" - }, - "query": { - "match": { - "destination.ip": { - "query": "::1", - "type": "phrase" - } - } - } - } - ], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Linux: Java Process Connecting to the Internet", - "query": "not destination.ip: 10.0.0.0/8 and not 172.16.0.0/12", - "risk_score": 50, - "rule_id": "7f65b8c5-27ed-4cf6-a088-3a20d2f84bf5", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_lzop_activity_possible_julianrunnels.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_lzop_activity_possible_julianrunnels.json deleted file mode 100644 index 62203b6c42a5a..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_lzop_activity_possible_julianrunnels.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Linux lzop activity - possible @JulianRunnels", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Linux lzop activity - possible @JulianRunnels", - "query": "process.name:lzop", - "risk_score": 50, - "rule_id": "d89b05b1-9b2b-45ea-9876-4a74550af6a6", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_unusual_shell_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_unusual_shell_activity.json deleted file mode 100644 index a63b2ea7dc522..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_unusual_shell_activity.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "description": "Linux unusual shell activity", - "enabled": false, - "filters": [ - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "key": "process.name", - "negate": true, - "params": { - "query": "bash" - }, - "type": "phrase", - "value": "bash" - }, - "query": { - "match": { - "process.name": { - "query": "bash", - "type": "phrase" - } - } - } - }, - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", - "key": "process.executable", - "negate": true, - "params": { - "query": "/bin/dash" - }, - "type": "phrase", - "value": "/bin/dash" - }, - "query": { - "match": { - "process.executable": { - "query": "/bin/dash", - "type": "phrase" - } - } - } - }, - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[2].meta.index", - "key": "process.name", - "negate": true, - "params": { - "query": "ReportCrash" - }, - "type": "phrase", - "value": "ReportCrash" - }, - "query": { - "match": { - "process.name": { - "query": "ReportCrash", - "type": "phrase" - } - } - } - } - ], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Linux unusual shell activity", - "query": "process.name:*sh", - "risk_score": 50, - "rule_id": "4cc78842-f8a9-4a20-b703-a596c4f24e4f", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/powershell_network_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/powershell_network_connection.json deleted file mode 100644 index 075f77490a237..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/powershell_network_connection.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "description": "Powershell network connection", - "enabled": false, - "filters": [ - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "key": "event.action", - "negate": false, - "params": { - "query": "Network connection detected (rule: NetworkConnect)" - }, - "type": "phrase", - "value": "Network connection detected (rule: NetworkConnect)" - }, - "query": { - "match": { - "event.action": { - "query": "Network connection detected (rule: NetworkConnect)", - "type": "phrase" - } - } - } - }, - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", - "key": "destination.ip", - "negate": true, - "params": { - "query": "169.254.169.254" - }, - "type": "phrase", - "value": "169.254.169.254" - }, - "query": { - "match": { - "destination.ip": { - "query": "169.254.169.254", - "type": "phrase" - } - } - } - } - ], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Powershell network connection", - "query": "process.name:powershell.exe", - "risk_score": 50, - "rule_id": "8e792144-39a6-4a63-9779-2f12719dc132", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_execution_via_wmi.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_execution_via_wmi.json deleted file mode 100644 index 5ed0ad3899b4c..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_execution_via_wmi.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Process Execution via WMI", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Process Execution via WMI", - "query": "process.name:scrcons.exe", - "risk_score": 50, - "rule_id": "14ba7cd9-1489-459b-99a4-153c7a3f9abb", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_acrobat_reader_possible_payload.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_acrobat_reader_possible_payload.json deleted file mode 100644 index c00b88e5f88ef..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_acrobat_reader_possible_payload.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "description": "Process started by Acrobat reader - possible payload", - "enabled": false, - "filters": [ - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "key": "event.action", - "negate": false, - "params": { - "query": "Process Create (rule: ProcessCreate)" - }, - "type": "phrase", - "value": "Process Create (rule: ProcessCreate)" - }, - "query": { - "match": { - "event.action": { - "query": "Process Create (rule: ProcessCreate)", - "type": "phrase" - } - } - } - } - ], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Process started by Acrobat reader - possible payload", - "query": "process.parent.name:AcroRd32.exe", - "risk_score": 50, - "rule_id": "c359628d-d5af-4a20-99df-aeeea109b690", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_ms_office_program_possible_payload.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_ms_office_program_possible_payload.json deleted file mode 100644 index 5237b17e7d69f..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_ms_office_program_possible_payload.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "description": "Process started by MS Office program - possible payload", - "enabled": false, - "filters": [ - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "key": "event.action", - "negate": false, - "params": { - "query": "Process Create (rule: ProcessCreate)" - }, - "type": "phrase", - "value": "Process Create (rule: ProcessCreate)" - }, - "query": { - "match": { - "event.action": { - "query": "Process Create (rule: ProcessCreate)", - "type": "phrase" - } - } - } - } - ], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Process started by MS Office program - possible payload", - "query": " process.parent.name:EXCEL.EXE or process.parent.name:MSPUB.EXE or process.parent.name:OUTLOOK.EXE or process.parent.name:POWERPNT.EXE or process.parent.name:VISIO.EXE or process.parent.name:WINWORD.EXE", - "risk_score": 50, - "rule_id": "3181b814-08e3-43f9-b77a-a2530603b131", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_windows_defender.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_windows_defender.json deleted file mode 100644 index 1a686a4482df6..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/process_started_by_windows_defender.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Process started by Windows Defender", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Process started by Windows Defender", - "query": "parent.process.name:MsMpEng.exe", - "risk_score": 50, - "rule_id": "b3da3321-417d-494b-854c-b40369e063f0", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/psexec_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/psexec_activity.json deleted file mode 100644 index b928e7dc80576..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/psexec_activity.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "PSexec activity", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "PSexec activity", - "query": "process.name:PsExec.exe or process.name:PsExec64.exe", - "risk_score": 50, - "rule_id": "9511b7f4-3898-4813-8bd3-d810b03148ab", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/search_windows_10.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/search_windows_10.json deleted file mode 100644 index ab76b1ed9ff9e..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/search_windows_10.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "description": "(Search) Windows 10", - "enabled": false, - "filters": [ - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "key": "agent.hostname", - "negate": false, - "params": { - "query": "LAPTOP-CQNI37L2" - }, - "type": "phrase" - }, - "query": { - "match": { - "agent.hostname": { - "query": "LAPTOP-CQNI37L2", - "type": "phrase" - } - } - } - }, - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index", - "key": "event.provider", - "negate": false, - "params": { - "query": "Microsoft-Windows-Sysmon" - }, - "type": "phrase" - }, - "query": { - "match": { - "event.provider": { - "query": "Microsoft-Windows-Sysmon", - "type": "phrase" - } - } - } - } - ], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "(Search) Windows 10", - "query": "", - "risk_score": 50, - "rule_id": "5d00c579-794c-4f64-be52-1ed8cae2b11e", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_child_processes_of_spoolsvexe.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_child_processes_of_spoolsvexe.json deleted file mode 100644 index e20197dfd2c92..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_child_processes_of_spoolsvexe.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Splunk - Child Processes of Spoolsv.exe", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Splunk - Child Processes of Spoolsv.exe", - "query": "process.parent.name:spoolsv.exe and not process.name:regsvr32.exe ", - "risk_score": 50, - "rule_id": "2f026c73-bb63-455e-abdf-f11f463acf0d", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_large_outbound_icmp_packets.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_large_outbound_icmp_packets.json deleted file mode 100644 index 11186bfb44d62..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_large_outbound_icmp_packets.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Splunk - Detect Large Outbound ICMP Packets", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Splunk - Detect Large Outbound ICMP Packets", - "query": "network.transport:icmp and network.bytes>1000 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16", - "risk_score": 50, - "rule_id": "e108c0c6-5ee8-47a0-8c23-ec47ba3a9b00", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_long_dns_txt_record_response.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_long_dns_txt_record_response.json deleted file mode 100644 index 724985b2d1de8..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_long_dns_txt_record_response.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Splunk - Detect Long DNS TXT Record Response", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Splunk - Detect Long DNS TXT Record Response", - "query": "network.protocol:dns and server.bytes>100 and not destination.ip:10.0.0.0/8 and not destination.ip:172.16.0.0/12 and not destination.ip:192.168.0.0/16 and not destination.ip:169.254.169.254 and not destination.ip:127.0.0.53", - "risk_score": 50, - "rule_id": "2cdf84be-1c9c-4184-9880-75b9a6ddeaba", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_new_local_admin_account.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_new_local_admin_account.json deleted file mode 100644 index c0e773f09b168..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_new_local_admin_account.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Splunk - Detect New Local Admin account", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Splunk - Detect New Local Admin account", - "query": "event.code:(4720 or 4732) and winlog.event_data.TargetUserName:Administrators", - "risk_score": 50, - "rule_id": "030fc8e4-2c5f-4cc9-a6bd-2b6b7b98ae16", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_psexec_with_accepteula_flag.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_psexec_with_accepteula_flag.json deleted file mode 100644 index f9ad5793f2547..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_psexec_with_accepteula_flag.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Splunk - Detect PsExec With accepteula Flag", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Splunk - Detect PsExec With accepteula Flag", - "query": "process.name:PsExec.exe and process.args:\"-accepteula\"", - "risk_score": 50, - "rule_id": "4b63cf13-9043-41e3-84ec-6e39eb0d407e", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_use_of_cmdexe_to_launch_script_interpreters.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_use_of_cmdexe_to_launch_script_interpreters.json deleted file mode 100644 index 0a67c3adeaea5..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_detect_use_of_cmdexe_to_launch_script_interpreters.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Splunk - Detect Use of cmd.exe to Launch Script Interpreters", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Splunk - Detect Use of cmd.exe to Launch Script Interpreters", - "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:(\"wscript.exe\" or \"cscript.exe\") and process.parent.name:\"cmd.exe\"", - "risk_score": 50, - "rule_id": "f4388e4c-ec3d-41b3-be5c-27c11f61473c", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_processes_created_by_netsh.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_processes_created_by_netsh.json deleted file mode 100644 index 466f9aff01942..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_processes_created_by_netsh.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Splunk - Processes created by netsh", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Splunk - Processes created by netsh", - "query": "process.parent.name:netsh.exe", - "risk_score": 50, - "rule_id": "ce7a0bde-7406-4729-a075-a215f4571ff6", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_processes_launching_netsh.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_processes_launching_netsh.json deleted file mode 100644 index cc54721cd92f2..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_processes_launching_netsh.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Splunk - Processes launching netsh", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Splunk - Processes launching netsh", - "query": "process.name:netsh.exe and event.action:\"Process Create (rule: ProcessCreate)\" ", - "risk_score": 50, - "rule_id": "600dba95-f1c6-4a4d-aae1-c79cbd8a5ddd", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_protocols_passing_authentication_in_cleartext.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_protocols_passing_authentication_in_cleartext.json deleted file mode 100644 index c68e074d43817..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_protocols_passing_authentication_in_cleartext.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Splunk - Protocols passing authentication in cleartext", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Splunk - Protocols passing authentication in cleartext", - "query": "destination.port:(21 or 23 or 110 or 143) and network.transport:tcp", - "risk_score": 50, - "rule_id": "f4442e7f-856a-4a4a-851b-c1f9b97b0d39", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_windows_event_log_cleared.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_windows_event_log_cleared.json deleted file mode 100644 index 5f36d6623bcfb..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/splunk_windows_event_log_cleared.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "Splunk - Windows Event Log Cleared", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Splunk - Windows Event Log Cleared", - "query": "event.code:(1102 or 1100)", - "risk_score": 50, - "rule_id": "c0747553-4652-4e74-bc86-898f2daa2bde", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suspicious_process_started_by_a_script.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suspicious_process_started_by_a_script.json deleted file mode 100644 index 37cf174786f97..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/suspicious_process_started_by_a_script.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "description": "Suspicious process started by a script", - "enabled": false, - "filters": [ - { - "$state": { - "store": "appState" - }, - "meta": { - "alias": null, - "disabled": false, - "indexRefName": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "key": "event.action", - "negate": false, - "params": { - "query": "Process Create (rule: ProcessCreate)" - }, - "type": "phrase", - "value": "Process Create (rule: ProcessCreate)" - }, - "query": { - "match": { - "event.action": { - "query": "Process Create (rule: ProcessCreate)", - "type": "phrase" - } - } - } - } - ], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "Suspicious process started by a script", - "query": "(process.parent.name:cmd.exe or process.parent.name:cscript.exe or process.parent.name:mshta.exe or process.parent.name:powershell.exe or process.parent.name:rundll32.exe or process.parent.name:wscript.exe or process.parent.name:wmiprvse.exe) and (process.name:bitsadmin.exe or process.name:certutil.exe or mshta.exe or process.name:nslookup.exe or process.name:schtasks.exe)", - "risk_score": 50, - "rule_id": "e49b532b-3e52-4f3d-90f6-05a86982d347", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windump_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windump_activity.json deleted file mode 100644 index 7b40fc208ecd5..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windump_activity.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "WinDump activity", - "enabled": false, - "filters": [], - "from": "now-6m", - "immutable": true, - "interval": "5m", - "language": "kuery", - "name": "WinDump activity", - "query": "process.name:WinDump.exe", - "risk_score": 50, - "rule_id": "61c56cf4-0c08-4ad5-83ea-d2fe6ac62fa8", - "severity": "low", - "to": "now", - "type": "query", - "version": 1 -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_prepackaged_rules_status.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_prepackaged_rules_status.sh new file mode 100755 index 0000000000000..40b10b15e21f6 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_prepackaged_rules_status.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# +# 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. +# + +set -e +./check_env_variables.sh + +# Example: ./get_prepackaged_rules_status.sh +curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/detection_engine/rules/prepackaged/_status | jq . diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json index e9d955f920571..4a90d904f31ab 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_everything.json @@ -21,7 +21,6 @@ } ], "enabled": false, - "immutable": false, "index": ["auditbeat-*", "filebeat-*"], "interval": "5m", "query": "user.name: root or user.name: admin", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json index 16d5d6cc2b36a..2b7dbc8cccf0e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/saved_queries/saved_query_with_everything.json @@ -21,7 +21,6 @@ } ], "enabled": false, - "immutable": true, "index": ["auditbeat-*", "filebeat-*"], "interval": "5m", "query": "user.name: root or user.name: admin", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_immutable.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_immutable.json deleted file mode 100644 index 14706b6e54e7b..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_immutable.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "rule_id": "query-rule-id", - "immutable": true -} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json index 7fc8de9fe8f9e..a47d0155727d8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_query_everything.json @@ -21,7 +21,6 @@ } ], "enabled": false, - "immutable": true, "index": ["auditbeat-*", "filebeat-*"], "interval": "5m", "query": "user.name: root or user.name: admin", diff --git a/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts index 07b748024743c..4bd980fd2ff80 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/network/elasticsearch_adapter.ts @@ -18,10 +18,16 @@ import { NetworkHttpData, NetworkHttpEdges, NetworkTopNFlowEdges, - MatrixOverOrdinalHistogramData, + NetworkDsOverTimeData, + MatrixOverTimeHistogramData, } from '../../graphql/types'; import { inspectStringifyObject } from '../../utils/build_query'; -import { DatabaseSearchResponse, FrameworkAdapter, FrameworkRequest } from '../framework'; +import { + DatabaseSearchResponse, + FrameworkAdapter, + FrameworkRequest, + MatrixHistogramRequestOptions, +} from '../framework'; import { TermAggregation } from '../types'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants'; @@ -32,6 +38,7 @@ import { NetworkTopNFlowRequestOptions, } from './index'; import { buildDnsQuery } from './query_dns.dsl'; +import { buildDnsHistogramQuery } from './query_dns_histogram.dsl'; import { buildTopNFlowQuery, getOppositeField } from './query_top_n_flow.dsl'; import { buildHttpQuery } from './query_http.dsl'; import { buildTopCountriesQuery } from './query_top_countries.dsl'; @@ -41,7 +48,9 @@ import { NetworkTopCountriesBuckets, NetworkHttpBuckets, NetworkTopNFlowBuckets, + DnsHistogramGroupData, } from './types'; +import { EventHit } from '../events/types'; export class ElasticsearchNetworkAdapter implements NetworkAdapter { constructor(private readonly framework: FrameworkAdapter) {} @@ -141,7 +150,6 @@ export class ElasticsearchNetworkAdapter implements NetworkAdapter { ); const fakeTotalCount = fakePossibleCount <= totalCount ? fakePossibleCount : totalCount; const edges = networkDnsEdges.splice(cursorStart, querySize - cursorStart); - const histogram = getHistogramData(edges); const inspect = { dsl: [inspectStringifyObject(dsl)], response: [inspectStringifyObject(response)], @@ -156,7 +164,6 @@ export class ElasticsearchNetworkAdapter implements NetworkAdapter { showMorePagesIndicator, }, totalCount, - histogram, }; } @@ -195,29 +202,36 @@ export class ElasticsearchNetworkAdapter implements NetworkAdapter { totalCount, }; } + + public async getNetworkDnsHistogramData( + request: FrameworkRequest, + options: MatrixHistogramRequestOptions + ): Promise { + const dsl = buildDnsHistogramQuery(options); + const response = await this.framework.callWithRequest( + request, + 'search', + dsl + ); + const totalCount = getOr(0, 'hits.total.value', response); + const matrixHistogramData = getOr([], 'aggregations.NetworkDns.buckets', response); + const inspect = { + dsl: [inspectStringifyObject(dsl)], + response: [inspectStringifyObject(response)], + }; + return { + inspect, + matrixHistogramData: getHistogramData(matrixHistogramData), + totalCount, + }; + } } -const getHistogramData = ( - data: NetworkDnsEdges[] -): MatrixOverOrdinalHistogramData[] | undefined => { - if (!Array.isArray(data)) return undefined; +const getHistogramData = (data: DnsHistogramGroupData[]): MatrixOverTimeHistogramData[] => { return data.reduce( - (acc: MatrixOverOrdinalHistogramData[], { node: { dnsBytesOut, dnsBytesIn, _id } }) => { - if (_id != null && dnsBytesOut != null && dnsBytesIn != null) - return [ - ...acc, - { - x: _id, - y: dnsBytesOut, - g: 'DNS Bytes Out', - }, - { - x: _id, - y: dnsBytesIn, - g: 'DNS Bytes In', - }, - ]; - return acc; + (acc: MatrixOverTimeHistogramData[], { key: time, histogram: { buckets } }) => { + const temp = buckets.map(({ key, doc_count }) => ({ x: time, y: doc_count, g: key })); + return [...acc, ...temp]; }, [] ); diff --git a/x-pack/legacy/plugins/siem/server/lib/network/index.ts b/x-pack/legacy/plugins/siem/server/lib/network/index.ts index 42ce9f0726ddb..cbcd33b753d8a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/index.ts +++ b/x-pack/legacy/plugins/siem/server/lib/network/index.ts @@ -14,8 +14,13 @@ import { NetworkTopCountriesData, NetworkTopNFlowData, NetworkTopTablesSortField, + NetworkDsOverTimeData, } from '../../graphql/types'; -import { FrameworkRequest, RequestOptionsPaginated } from '../framework'; +import { + FrameworkRequest, + RequestOptionsPaginated, + MatrixHistogramRequestOptions, +} from '../framework'; export * from './elasticsearch_adapter'; import { NetworkAdapter } from './types'; @@ -68,6 +73,13 @@ export class Network { return this.adapter.getNetworkDns(req, options); } + public async getNetworkDnsHistogramData( + req: FrameworkRequest, + options: MatrixHistogramRequestOptions + ): Promise { + return this.adapter.getNetworkDnsHistogramData(req, options); + } + public async getNetworkHttp( req: FrameworkRequest, options: NetworkHttpRequestOptions diff --git a/x-pack/legacy/plugins/siem/server/lib/network/query_dns_histogram.dsl.ts b/x-pack/legacy/plugins/siem/server/lib/network/query_dns_histogram.dsl.ts new file mode 100644 index 0000000000000..67457ab4840ac --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/network/query_dns_histogram.dsl.ts @@ -0,0 +1,83 @@ +/* + * 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 { createQueryFilterClauses, calculateTimeseriesInterval } from '../../utils/build_query'; +import { MatrixHistogramRequestOptions } from '../framework'; + +export const buildDnsHistogramQuery = ({ + filterQuery, + timerange: { from, to }, + defaultIndex, + sourceConfiguration: { + fields: { timestamp }, + }, + stackByField, +}: MatrixHistogramRequestOptions) => { + const filter = [ + ...createQueryFilterClauses(filterQuery), + { + range: { + [timestamp]: { + gte: from, + lte: to, + }, + }, + }, + ]; + + const getHistogramAggregation = () => { + const interval = calculateTimeseriesInterval(from, to); + const histogramTimestampField = '@timestamp'; + const dateHistogram = { + date_histogram: { + field: histogramTimestampField, + fixed_interval: `${interval}s`, + }, + }; + + return { + NetworkDns: { + ...dateHistogram, + aggs: { + histogram: { + terms: { + field: stackByField, + order: { + orderAgg: 'desc', + }, + size: 10, + }, + aggs: { + orderAgg: { + cardinality: { + field: 'dns.question.name', + }, + }, + }, + }, + }, + }, + }; + }; + + const dslQuery = { + index: defaultIndex, + allowNoIndices: true, + ignoreUnavailable: true, + body: { + aggregations: getHistogramAggregation(), + query: { + bool: { + filter, + }, + }, + size: 0, + track_total_hits: true, + }, + }; + + return dslQuery; +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/network/types.ts b/x-pack/legacy/plugins/siem/server/lib/network/types.ts index 960fbe425b002..b5563f9a2fef1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/network/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/network/types.ts @@ -9,8 +9,13 @@ import { NetworkHttpData, NetworkTopCountriesData, NetworkTopNFlowData, + NetworkDsOverTimeData, } from '../../graphql/types'; -import { FrameworkRequest, RequestOptionsPaginated } from '../framework'; +import { + FrameworkRequest, + RequestOptionsPaginated, + MatrixHistogramRequestOptions, +} from '../framework'; import { TotalValue } from '../types'; import { NetworkDnsRequestOptions } from '.'; @@ -24,6 +29,10 @@ export interface NetworkAdapter { options: RequestOptionsPaginated ): Promise; getNetworkDns(req: FrameworkRequest, options: NetworkDnsRequestOptions): Promise; + getNetworkDnsHistogramData( + request: FrameworkRequest, + options: MatrixHistogramRequestOptions + ): Promise; getNetworkHttp(req: FrameworkRequest, options: RequestOptionsPaginated): Promise; } @@ -143,3 +152,23 @@ export interface NetworkHttpBuckets { buckets: GenericBuckets[]; }; } + +interface DnsHistogramSubBucket { + key: string; + doc_count: number; + orderAgg: { + value: number; + }; +} +interface DnsHistogramBucket { + doc_count_error_upper_bound: number; + sum_other_doc_count: number; + buckets: DnsHistogramSubBucket[]; +} + +export interface DnsHistogramGroupData { + key: number; + doc_count: number; + key_as_string: string; + histogram: DnsHistogramBucket; +} diff --git a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx index 0a5e9558f91af..2493a8fbd9ffb 100644 --- a/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx +++ b/x-pack/legacy/plugins/snapshot_restore/public/app/sections/home/policy_list/policy_table/policy_table.tsx @@ -199,7 +199,7 @@ export const PolicyTable: React.FunctionComponent = ({ render: ({ name, inProgress, isManagedPolicy }: SlmPolicy) => { return ( - + {executePolicyPrompt => { return ( @@ -235,7 +235,7 @@ export const PolicyTable: React.FunctionComponent = ({ }} - + = ({ /> - + {deletePolicyPrompt => { const label = !isManagedPolicy diff --git a/x-pack/legacy/plugins/task_manager/server/legacy.ts b/x-pack/legacy/plugins/task_manager/server/legacy.ts index 772309d67c334..f5e81bfd90169 100644 --- a/x-pack/legacy/plugins/task_manager/server/legacy.ts +++ b/x-pack/legacy/plugins/task_manager/server/legacy.ts @@ -17,7 +17,7 @@ import { TaskInstanceWithId, TaskDefinition, } from '../../../../plugins/task_manager/server/task.js'; -import { FetchOpts } from '../../../../plugins/task_manager/server/task_store.js'; +import { SearchOpts } from '../../../../plugins/task_manager/server/task_store.js'; // Once all plugins are migrated to NP and we can remove Legacy TaskManager in version 8.0.0, // this can be removed @@ -46,7 +46,7 @@ export function createLegacyApi(legacyTaskManager: Promise): Legacy registerTaskDefinitions: (taskDefinitions: TaskDictionary) => { legacyTaskManager.then((tm: TaskManager) => tm.registerTaskDefinitions(taskDefinitions)); }, - fetch: (opts: FetchOpts) => legacyTaskManager.then((tm: TaskManager) => tm.fetch(opts)), + fetch: (opts: SearchOpts) => legacyTaskManager.then((tm: TaskManager) => tm.fetch(opts)), remove: (id: string) => legacyTaskManager.then((tm: TaskManager) => tm.remove(id)), schedule: (taskInstance: TaskInstanceWithDeprecatedFields, options?: any) => legacyTaskManager.then((tm: TaskManager) => tm.schedule(taskInstance, options)), diff --git a/x-pack/legacy/plugins/tilemap/index.js b/x-pack/legacy/plugins/tilemap/index.js index b5f7839c7c7f7..767a0fe72985e 100644 --- a/x-pack/legacy/plugins/tilemap/index.js +++ b/x-pack/legacy/plugins/tilemap/index.js @@ -12,7 +12,7 @@ export const tilemap = kibana => { return new kibana.Plugin({ id: 'tilemap', configPrefix: 'xpack.tilemap', - require: ['xpack_main', 'kbn_vislib_vis_types'], + require: ['xpack_main', 'vis_type_vislib'], publicDir: resolve(__dirname, 'public'), uiExports: { visTypeEnhancers: ['plugins/tilemap/vis_type_enhancers/update_tilemap_settings'], diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.tsx index ebe4f7efda055..e2f92e0acd645 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.tsx @@ -6,12 +6,14 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { EuiBadge, EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiProgress, + EuiScreenReaderOnly, EuiText, EuiToolTip, RIGHT_ALIGNMENT, @@ -98,6 +100,16 @@ export const getColumns = ( ActionsColumnType ] = [ { + name: ( + +

+ +

+
+ ), align: RIGHT_ALIGNMENT, width: '40px', isExpander: true, diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx index 63a054c0c4889..98780d23c5a62 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx @@ -45,7 +45,7 @@ const EmbeddedPanel = styled.div` } `; -export const EmbeddedMap = ({ upPoints, downPoints }: EmbeddedMapProps) => { +export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProps) => { const { colors } = useContext(UptimeThemeContext); const [embeddable, setEmbeddable] = useState(); const embeddableRoot: React.RefObject = useRef(null); @@ -55,10 +55,6 @@ export const EmbeddedMap = ({ upPoints, downPoints }: EmbeddedMapProps) => { id: uuid.v4(), filters: [], hidePanelTitles: true, - query: { - query: '', - language: 'kuery', - }, refreshConfig: { value: 0, pause: false, @@ -116,6 +112,6 @@ export const EmbeddedMap = ({ upPoints, downPoints }: EmbeddedMapProps) => {
); -}; +}); EmbeddedMap.displayName = 'EmbeddedMap'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_details.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_details.tsx index bb87497d335ef..63abb6fc4823c 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_details.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_details.tsx @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useEffect } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { LocationMap } from '../location_map'; import { MonitorStatusBar } from './monitor_status_bar'; +import { UptimeRefreshContext } from '../../../contexts'; interface MonitorStatusBarProps { monitorId: string; @@ -29,6 +30,27 @@ export const MonitorStatusDetailsComponent = ({ useEffect(() => { loadMonitorLocations(monitorId); }, [loadMonitorLocations, monitorId, dateStart, dateEnd]); + const { refreshApp } = useContext(UptimeRefreshContext); + + const [isTabActive] = useState(document.visibilityState); + const onTabActive = () => { + if (document.visibilityState === 'visible' && isTabActive === 'hidden') { + refreshApp(); + } + }; + + // Refreshing application state after Tab becomes active to render latest map state + // If application renders in when tab is not in focus it gives some unexpected behaviors + // Where map is not visible on change + useEffect(() => { + document.addEventListener('visibilitychange', onTabActive); + return () => { + document.removeEventListener('visibilitychange', onTabActive); + }; + + // we want this effect to execute exactly once after the component mounts + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return ( diff --git a/x-pack/package.json b/x-pack/package.json index 84f15d3d38828..ad0be351483f6 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -176,7 +176,7 @@ "@elastic/apm-rum-react": "^0.3.2", "@elastic/datemath": "5.0.2", "@elastic/ems-client": "7.6.0", - "@elastic/eui": "18.2.0", + "@elastic/eui": "18.2.1", "@elastic/filesaver": "1.1.2", "@elastic/maki": "6.1.0", "@elastic/node-crypto": "^1.0.0", @@ -252,7 +252,7 @@ "immer": "^1.5.0", "inline-style": "^2.0.0", "intl": "^1.2.5", - "io-ts": "^2.0.1", + "io-ts": "^2.0.5", "isbinaryfile": "4.0.2", "joi": "^13.5.2", "jquery": "^3.4.1", diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx index 82c95b37ee7b0..d69e068bdea3a 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx @@ -9,6 +9,9 @@ import ReactDOM from 'react-dom'; import { CoreStart, AppMountParameters } from 'kibana/public'; import { I18nProvider, FormattedMessage } from '@kbn/i18n/react'; import { Route, BrowserRouter, Switch } from 'react-router-dom'; +import { Provider } from 'react-redux'; +import { Store } from 'redux'; +import { appStoreFactory } from './store'; /** * This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle. @@ -16,7 +19,9 @@ import { Route, BrowserRouter, Switch } from 'react-router-dom'; export function renderApp(coreStart: CoreStart, { appBasePath, element }: AppMountParameters) { coreStart.http.get('/api/endpoint/hello-world'); - ReactDOM.render(, element); + const store = appStoreFactory(coreStart); + + ReactDOM.render(, element); return () => { ReactDOM.unmountComponentAtNode(element); @@ -25,38 +30,46 @@ export function renderApp(coreStart: CoreStart, { appBasePath, element }: AppMou interface RouterProps { basename: string; + store: Store; } -const AppRoot: React.FunctionComponent = React.memo(({ basename }) => ( - - - - ( -

- -

- )} - /> - ( -

- -

- )} - /> - ( - - )} - /> -
-
-
+const AppRoot: React.FunctionComponent = React.memo(({ basename, store }) => ( + + + + + ( +

+ +

+ )} + /> + { + // FIXME: This is temporary. Will be removed in next PR for endpoint list + store.dispatch({ type: 'userEnteredEndpointListPage' }); + + return ( +

+ +

+ ); + }} + /> + ( + + )} + /> +
+
+
+
)); diff --git a/x-pack/test/typings/hapi_basic.d.ts b/x-pack/plugins/endpoint/public/applications/endpoint/lib/index.ts similarity index 89% rename from x-pack/test/typings/hapi_basic.d.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/lib/index.ts index b7b27a10599a8..ba2e1ce8f9fe6 100644 --- a/x-pack/test/typings/hapi_basic.d.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/lib/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -declare module '@hapi/basic'; +export * from './saga'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.test.ts new file mode 100644 index 0000000000000..0387eac0e7c7f --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.test.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { createSagaMiddleware, SagaContext } from './index'; +import { applyMiddleware, createStore, Reducer } from 'redux'; + +describe('saga', () => { + const INCREMENT_COUNTER = 'INCREMENT'; + const DELAYED_INCREMENT_COUNTER = 'DELAYED INCREMENT COUNTER'; + const STOP_SAGA_PROCESSING = 'BREAK ASYNC ITERATOR'; + + const sleep = (ms = 1000) => new Promise(resolve => setTimeout(resolve, ms)); + let reducerA: Reducer; + let sideAffect: (a: unknown, s: unknown) => void; + let sagaExe: (sagaContext: SagaContext) => Promise; + + beforeEach(() => { + reducerA = jest.fn((prevState = { count: 0 }, { type }) => { + switch (type) { + case INCREMENT_COUNTER: + return { ...prevState, count: prevState.count + 1 }; + default: + return prevState; + } + }); + + sideAffect = jest.fn(); + + sagaExe = jest.fn(async ({ actionsAndState, dispatch }: SagaContext) => { + for await (const { action, state } of actionsAndState()) { + expect(action).toBeDefined(); + expect(state).toBeDefined(); + + if (action.type === STOP_SAGA_PROCESSING) { + break; + } + + sideAffect(action, state); + + if (action.type === DELAYED_INCREMENT_COUNTER) { + await sleep(1); + dispatch({ + type: INCREMENT_COUNTER, + }); + } + } + }); + }); + + test('it returns Redux Middleware from createSagaMiddleware()', () => { + const sagaMiddleware = createSagaMiddleware(async () => {}); + expect(sagaMiddleware).toBeInstanceOf(Function); + }); + test('it does nothing if saga is not started', () => { + const store = createStore(reducerA, applyMiddleware(createSagaMiddleware(sagaExe))); + expect(store.getState().count).toEqual(0); + expect(reducerA).toHaveBeenCalled(); + expect(sagaExe).toHaveBeenCalled(); + expect(sideAffect).not.toHaveBeenCalled(); + expect(store.getState()).toEqual({ count: 0 }); + }); + test('it updates store once running', async () => { + const sagaMiddleware = createSagaMiddleware(sagaExe); + const store = createStore(reducerA, applyMiddleware(sagaMiddleware)); + + expect(store.getState()).toEqual({ count: 0 }); + expect(sagaExe).toHaveBeenCalled(); + + store.dispatch({ type: DELAYED_INCREMENT_COUNTER }); + expect(store.getState()).toEqual({ count: 0 }); + + await sleep(100); + + expect(sideAffect).toHaveBeenCalled(); + expect(store.getState()).toEqual({ count: 1 }); + }); + test('it stops processing if break out of loop', async () => { + const sagaMiddleware = createSagaMiddleware(sagaExe); + const store = createStore(reducerA, applyMiddleware(sagaMiddleware)); + + store.dispatch({ type: DELAYED_INCREMENT_COUNTER }); + await sleep(100); + + expect(store.getState()).toEqual({ count: 1 }); + expect(sideAffect).toHaveBeenCalledTimes(2); + + store.dispatch({ type: STOP_SAGA_PROCESSING }); + await sleep(100); + + expect(store.getState()).toEqual({ count: 1 }); + expect(sideAffect).toHaveBeenCalledTimes(2); + + store.dispatch({ type: DELAYED_INCREMENT_COUNTER }); + await sleep(100); + + expect(store.getState()).toEqual({ count: 1 }); + expect(sideAffect).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.ts b/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.ts new file mode 100644 index 0000000000000..b93360ec6b5aa --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.ts @@ -0,0 +1,129 @@ +/* + * 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 { AnyAction, Dispatch, Middleware, MiddlewareAPI } from 'redux'; +import { GlobalState } from '../store'; + +interface QueuedAction { + /** + * The Redux action that was dispatched + */ + action: TAction; + /** + * The Global state at the time the action was dispatched + */ + state: GlobalState; +} + +interface IteratorInstance { + queue: QueuedAction[]; + nextResolve: null | ((inst: QueuedAction) => void); +} + +type Saga = (storeContext: SagaContext) => Promise; + +type StoreActionsAndState = AsyncIterableIterator>; + +export interface SagaContext { + /** + * A generator function that will `yield` `Promise`s that resolve with a `QueuedAction` + */ + actionsAndState: () => StoreActionsAndState; + dispatch: Dispatch; +} + +const noop = () => {}; + +/** + * Creates Saga Middleware for use with Redux. + * + * @param {Saga} saga The `saga` should initialize a long-running `for await...of` loop against + * the return value of the `actionsAndState()` method provided by the `SagaContext`. + * + * @return {Middleware} + * + * @example + * + * type TPossibleActions = { type: 'add', payload: any[] }; + * //... + * const endpointsSaga = async ({ actionsAndState, dispatch }: SagaContext) => { + * for await (const { action, state } of actionsAndState()) { + * if (action.type === "userRequestedResource") { + * const resourceData = await doApiFetch('of/some/resource'); + * dispatch({ + * type: 'add', + * payload: [ resourceData ] + * }); + * } + * } + * } + * const endpointsSagaMiddleware = createSagaMiddleware(endpointsSaga); + * //.... + * const store = createStore(reducers, [ endpointsSagaMiddleware ]); + */ +export function createSagaMiddleware(saga: Saga): Middleware { + const iteratorInstances = new Set(); + let runSaga: () => void = noop; + + async function* getActionsAndStateIterator(): StoreActionsAndState { + const instance: IteratorInstance = { queue: [], nextResolve: null }; + iteratorInstances.add(instance); + try { + while (true) { + yield await nextActionAndState(); + } + } finally { + // If the consumer stops consuming this (e.g. `break` or `return` is called in the `for await` + // then this `finally` block will run and unregister this instance and reset `runSaga` + iteratorInstances.delete(instance); + runSaga = noop; + } + + function nextActionAndState() { + if (instance.queue.length) { + return Promise.resolve(instance.queue.shift() as QueuedAction); + } else { + return new Promise(function(resolve) { + instance.nextResolve = resolve; + }); + } + } + } + + function enqueue(value: QueuedAction) { + for (const iteratorInstance of iteratorInstances) { + iteratorInstance.queue.push(value); + if (iteratorInstance.nextResolve !== null) { + iteratorInstance.nextResolve(iteratorInstance.queue.shift() as QueuedAction); + iteratorInstance.nextResolve = null; + } + } + } + + function middleware({ getState, dispatch }: MiddlewareAPI) { + if (runSaga === noop) { + runSaga = saga.bind>(null, { + actionsAndState: getActionsAndStateIterator, + dispatch, + }); + runSaga(); + } + return (next: Dispatch) => (action: AnyAction) => { + // Call the next dispatch method in the middleware chain. + const returnValue = next(action); + + enqueue({ + action, + state: getState(), + }); + + // This will likely be the action itself, unless a middleware further in chain changed it. + return returnValue; + }; + } + + return middleware; +} diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/actions.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/actions.ts new file mode 100644 index 0000000000000..796dabce1d76a --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/actions.ts @@ -0,0 +1,9 @@ +/* + * 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 { EndpointListAction } from './endpoint_list'; + +export type AppAction = EndpointListAction; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/action.ts new file mode 100644 index 0000000000000..02ec0f9d09035 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/action.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 { EndpointListData } from './types'; + +interface ServerReturnedEndpointList { + type: 'serverReturnedEndpointList'; + payload: EndpointListData; +} + +interface UserEnteredEndpointListPage { + type: 'userEnteredEndpointListPage'; +} + +interface UserExitedEndpointListPage { + type: 'userExitedEndpointListPage'; +} + +export type EndpointListAction = + | ServerReturnedEndpointList + | UserEnteredEndpointListPage + | UserExitedEndpointListPage; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/index.test.ts new file mode 100644 index 0000000000000..a46653f82ee45 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/index.test.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 { createStore, Dispatch, Store } from 'redux'; +import { EndpointListAction, EndpointData, endpointListReducer, EndpointListState } from './index'; +import { endpointListData } from './selectors'; + +describe('endpoint_list store concerns', () => { + let store: Store; + let dispatch: Dispatch; + const createTestStore = () => { + store = createStore(endpointListReducer); + dispatch = store.dispatch; + }; + const generateEndpoint = (): EndpointData => { + return { + machine_id: Math.random() + .toString(16) + .substr(2), + created_at: new Date(), + host: { + name: '', + hostname: '', + ip: '', + mac_address: '', + os: { + name: '', + full: '', + }, + }, + endpoint: { + domain: '', + is_base_image: true, + active_directory_distinguished_name: '', + active_directory_hostname: '', + upgrade: { + status: '', + updated_at: new Date(), + }, + isolation: { + status: false, + request_status: true, + updated_at: new Date(), + }, + policy: { + name: '', + id: '', + }, + sensor: { + persistence: true, + status: {}, + }, + }, + }; + }; + const loadDataToStore = () => { + dispatch({ + type: 'serverReturnedEndpointList', + payload: { + endpoints: [generateEndpoint()], + request_page_size: 1, + request_index: 1, + total: 10, + }, + }); + }; + + describe('# Reducers', () => { + beforeEach(() => { + createTestStore(); + }); + + test('it creates default state', () => { + expect(store.getState()).toEqual({ + endpoints: [], + request_page_size: 10, + request_index: 0, + total: 0, + }); + }); + + test('it handles `serverReturnedEndpointList', () => { + const payload = { + endpoints: [generateEndpoint()], + request_page_size: 1, + request_index: 1, + total: 10, + }; + dispatch({ + type: 'serverReturnedEndpointList', + payload, + }); + + const currentState = store.getState(); + expect(currentState.endpoints).toEqual(payload.endpoints); + expect(currentState.request_page_size).toEqual(payload.request_page_size); + expect(currentState.request_index).toEqual(payload.request_index); + expect(currentState.total).toEqual(payload.total); + }); + + test('it handles `userExitedEndpointListPage`', () => { + loadDataToStore(); + + expect(store.getState().total).toEqual(10); + + dispatch({ type: 'userExitedEndpointListPage' }); + expect(store.getState().endpoints.length).toEqual(0); + expect(store.getState().request_index).toEqual(0); + }); + }); + + describe('# Selectors', () => { + beforeEach(() => { + createTestStore(); + loadDataToStore(); + }); + + test('it selects `endpointListData`', () => { + const currentState = store.getState(); + expect(endpointListData(currentState)).toEqual(currentState.endpoints); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/index.ts new file mode 100644 index 0000000000000..bdf0708457bb0 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/index.ts @@ -0,0 +1,10 @@ +/* + * 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 { endpointListReducer } from './reducer'; +export { EndpointListAction } from './action'; +export { endpointListSaga } from './saga'; +export * from './types'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/reducer.ts new file mode 100644 index 0000000000000..9813777c988ef --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/reducer.ts @@ -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 { EndpointListState } from './types'; +import { EndpointListAction } from './action'; + +const initialState = (): EndpointListState => { + return { + endpoints: [], + request_page_size: 10, + request_index: 0, + total: 0, + }; +}; + +export const endpointListReducer = (state = initialState(), action: EndpointListAction) => { + if (action.type === 'serverReturnedEndpointList') { + return { + ...state, + ...action.payload, + }; + } + + if (action.type === 'userExitedEndpointListPage') { + return initialState(); + } + + return state; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/saga.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/saga.test.ts new file mode 100644 index 0000000000000..92bf3b7fd92dd --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/saga.test.ts @@ -0,0 +1,111 @@ +/* + * 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 { CoreStart, HttpSetup } from 'kibana/public'; +import { applyMiddleware, createStore, Dispatch, Store } from 'redux'; +import { createSagaMiddleware, SagaContext } from '../../lib'; +import { endpointListSaga } from './saga'; +import { coreMock } from '../../../../../../../../src/core/public/mocks'; +import { + EndpointData, + EndpointListAction, + EndpointListData, + endpointListReducer, + EndpointListState, +} from './index'; +import { endpointListData } from './selectors'; + +describe('endpoint list saga', () => { + const sleep = (ms = 100) => new Promise(wakeup => setTimeout(wakeup, ms)); + let fakeCoreStart: jest.Mocked; + let fakeHttpServices: jest.Mocked; + let store: Store; + let dispatch: Dispatch; + + // TODO: consolidate the below ++ helpers in `index.test.ts` into a `test_helpers.ts`?? + const generateEndpoint = (): EndpointData => { + return { + machine_id: Math.random() + .toString(16) + .substr(2), + created_at: new Date(), + host: { + name: '', + hostname: '', + ip: '', + mac_address: '', + os: { + name: '', + full: '', + }, + }, + endpoint: { + domain: '', + is_base_image: true, + active_directory_distinguished_name: '', + active_directory_hostname: '', + upgrade: { + status: '', + updated_at: new Date(), + }, + isolation: { + status: false, + request_status: true, + updated_at: new Date(), + }, + policy: { + name: '', + id: '', + }, + sensor: { + persistence: true, + status: {}, + }, + }, + }; + }; + const getEndpointListApiResponse = (): EndpointListData => { + return { + endpoints: [generateEndpoint()], + request_page_size: 1, + request_index: 1, + total: 10, + }; + }; + + const endpointListSagaFactory = () => { + return async (sagaContext: SagaContext) => { + await endpointListSaga(sagaContext, fakeCoreStart).catch((e: Error) => { + // eslint-disable-next-line no-console + console.error(e); + return Promise.reject(e); + }); + }; + }; + + beforeEach(() => { + fakeCoreStart = coreMock.createStart({ basePath: '/mock' }); + fakeHttpServices = fakeCoreStart.http as jest.Mocked; + store = createStore( + endpointListReducer, + applyMiddleware(createSagaMiddleware(endpointListSagaFactory())) + ); + dispatch = store.dispatch; + }); + + test('it handles `userEnteredEndpointListPage`', async () => { + const apiResponse = getEndpointListApiResponse(); + + fakeHttpServices.post.mockResolvedValue(apiResponse); + expect(fakeHttpServices.post).not.toHaveBeenCalled(); + + dispatch({ type: 'userEnteredEndpointListPage' }); + await sleep(); + + expect(fakeHttpServices.post).toHaveBeenCalledWith('/api/endpoint/endpoints'); + expect(endpointListData(store.getState())).toEqual(apiResponse.endpoints); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/saga.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/saga.ts new file mode 100644 index 0000000000000..cc156cfa88002 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/saga.ts @@ -0,0 +1,26 @@ +/* + * 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 { CoreStart } from 'kibana/public'; +import { SagaContext } from '../../lib'; +import { EndpointListAction } from './action'; + +export const endpointListSaga = async ( + { actionsAndState, dispatch }: SagaContext, + coreStart: CoreStart +) => { + const { post: httpPost } = coreStart.http; + + for await (const { action } of actionsAndState()) { + if (action.type === 'userEnteredEndpointListPage') { + const response = await httpPost('/api/endpoint/endpoints'); + dispatch({ + type: 'serverReturnedEndpointList', + payload: response, + }); + } + } +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/selectors.ts new file mode 100644 index 0000000000000..6ffcebc3f41aa --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/selectors.ts @@ -0,0 +1,9 @@ +/* + * 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 { EndpointListState } from './types'; + +export const endpointListData = (state: EndpointListState) => state.endpoints; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/types.ts new file mode 100644 index 0000000000000..f2810dd89f857 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/endpoint_list/types.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. + */ + +// FIXME: temporary until server defined `interface` is moved +export interface EndpointData { + machine_id: string; + created_at: Date; + host: { + name: string; + hostname: string; + ip: string; + mac_address: string; + os: { + name: string; + full: string; + }; + }; + endpoint: { + domain: string; + is_base_image: boolean; + active_directory_distinguished_name: string; + active_directory_hostname: string; + upgrade: { + status?: string; + updated_at?: Date; + }; + isolation: { + status: boolean; + request_status?: string | boolean; + updated_at?: Date; + }; + policy: { + name: string; + id: string; + }; + sensor: { + persistence: boolean; + status: object; + }; + }; +} + +// FIXME: temporary until server defined `interface` is moved to a module we can reference +export interface EndpointListData { + endpoints: EndpointData[]; + request_page_size: number; + request_index: number; + total: number; +} + +export type EndpointListState = EndpointListData; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts new file mode 100644 index 0000000000000..d0dc002031ce2 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/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 { createStore, compose, applyMiddleware } from 'redux'; +import { CoreStart } from 'kibana/public'; +import { appSagaFactory } from './saga'; +import { appReducer } from './reducer'; + +export { GlobalState } from './reducer'; + +const composeWithReduxDevTools = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ + ? (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ name: 'EndpointApp' }) + : compose; + +export const appStoreFactory = (coreStart: CoreStart) => { + const store = createStore( + appReducer, + composeWithReduxDevTools(applyMiddleware(appSagaFactory(coreStart))) + ); + return store; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts new file mode 100644 index 0000000000000..59ca4de91ac83 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.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 { combineReducers, Reducer } from 'redux'; +import { endpointListReducer, EndpointListState } from './endpoint_list'; +import { AppAction } from './actions'; + +export interface GlobalState { + endpointList: EndpointListState; +} + +export const appReducer: Reducer = combineReducers({ + endpointList: endpointListReducer, +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/saga.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/saga.ts new file mode 100644 index 0000000000000..3b7de79d5443c --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/saga.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 { CoreStart } from 'kibana/public'; +import { createSagaMiddleware, SagaContext } from '../lib'; +import { endpointListSaga } from './endpoint_list'; + +export const appSagaFactory = (coreStart: CoreStart) => { + return createSagaMiddleware(async (sagaContext: SagaContext) => { + await Promise.all([ + // Concerns specific sagas here + endpointListSaga(sagaContext, coreStart), + ]); + }); +}; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx index c439e3318d3c9..d252988d0cdcc 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/index.tsx @@ -159,5 +159,12 @@ const Resolver = styled( */ display: flex; flex-grow: 1; + /** + * The placeholder components use absolute positioning. + */ position: relative; + /** + * Prevent partially visible components from showing up outside the bounds of Resolver. + */ + overflow: hidden; `; diff --git a/x-pack/plugins/task_manager/server/config.ts b/x-pack/plugins/task_manager/server/config.ts index 06e6ad3e62282..e2752f80ad6c4 100644 --- a/x-pack/plugins/task_manager/server/config.ts +++ b/x-pack/plugins/task_manager/server/config.ts @@ -6,6 +6,9 @@ import { schema, TypeOf } from '@kbn/config-schema'; +export const DEFAULT_MAX_WORKERS = 10; +export const DEFAULT_POLL_INTERVAL = 3000; + export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), /* The maximum number of times a task will be attempted before being abandoned as failed */ @@ -15,7 +18,7 @@ export const configSchema = schema.object({ }), /* How often, in milliseconds, the task manager will look for more work. */ poll_interval: schema.number({ - defaultValue: 3000, + defaultValue: DEFAULT_POLL_INTERVAL, min: 100, }), /* How many requests can Task Manager buffer before it rejects new requests. */ @@ -35,7 +38,7 @@ export const configSchema = schema.object({ }), /* The maximum number of tasks that this Kibana instance will run simultaneously. */ max_workers: schema.number({ - defaultValue: 10, + defaultValue: DEFAULT_MAX_WORKERS, // disable the task manager rather than trying to specify it with 0 workers min: 1, }), diff --git a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts index 93a8187b673be..ac98fbbda5aa2 100644 --- a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts +++ b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.test.ts @@ -9,9 +9,9 @@ import { asUpdateByQuery, shouldBeOneOf, mustBeAllOf, - ExistsBoolClause, - TermBoolClause, - RangeBoolClause, + ExistsFilter, + TermFilter, + RangeFilter, } from './query_clauses'; import { @@ -51,7 +51,7 @@ describe('mark_available_tasks_as_claimed', () => { // status running or claiming with a retryAt <= now. shouldBeOneOf(IdleTaskWithExpiredRunAt, RunningOrClaimingTaskWithExpiredRetryAt), // Either task has an schedule or the attempts < the maximum configured - shouldBeOneOf( + shouldBeOneOf( TaskWithSchedule, ...Object.entries(definitions).map(([type, { maxAttempts }]) => taskWithLessThanMaxAttempts(type, maxAttempts || defaultMaxAttempts) diff --git a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts index 6691b31a546bc..b0d9dc61c9667 100644 --- a/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts +++ b/x-pack/plugins/task_manager/server/queries/mark_available_tasks_as_claimed.ts @@ -3,24 +3,24 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { defaultsDeep } from 'lodash'; import { - BoolClause, - IDsClause, SortClause, ScriptClause, - ExistsBoolClause, - TermBoolClause, - RangeBoolClause, + ExistsFilter, + TermFilter, + RangeFilter, + mustBeAllOf, + MustCondition, + MustNotCondition, } from './query_clauses'; -export const TaskWithSchedule: ExistsBoolClause = { +export const TaskWithSchedule: ExistsFilter = { exists: { field: 'task.schedule' }, }; export function taskWithLessThanMaxAttempts( type: string, maxAttempts: number -): BoolClause { +): MustCondition { return { bool: { must: [ @@ -37,34 +37,37 @@ export function taskWithLessThanMaxAttempts( }; } -export const IdleTaskWithExpiredRunAt: BoolClause = { +export function tasksClaimedByOwner(taskManagerId: string) { + return mustBeAllOf( + { + term: { + 'task.ownerId': taskManagerId, + }, + }, + { term: { 'task.status': 'claiming' } } + ); +} + +export const IdleTaskWithExpiredRunAt: MustCondition = { bool: { must: [{ term: { 'task.status': 'idle' } }, { range: { 'task.runAt': { lte: 'now' } } }], }, }; -export const taskWithIDsAndRunnableStatus = ( - claimTasksById: string[] -): BoolClause => ({ +export const InactiveTasks: MustNotCondition = { bool: { - must: [ + must_not: [ { bool: { - should: [{ term: { 'task.status': 'idle' } }, { term: { 'task.status': 'failed' } }], - }, - }, - { - ids: { - values: claimTasksById, + should: [{ term: { 'task.status': 'running' } }, { term: { 'task.status': 'claiming' } }], }, }, + { range: { 'task.retryAt': { gt: 'now' } } }, ], }, -}); +}; -export const RunningOrClaimingTaskWithExpiredRetryAt: BoolClause< - TermBoolClause | RangeBoolClause -> = { +export const RunningOrClaimingTaskWithExpiredRetryAt: MustCondition = { bool: { must: [ { @@ -95,31 +98,6 @@ if (doc['task.runAt'].size()!=0) { }, }; -const SORT_VALUE_TO_BE_FIRST = 0; -export const sortByIdsThenByScheduling = (claimTasksById: string[]): SortClause => { - const { - _script: { - script: { source }, - }, - } = SortByRunAtAndRetryAt; - return defaultsDeep( - { - _script: { - script: { - source: ` -if(params.ids.contains(doc['_id'].value)){ - return ${SORT_VALUE_TO_BE_FIRST}; -} -${source} -`, - params: { ids: claimTasksById }, - }, - }, - }, - SortByRunAtAndRetryAt - ); -}; - export const updateFields = (fieldUpdates: { [field: string]: string | number | Date; }): ScriptClause => ({ diff --git a/x-pack/plugins/task_manager/server/queries/query_clauses.test.ts b/x-pack/plugins/task_manager/server/queries/query_clauses.test.ts new file mode 100644 index 0000000000000..beb8f864bd754 --- /dev/null +++ b/x-pack/plugins/task_manager/server/queries/query_clauses.test.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import { + MustCondition, + shouldBeOneOf, + mustBeAllOf, + ExistsFilter, + TermFilter, + RangeFilter, + matchesClauses, +} from './query_clauses'; + +describe('matchesClauses', () => { + test('merges multiple types of Bool Clauses into one', () => { + const TaskWithSchedule: ExistsFilter = { + exists: { field: 'task.schedule' }, + }; + + const IdleTaskWithExpiredRunAt: MustCondition = { + bool: { + must: [{ term: { 'task.status': 'idle' } }, { range: { 'task.runAt': { lte: 'now' } } }], + }, + }; + + const RunningTask: MustCondition = { + bool: { + must: [{ term: { 'task.status': 'running' } }], + }, + }; + + expect( + matchesClauses( + mustBeAllOf(TaskWithSchedule), + shouldBeOneOf( + RunningTask, + IdleTaskWithExpiredRunAt + ) + ) + ).toMatchObject({ + bool: { + must: [TaskWithSchedule], + should: [RunningTask, IdleTaskWithExpiredRunAt], + }, + }); + }); +}); diff --git a/x-pack/plugins/task_manager/server/queries/query_clauses.ts b/x-pack/plugins/task_manager/server/queries/query_clauses.ts index 1f76ce99e600a..de7e3b085ba2d 100644 --- a/x-pack/plugins/task_manager/server/queries/query_clauses.ts +++ b/x-pack/plugins/task_manager/server/queries/query_clauses.ts @@ -4,30 +4,150 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface TermBoolClause { +/** + * Terminology + * =========== + * The terms for the differenct clauses in an Elasticsearch query can be confusing, here are some + * clarifications that might help you understand the Typescript types we use here. + * + * Given the following Query: + * { + * "query": { (1) + * "bool": { (2) + * "must": + * [ + * (3) { "term" : { "tag" : "wow" } }, + * { "term" : { "tag" : "elasticsearch" } }, + * { + * "must" : { "term" : { "user" : "kimchy" } } + * } + * ] + * } + * } + * } + * + * These are referred to as: + * (1). BoolClause / BoolClauseWithAnyCondition + * (2). BoolCondition / AnyBoolCondition + * (3). BoolClauseFilter + * + */ + +export interface TermFilter { term: { [field: string]: string | string[] }; } -export interface RangeBoolClause { - range: { [field: string]: { lte: string | number } | { lt: string | number } }; +export interface RangeFilter { + range: { + [field: string]: { lte: string | number } | { lt: string | number } | { gt: string | number }; + }; } -export interface ExistsBoolClause { +export interface ExistsFilter { exists: { field: string }; } -export interface IDsClause { - ids: { - values: string[]; - }; -} -export interface ShouldClause { - should: Array | IDsClause | T>; +type BoolClauseFilter = TermFilter | RangeFilter | ExistsFilter; +type BoolClauseFiltering = + | BoolClauseWithAnyCondition + | PinnedQuery + | T; + +enum Conditions { + Should = 'should', + Must = 'must', + MustNot = 'must_not', + Filter = 'filter', } -export interface MustClause { - must: Array | IDsClause | T>; + +/** + * Describe a specific BoolClause Condition with a BoolClauseFilter on it, such as: + * ``` + * { + * must : [ + * T, ... + * ] + * } + * ``` + */ +type BoolCondition = { + [c in C]: Array>; +}; + +/** + * Describe a Bool clause with a specific Condition, such as: + * ``` + * { + * // described by BoolClause + * bool: { + * // described by BoolCondition + * must: [ + * T, ... + * ] + * } + * } + * ``` + */ +interface BoolClause { + bool: BoolCondition; } -export interface BoolClause { - bool: MustClause | ShouldClause; + +/** + * Describe a Bool clause with mixed Conditions, such as: + * ``` + * { + * // described by BoolClause<...> + * bool: { + * // described by BoolCondition + * must : { + * ... + * }, + * // described by BoolCondition + * should : { + * ... + * } + * } + * } + * ``` + */ +type AnyBoolCondition = { + [Condition in Conditions]?: Array>; +}; + +/** + * Describe a Bool Condition with any Condition on it, so it can handle both: + * ``` + * { + * bool: { + * must : { + * ... + * } + * } + * } + * ``` + * + * and: + * + * ``` + * { + * bool: { + * must_not : { + * ... + * } + * } + * } + * ``` + */ +export interface BoolClauseWithAnyCondition { + bool: AnyBoolCondition; } + +/** + * Describe the various Bool Clause Conditions we support, as specified in the Conditions enum + */ +export type ShouldCondition = BoolClause; +export type MustCondition = BoolClause; +export type MustNotCondition = BoolClause; +export type FilterCondition = BoolClause; + export interface SortClause { _script: { type: string; @@ -39,6 +159,8 @@ export interface SortClause { }; }; } +export type SortOptions = string | SortClause | Array; + export interface ScriptClause { source: string; lang: string; @@ -46,18 +168,34 @@ export interface ScriptClause { [field: string]: string | number | Date; }; } -export interface UpdateByQuery { - query: BoolClause; - sort: SortClause; + +export interface UpdateByQuery { + query: PinnedQuery | BoolClauseWithAnyCondition; + sort: SortOptions; seq_no_primary_term: true; script: ScriptClause; } -export function shouldBeOneOf( - ...should: Array | IDsClause | T> -): { - bool: ShouldClause; -} { +export interface PinnedQuery { + pinned: PinnedClause; +} + +export interface PinnedClause { + ids: string[]; + organic: BoolClauseWithAnyCondition; +} + +export function matchesClauses( + ...clauses: Array> +): BoolClauseWithAnyCondition { + return { + bool: Object.assign({}, ...clauses.map(clause => clause.bool)), + }; +} + +export function shouldBeOneOf( + ...should: Array> +): ShouldCondition { return { bool: { should, @@ -65,11 +203,9 @@ export function shouldBeOneOf( }; } -export function mustBeAllOf( - ...must: Array | IDsClause | T> -): { - bool: MustClause; -} { +export function mustBeAllOf( + ...must: Array> +): MustCondition { return { bool: { must, @@ -77,14 +213,36 @@ export function mustBeAllOf( }; } -export function asUpdateByQuery({ +export function filterDownBy( + ...filter: Array> +): FilterCondition { + return { + bool: { + filter, + }, + }; +} + +export function asPinnedQuery( + ids: PinnedClause['ids'], + organic: PinnedClause['organic'] +): PinnedQuery { + return { + pinned: { + ids, + organic, + }, + }; +} + +export function asUpdateByQuery({ query, update, sort, }: { - query: BoolClause; - update: ScriptClause; - sort: SortClause; + query: UpdateByQuery['query']; + update: UpdateByQuery['script']; + sort: UpdateByQuery['sort']; }): UpdateByQuery { return { query, diff --git a/x-pack/plugins/task_manager/server/task_manager.ts b/x-pack/plugins/task_manager/server/task_manager.ts index c0baed3708a0a..93e98f33a30b0 100644 --- a/x-pack/plugins/task_manager/server/task_manager.ts +++ b/x-pack/plugins/task_manager/server/task_manager.ts @@ -48,11 +48,11 @@ import { createTaskPoller, PollingError, PollingErrorType } from './task_poller' import { TaskPool } from './task_pool'; import { TaskManagerRunner, TaskRunner } from './task_runner'; import { - FetchOpts, FetchResult, TaskStore, OwnershipClaimingOpts, ClaimOwnershipResult, + SearchOpts, } from './task_store'; import { identifyEsError } from './lib/identify_es_error'; import { ensureDeprecatedFieldsAreCorrected } from './lib/correct_deprecated_fields'; @@ -323,12 +323,12 @@ export class TaskManager { } /** - * Fetches a paginatable list of scheduled tasks. + * Fetches a list of scheduled tasks. * * @param opts - The query options used to filter tasks * @returns {Promise} */ - public async fetch(opts: FetchOpts): Promise { + public async fetch(opts: SearchOpts): Promise { await this.waitUntilStarted(); return this.store.fetch(opts); } diff --git a/x-pack/plugins/task_manager/server/task_store.test.ts b/x-pack/plugins/task_manager/server/task_store.test.ts index f47cc41c2d045..e6cce7a664d91 100644 --- a/x-pack/plugins/task_manager/server/task_store.test.ts +++ b/x-pack/plugins/task_manager/server/task_store.test.ts @@ -16,7 +16,7 @@ import { TaskStatus, TaskLifecycleResult, } from './task'; -import { FetchOpts, StoreOpts, OwnershipClaimingOpts, TaskStore } from './task_store'; +import { StoreOpts, OwnershipClaimingOpts, TaskStore, SearchOpts } from './task_store'; import { savedObjectsRepositoryMock } from '../../../../src/core/server/mocks'; import { SavedObjectsSerializer, @@ -175,7 +175,7 @@ describe('TaskStore', () => { }); describe('fetch', () => { - async function testFetch(opts?: FetchOpts, hits: any[] = []) { + async function testFetch(opts?: SearchOpts, hits: any[] = []) { const callCluster = sinon.spy(async (name: string, params?: any) => ({ hits: { hits } })); const store = new TaskStore({ index: 'tasky', @@ -203,7 +203,7 @@ describe('TaskStore', () => { expect(args).toMatchObject({ index: 'tasky', body: { - sort: [{ 'task.runAt': 'asc' }, { _id: 'desc' }], + sort: [{ 'task.runAt': 'asc' }], query: { term: { type: 'task' } }, }, }); @@ -226,122 +226,6 @@ describe('TaskStore', () => { }, }); }); - - test('sorts by id if custom sort does not have an id sort in it', async () => { - const { args } = await testFetch({ - sort: [{ 'task.taskType': 'desc' }], - }); - - expect(args).toMatchObject({ - body: { - sort: [{ 'task.taskType': 'desc' }, { _id: 'desc' }], - }, - }); - }); - - test('allows custom sort by id', async () => { - const { args } = await testFetch({ - sort: [{ _id: 'asc' }], - }); - - expect(args).toMatchObject({ - body: { - sort: [{ _id: 'asc' }], - }, - }); - }); - - test('allows specifying pagination', async () => { - const now = new Date(); - const searchAfter = [now, '143243sdafa32']; - const { args } = await testFetch({ - searchAfter, - }); - - expect(args).toMatchObject({ - body: { - search_after: searchAfter, - }, - }); - }); - - test('returns paginated tasks', async () => { - const runAt = new Date(); - const { result } = await testFetch(undefined, [ - { - _id: 'aaa', - _source: { - type: 'task', - task: { - runAt, - taskType: 'foo', - schedule: undefined, - attempts: 0, - status: 'idle', - params: '{ "hello": "world" }', - state: '{ "baby": "Henhen" }', - user: 'jimbo', - scope: ['reporting'], - }, - }, - sort: ['a', 1], - }, - { - _id: 'bbb', - _source: { - type: 'task', - task: { - runAt, - taskType: 'bar', - schedule: { interval: '5m' }, - attempts: 2, - status: 'running', - params: '{ "shazm": 1 }', - state: '{ "henry": "The 8th" }', - user: 'dabo', - scope: ['reporting', 'ceo'], - }, - }, - sort: ['b', 2], - }, - ]); - - expect(result).toEqual({ - docs: [ - { - attempts: 0, - id: 'aaa', - schedule: undefined, - params: { hello: 'world' }, - runAt, - scheduledAt: mockedDate, - scope: ['reporting'], - state: { baby: 'Henhen' }, - status: 'idle', - taskType: 'foo', - user: 'jimbo', - retryAt: undefined, - startedAt: undefined, - }, - { - attempts: 2, - id: 'bbb', - schedule: { interval: '5m' }, - params: { shazm: 1 }, - runAt, - scheduledAt: mockedDate, - scope: ['reporting', 'ceo'], - state: { henry: 'The 8th' }, - status: 'running', - taskType: 'bar', - user: 'dabo', - retryAt: undefined, - startedAt: undefined, - }, - ], - searchAfter: ['b', 2], - }); - }); }); describe('claimAvailableTasks', () => { @@ -450,65 +334,88 @@ describe('TaskStore', () => { must: [ { bool: { - should: [ + must: [ { bool: { - must: [ - { term: { 'task.status': 'idle' } }, - { range: { 'task.runAt': { lte: 'now' } } }, + should: [ + { + bool: { + must: [ + { term: { 'task.status': 'idle' } }, + { range: { 'task.runAt': { lte: 'now' } } }, + ], + }, + }, + { + bool: { + must: [ + { + bool: { + should: [ + { term: { 'task.status': 'running' } }, + { term: { 'task.status': 'claiming' } }, + ], + }, + }, + { range: { 'task.retryAt': { lte: 'now' } } }, + ], + }, + }, ], }, }, { bool: { - must: [ + should: [ + { exists: { field: 'task.schedule' } }, + { + bool: { + must: [ + { term: { 'task.taskType': 'foo' } }, + { + range: { + 'task.attempts': { + lt: maxAttempts, + }, + }, + }, + ], + }, + }, { bool: { - should: [ - { term: { 'task.status': 'running' } }, - { term: { 'task.status': 'claiming' } }, + must: [ + { term: { 'task.taskType': 'bar' } }, + { + range: { + 'task.attempts': { + lt: customMaxAttempts, + }, + }, + }, ], }, }, - { range: { 'task.retryAt': { lte: 'now' } } }, ], }, }, ], }, }, + ], + filter: [ { bool: { - should: [ - { exists: { field: 'task.schedule' } }, - { - bool: { - must: [ - { term: { 'task.taskType': 'foo' } }, - { - range: { - 'task.attempts': { - lt: maxAttempts, - }, - }, - }, - ], - }, - }, + must_not: [ { bool: { - must: [ - { term: { 'task.taskType': 'bar' } }, - { - range: { - 'task.attempts': { - lt: customMaxAttempts, - }, - }, - }, + should: [ + { term: { 'task.status': 'running' } }, + { term: { 'task.status': 'claiming' } }, ], }, }, + { range: { 'task.retryAt': { gt: 'now' } } }, ], }, }, @@ -562,96 +469,99 @@ describe('TaskStore', () => { { term: { type: 'task' } }, { bool: { - should: [ + must: [ { - bool: { - must: [ - { - bool: { - should: [ - { - bool: { - must: [ - { term: { 'task.status': 'idle' } }, - { range: { 'task.runAt': { lte: 'now' } } }, - ], - }, - }, - { - bool: { - must: [ - { - bool: { - should: [ - { term: { 'task.status': 'running' } }, - { term: { 'task.status': 'claiming' } }, - ], - }, + pinned: { + ids: [ + 'task:33c6977a-ed6d-43bd-98d9-3f827f7b7cd8', + 'task:a208b22c-14ec-4fb4-995f-d2ff7a3b03b8', + ], + organic: { + bool: { + must: [ + { + bool: { + should: [ + { + bool: { + must: [ + { term: { 'task.status': 'idle' } }, + { range: { 'task.runAt': { lte: 'now' } } }, + ], }, - { range: { 'task.retryAt': { lte: 'now' } } }, - ], - }, - }, - ], - }, - }, - { - bool: { - should: [ - { exists: { field: 'task.schedule' } }, - { - bool: { - must: [ - { term: { 'task.taskType': 'foo' } }, - { - range: { - 'task.attempts': { - lt: maxAttempts, + }, + { + bool: { + must: [ + { + bool: { + should: [ + { term: { 'task.status': 'running' } }, + { term: { 'task.status': 'claiming' } }, + ], + }, }, - }, + { range: { 'task.retryAt': { lte: 'now' } } }, + ], }, - ], - }, + }, + ], }, - { - bool: { - must: [ - { term: { 'task.taskType': 'bar' } }, - { - range: { - 'task.attempts': { - lt: customMaxAttempts, + }, + { + bool: { + should: [ + { exists: { field: 'task.schedule' } }, + { + bool: { + must: [ + { term: { 'task.taskType': 'foo' } }, + { + range: { + 'task.attempts': { + lt: maxAttempts, + }, + }, }, - }, + ], }, - ], - }, + }, + { + bool: { + must: [ + { term: { 'task.taskType': 'bar' } }, + { + range: { + 'task.attempts': { + lt: customMaxAttempts, + }, + }, + }, + ], + }, + }, + ], }, - ], - }, + }, + ], }, - ], + }, }, }, + ], + filter: [ { bool: { - must: [ + must_not: [ { bool: { should: [ - { term: { 'task.status': 'idle' } }, - { term: { 'task.status': 'failed' } }, - ], - }, - }, - { - ids: { - values: [ - 'task:33c6977a-ed6d-43bd-98d9-3f827f7b7cd8', - 'task:a208b22c-14ec-4fb4-995f-d2ff7a3b03b8', + { term: { 'task.status': 'running' } }, + { term: { 'task.status': 'claiming' } }, ], }, }, + { range: { 'task.retryAt': { gt: 'now' } } }, ], }, }, @@ -662,34 +572,26 @@ describe('TaskStore', () => { }, }); - expect(sort).toMatchObject({ - _script: { - type: 'number', - order: 'asc', - script: { - lang: 'painless', - source: ` -if(params.ids.contains(doc['_id'].value)){ - return 0; -} - + expect(sort).toMatchObject([ + '_score', + { + _script: { + type: 'number', + order: 'asc', + script: { + lang: 'painless', + source: ` if (doc['task.retryAt'].size()!=0) { return doc['task.retryAt'].value.toInstant().toEpochMilli(); } if (doc['task.runAt'].size()!=0) { return doc['task.runAt'].value.toInstant().toEpochMilli(); } - -`, - params: { - ids: [ - 'task:33c6977a-ed6d-43bd-98d9-3f827f7b7cd8', - 'task:a208b22c-14ec-4fb4-995f-d2ff7a3b03b8', - ], + `, }, }, }, - }); + ]); }); test('it claims tasks by setting their ownerId, status and retryAt', async () => { diff --git a/x-pack/plugins/task_manager/server/task_store.ts b/x-pack/plugins/task_manager/server/task_store.ts index f4695b152237a..4f2e97704941f 100644 --- a/x-pack/plugins/task_manager/server/task_store.ts +++ b/x-pack/plugins/task_manager/server/task_store.ts @@ -36,22 +36,23 @@ import { asUpdateByQuery, shouldBeOneOf, mustBeAllOf, - ExistsBoolClause, - TermBoolClause, - RangeBoolClause, - BoolClause, - IDsClause, + filterDownBy, + ExistsFilter, + TermFilter, + RangeFilter, + asPinnedQuery, + matchesClauses, } from './queries/query_clauses'; import { updateFields, IdleTaskWithExpiredRunAt, + InactiveTasks, RunningOrClaimingTaskWithExpiredRetryAt, TaskWithSchedule, taskWithLessThanMaxAttempts, SortByRunAtAndRetryAt, - taskWithIDsAndRunnableStatus, - sortByIdsThenByScheduling, + tasksClaimedByOwner, } from './queries/mark_available_tasks_as_claimed'; export interface StoreOpts { @@ -65,18 +66,13 @@ export interface StoreOpts { } export interface SearchOpts { - searchAfter?: any[]; - sort?: object | object[]; + sort?: string | object | object[]; query?: object; size?: number; seq_no_primary_term?: boolean; search_after?: any[]; } -export interface FetchOpts extends SearchOpts { - sort?: object[]; -} - export interface UpdateByQuerySearchOpts extends SearchOpts { script?: object; } @@ -92,7 +88,6 @@ export interface OwnershipClaimingOpts { } export interface FetchResult { - searchAfter: any[]; docs: ConcreteTaskInstance[]; } @@ -152,8 +147,8 @@ export class TaskStore { return this.events$; } - private emitEvent = (event: TaskClaim) => { - this.events$.next(event); + private emitEvents = (events: TaskClaim[]) => { + events.forEach(event => this.events$.next(event)); }; /** @@ -180,16 +175,16 @@ export class TaskStore { } /** - * Fetches a paginatable list of scheduled tasks. + * Fetches a list of scheduled tasks with default sorting. * * @param opts - The query options used to filter tasks */ - public async fetch(opts: FetchOpts = {}): Promise { - const sort = paginatableSort(opts.sort); + public async fetch({ sort = [{ 'task.runAt': 'asc' }], ...opts }: SearchOpts = {}): Promise< + FetchResult + > { return this.search({ + ...opts, sort, - search_after: opts.searchAfter, - query: opts.query, }); } @@ -211,28 +206,30 @@ export class TaskStore { this.serializer.generateRawId(undefined, 'task', id) ); - const claimedTasks = await this.markAvailableTasksAsClaimed( + const numberOfTasksClaimed = await this.markAvailableTasksAsClaimed( claimOwnershipUntil, claimTasksByIdWithRawIds, size ); const docs = - claimedTasks > 0 ? await this.sweepForClaimedTasks(claimTasksByIdWithRawIds, size) : []; + numberOfTasksClaimed > 0 + ? await this.sweepForClaimedTasks(claimTasksByIdWithRawIds, size) + : []; // emit success/fail events for claimed tasks by id if (claimTasksById && claimTasksById.length) { - docs.map(doc => asTaskClaimEvent(doc.id, asOk(doc))).forEach(this.emitEvent); + this.emitEvents(docs.map(doc => asTaskClaimEvent(doc.id, asOk(doc)))); - difference( - claimTasksById, - docs.map(doc => doc.id) - ) - .map(id => asTaskClaimEvent(id, asErr(new Error(`failed to claim task '${id}'`)))) - .forEach(this.emitEvent); + this.emitEvents( + difference( + claimTasksById, + docs.map(doc => doc.id) + ).map(id => asTaskClaimEvent(id, asErr(new Error(`failed to claim task '${id}'`)))) + ); } return { - claimedTasks, + claimedTasks: numberOfTasksClaimed, docs, }; }; @@ -247,7 +244,7 @@ export class TaskStore { // status running or claiming with a retryAt <= now. shouldBeOneOf(IdleTaskWithExpiredRunAt, RunningOrClaimingTaskWithExpiredRetryAt), // Either task has a schedule or the attempts < the maximum configured - shouldBeOneOf( + shouldBeOneOf( TaskWithSchedule, ...Object.entries(this.definitions).map(([type, { maxAttempts }]) => taskWithLessThanMaxAttempts(type, maxAttempts || this.maxAttempts) @@ -255,36 +252,33 @@ export class TaskStore { ) ); - const { query, sort } = - claimTasksById && claimTasksById.length - ? { - query: shouldBeOneOf< - | ExistsBoolClause - | TermBoolClause - | RangeBoolClause - | BoolClause - >(queryForScheduledTasks, taskWithIDsAndRunnableStatus(claimTasksById)), - sort: sortByIdsThenByScheduling(claimTasksById), - } - : { - query: queryForScheduledTasks, - sort: SortByRunAtAndRetryAt, - }; - const { updated } = await this.updateByQuery( asUpdateByQuery({ - query, + query: matchesClauses( + mustBeAllOf( + claimTasksById && claimTasksById.length + ? asPinnedQuery(claimTasksById, queryForScheduledTasks) + : queryForScheduledTasks + ), + filterDownBy(InactiveTasks) + ), update: updateFields({ ownerId: this.taskManagerId, status: 'claiming', retryAt: claimOwnershipUntil, }), - sort, + sort: [ + // sort by score first, so the "pinned" Tasks are first + '_score', + // the nsort by other fields + SortByRunAtAndRetryAt, + ], }), { max_docs: size, } ); + return updated; } @@ -295,24 +289,14 @@ export class TaskStore { claimTasksById: OwnershipClaimingOpts['claimTasksById'], size: OwnershipClaimingOpts['size'] ): Promise { + const claimedTasksQuery = tasksClaimedByOwner(this.taskManagerId); const { docs } = await this.search({ - query: { - bool: { - must: [ - { - term: { - 'task.ownerId': this.taskManagerId, - }, - }, - { term: { 'task.status': 'claiming' } }, - ], - }, - }, - size, - sort: + query: claimTasksById && claimTasksById.length - ? sortByIdsThenByScheduling(claimTasksById) - : SortByRunAtAndRetryAt, + ? asPinnedQuery(claimTasksById, claimedTasksQuery) + : claimedTasksQuery, + size, + sort: SortByRunAtAndRetryAt, seq_no_primary_term: true, }); @@ -397,7 +381,6 @@ export class TaskStore { .map(doc => this.serializer.rawToSavedObject(doc)) .map(doc => omit(doc, 'namespace') as SavedObject) .map(savedObjectToConcreteTaskInstance), - searchAfter: (rawDocs.length && rawDocs[rawDocs.length - 1].sort) || [], }; } @@ -427,20 +410,6 @@ export class TaskStore { } } -function paginatableSort(sort: any[] = []) { - const sortById = { _id: 'desc' }; - - if (!sort.length) { - return [{ 'task.runAt': 'asc' }, sortById]; - } - - if (sort.find(({ _id }) => !!_id)) { - return sort; - } - - return [...sort, sortById]; -} - function taskInstanceToAttributes(doc: TaskInstance): SavedObjectAttributes { return { ...omit(doc, 'id', 'version'), diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b1aabe44433c4..8402092695590 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -530,7 +530,6 @@ "common.ui.vislib.colormaps.greysText": "グレー", "common.ui.vislib.colormaps.redsText": "赤", "common.ui.vislib.colormaps.yellowToRedText": "黄色から赤", - "common.ui.vislib.heatmap.maxBucketsText": "定義された数列が多すぎます ({nr}).構成されている最高値は {max} です。", "common.ui.visualize.queryGeohashBounds.unableToGetBoundErrorTitle": "バウンドを取得できませんでした", "console.autocomplete.addMethodMetaText": "メソド", "console.consoleDisplayName": "コンソール", @@ -2591,6 +2590,7 @@ "kbnVislibVisTypes.editors.heatmap.basicSettingsTitle": "基本設定", "kbnVislibVisTypes.editors.heatmap.heatmapSettingsTitle": "ヒートマップ設定", "kbnVislibVisTypes.editors.heatmap.highlightLabel": "ハイライト範囲", + "kbnVislibVisTypes.vislib.heatmap.maxBucketsText": "定義された数列が多すぎます ({nr}).構成されている最高値は {max} です。", "kbnVislibVisTypes.editors.heatmap.highlightLabelTooltip": "チャートのカーソルを当てた部分と凡例の対応するラベルをハイライトします。", "kbnVislibVisTypes.editors.pie.donutLabel": "ドーナッツ", "kbnVislibVisTypes.editors.pie.labelsSettingsTitle": "ラベル設定", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5521e414c4270..f3af5ec10338c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -530,7 +530,6 @@ "common.ui.vislib.colormaps.greysText": "灰色", "common.ui.vislib.colormaps.redsText": "红色", "common.ui.vislib.colormaps.yellowToRedText": "黄到红", - "common.ui.vislib.heatmap.maxBucketsText": "定义了过多的序列 ({nr})。配置的最大值为 {max}。", "common.ui.visualize.queryGeohashBounds.unableToGetBoundErrorTitle": "无法获取边界", "console.autocomplete.addMethodMetaText": "方法", "console.consoleDisplayName": "控制台", @@ -2591,6 +2590,7 @@ "kbnVislibVisTypes.editors.heatmap.basicSettingsTitle": "基本设置", "kbnVislibVisTypes.editors.heatmap.heatmapSettingsTitle": "热图设置", "kbnVislibVisTypes.editors.heatmap.highlightLabel": "高亮范围", + "kbnVislibVisTypes.vislib.heatmap.maxBucketsText": "定义了过多的序列 ({nr})。配置的最大值为 {max}。", "kbnVislibVisTypes.editors.heatmap.highlightLabelTooltip": "高亮显示图表中鼠标悬停的范围以及图例中对应的标签。", "kbnVislibVisTypes.editors.pie.donutLabel": "圆环图", "kbnVislibVisTypes.editors.pie.labelsSettingsTitle": "标签设置", diff --git a/x-pack/test/api_integration/apis/siem/network_dns.ts b/x-pack/test/api_integration/apis/siem/network_dns.ts index 5de7ea3a67087..13e98f09d072b 100644 --- a/x-pack/test/api_integration/apis/siem/network_dns.ts +++ b/x-pack/test/api_integration/apis/siem/network_dns.ts @@ -30,7 +30,6 @@ export default function({ getService }: FtrProviderContext) { query: networkDnsQuery, variables: { defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - isDNSHistogram: false, inspect: false, isPtrIncluded: false, pagination: { @@ -66,7 +65,7 @@ export default function({ getService }: FtrProviderContext) { query: networkDnsQuery, variables: { defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - isDNSHistogram: false, + isDnsHistogram: false, inspect: false, isPtrIncluded: false, pagination: { diff --git a/x-pack/test/plugin_api_integration/plugins/task_manager/index.js b/x-pack/test/plugin_api_integration/plugins/task_manager/index.js index 50fb9571c2687..e5b645367b8b7 100644 --- a/x-pack/test/plugin_api_integration/plugins/task_manager/index.js +++ b/x-pack/test/plugin_api_integration/plugins/task_manager/index.js @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +const { DEFAULT_MAX_WORKERS } = require('../../../../plugins/task_manager/server/config.ts'); const { EventEmitter } = require('events'); import { initRoutes } from './init_routes'; @@ -16,6 +17,7 @@ const once = function(emitter, event) { export default function TaskTestingAPI(kibana) { const taskTestingEvents = new EventEmitter(); + taskTestingEvents.setMaxListeners(DEFAULT_MAX_WORKERS * 2); return new kibana.Plugin({ name: 'sampleTask', diff --git a/x-pack/test/plugin_api_integration/plugins/task_manager/init_routes.js b/x-pack/test/plugin_api_integration/plugins/task_manager/init_routes.js index c0dcd99525915..9e818f050c929 100644 --- a/x-pack/test/plugin_api_integration/plugins/task_manager/init_routes.js +++ b/x-pack/test/plugin_api_integration/plugins/task_manager/init_routes.js @@ -193,15 +193,44 @@ export function initRoutes(server, taskManager, legacyTaskManager, taskTestingEv }, }); + server.route({ + path: '/api/sample_tasks/task/{taskId}', + method: 'GET', + async handler(request) { + try { + return taskManager.fetch({ + query: { + bool: { + must: [ + { + ids: { + values: [`task:${request.params.taskId}`], + }, + }, + ], + }, + }, + }); + } catch (err) { + return err; + } + }, + }); + server.route({ path: '/api/sample_tasks', method: 'DELETE', async handler() { try { - const { docs: tasks } = await taskManager.fetch({ - query: taskManagerQuery, - }); - return Promise.all(tasks.map(task => taskManager.remove(task.id))); + let tasksFound = 0; + do { + const { docs: tasks } = await taskManager.fetch({ + query: taskManagerQuery, + }); + tasksFound = tasks.length; + await Promise.all(tasks.map(task => taskManager.remove(task.id))); + } while (tasksFound > 0); + return 'OK'; } catch (err) { return err; } diff --git a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js index 0b1c1cbb5af29..7ec0e9b5efa5b 100644 --- a/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js +++ b/x-pack/test/plugin_api_integration/test_suites/task_manager/task_manager_integration.js @@ -13,6 +13,13 @@ const { task: { properties: taskManagerIndexMapping }, } = require('../../../../legacy/plugins/task_manager/server/mappings.json'); +const { + DEFAULT_MAX_WORKERS, + DEFAULT_POLL_INTERVAL, +} = require('../../../../plugins/task_manager/server/config.ts'); + +const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); + export default function({ getService }) { const es = getService('legacyEs'); const log = getService('log'); @@ -22,11 +29,12 @@ export default function({ getService }) { const supertest = supertestAsPromised(url.format(config.get('servers.kibana'))); describe('scheduling and running tasks', () => { - beforeEach(() => - supertest - .delete('/api/sample_tasks') - .set('kbn-xsrf', 'xxx') - .expect(200) + beforeEach( + async () => + await supertest + .delete('/api/sample_tasks') + .set('kbn-xsrf', 'xxx') + .expect(200) ); beforeEach(async () => { @@ -56,11 +64,19 @@ export default function({ getService }) { .then(response => response.body); } - function historyDocs() { + function currentTask(task) { + return supertest + .get(`/api/sample_tasks/task/${task}`) + .send({ task }) + .expect(200) + .then(response => response.body.docs[0]); + } + + function historyDocs(taskId) { return es .search({ index: testHistoryIndex, - q: 'type:task', + q: taskId ? `taskId:${taskId}` : 'type:task', }) .then(result => result.hits.hits); } @@ -223,7 +239,7 @@ export default function({ getService }) { }); await retry.try(async () => { - expect((await historyDocs()).length).to.eql(1); + expect((await historyDocs(originalTask.id)).length).to.eql(1); const [task] = (await currentTasks()).docs; expect(task.attempts).to.eql(0); @@ -318,6 +334,81 @@ export default function({ getService }) { }); }); + it('should prioritize tasks which are called using runNow', async () => { + const originalTask = await scheduleTask({ + taskType: 'sampleTask', + schedule: { interval: `30m` }, + params: {}, + }); + + await retry.try(async () => { + const docs = await historyDocs(originalTask.id); + expect(docs.length).to.eql(1); + + const task = await currentTask(originalTask.id); + + expect(task.state.count).to.eql(1); + + // ensure this task shouldnt run for another half hour + expectReschedule(Date.parse(originalTask.runAt), task, 30 * 60000); + }); + + const taskToBeReleased = await scheduleTask({ + taskType: 'sampleTask', + params: { waitForEvent: 'releaseSingleTask' }, + }); + + await retry.try(async () => { + // wait for taskToBeReleased to stall + expect((await historyDocs(taskToBeReleased.id)).length).to.eql(1); + }); + + // schedule multiple tasks that should force + // Task Manager to use up its worker capacity + // causing tasks to pile up + await Promise.all( + _.times(DEFAULT_MAX_WORKERS + _.random(1, DEFAULT_MAX_WORKERS), () => + scheduleTask({ + taskType: 'sampleTask', + params: { + waitForEvent: 'releaseTheOthers', + }, + }) + ) + ); + + // we need to ensure that TM has a chance to fill its queue with the stalling tasks + await delay(DEFAULT_POLL_INTERVAL); + + // call runNow for our task + const runNowResult = runTaskNow({ + id: originalTask.id, + }); + + // we need to ensure that TM has a chance to push the runNow task into the queue + // before we release the stalled task, so lets give it a chance + await delay(DEFAULT_POLL_INTERVAL); + + // and release only one slot in our worker queue + await releaseTasksWaitingForEventToComplete('releaseSingleTask'); + + expect(await runNowResult).to.eql({ id: originalTask.id }); + + await retry.try(async () => { + const task = await currentTask(originalTask.id); + expect(task.state.count).to.eql(2); + }); + + // drain tasks, othrwise they'll keep Task Manager stalled + await retry.try(async () => { + await releaseTasksWaitingForEventToComplete('releaseTheOthers'); + const tasks = (await currentTasks()).docs.filter( + task => task.params.originalParams.waitForEvent === 'releaseTheOthers' + ); + expect(tasks.length).to.eql(0); + }); + }); + it('should return a task run error result when running a task now fails', async () => { const originalTask = await scheduleTask({ taskType: 'sampleTask', @@ -329,10 +420,7 @@ export default function({ getService }) { const docs = await historyDocs(); expect(docs.filter(taskDoc => taskDoc._source.taskId === originalTask.id).length).to.eql(1); - const [task] = (await currentTasks()).docs.filter( - taskDoc => taskDoc.id === originalTask.id - ); - + const task = await currentTask(originalTask.id); expect(task.state.count).to.eql(1); // ensure this task shouldnt run for another half hour @@ -364,9 +452,7 @@ export default function({ getService }) { (await historyDocs()).filter(taskDoc => taskDoc._source.taskId === originalTask.id).length ).to.eql(2); - const [task] = (await currentTasks()).docs.filter( - taskDoc => taskDoc.id === originalTask.id - ); + const task = await currentTask(originalTask.id); expect(task.attempts).to.eql(1); }); }); diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index e309d0e127dfc..e9aab9b47535f 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -7,7 +7,10 @@ ] }, "include": [ - "**/*" + "**/*", + "../typings/**/*" ], - "exclude": [] + "exclude": [ + "../typings/jest.d.ts" + ] } diff --git a/x-pack/test/typings/hapi.d.ts b/x-pack/test/typings/hapi.d.ts deleted file mode 100644 index fc5ce09e5e618..0000000000000 --- a/x-pack/test/typings/hapi.d.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; - * you may not use this file except in compliance with the Elastic License. - */ - -import 'hapi'; - -import { XPackMainPlugin } from '../../legacy/plugins/xpack_main/server/xpack_main'; -import { SecurityPlugin } from '../../legacy/plugins/security'; -import { ActionsPlugin, ActionsClient } from '../../legacy/plugins/actions'; -import { AlertingPlugin, AlertsClient } from '../../legacy/plugins/alerting'; - -declare module 'hapi' { - interface Request { - getActionsClient?: () => ActionsClient; - getAlertsClient?: () => AlertsClient; - } - interface PluginProperties { - xpack_main: XPackMainPlugin; - security?: SecurityPlugin; - actions?: ActionsPlugin; - alerting?: AlertingPlugin; - } -} diff --git a/x-pack/test/typings/index.d.ts b/x-pack/test/typings/index.d.ts deleted file mode 100644 index db6b2d38b2c27..0000000000000 --- a/x-pack/test/typings/index.d.ts +++ /dev/null @@ -1,28 +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. - */ - -declare module '*.html' { - const template: string; - // eslint-disable-next-line import/no-default-export - export default template; -} - -declare module 'lodash/internal/toPath' { - function toPath(value: string | string[]): string[]; - export = toPath; -} - -declare module '*.json' { - const json: any; - // eslint-disable-next-line import/no-default-export - export default json; -} - -type MethodKeysOf = { - [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never; -}[keyof T]; - -type PublicMethodsOf = Pick>; diff --git a/x-pack/typings/index.d.ts b/x-pack/typings/index.d.ts index 2413f986922ed..7696ddbfd73cb 100644 --- a/x-pack/typings/index.d.ts +++ b/x-pack/typings/index.d.ts @@ -27,15 +27,6 @@ type Writable = { -readonly [K in keyof T]: T[K]; }; -type MockedKeys = { [P in keyof T]: jest.Mocked> }; - -type DeeplyMockedKeys = { - [P in keyof T]: T[P] extends (...args: any[]) => any - ? jest.MockInstance, Parameters> - : DeeplyMockedKeys; -} & - T; - // allow JSON files to be imported directly without lint errors // see: https://github.com/palantir/tslint/issues/1264#issuecomment-228433367 // and: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#arbitrary-expressions-are-forbidden-in-export-assignments-in-ambient-contexts diff --git a/x-pack/typings/jest_styled_components.d.ts b/x-pack/typings/jest.d.ts similarity index 75% rename from x-pack/typings/jest_styled_components.d.ts rename to x-pack/typings/jest.d.ts index 86f82ffb013c7..5d2aa51284e5e 100644 --- a/x-pack/typings/jest_styled_components.d.ts +++ b/x-pack/typings/jest.d.ts @@ -4,8 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -// https://github.com/styled-components/jest-styled-components/issues/264 +type MockedKeys = { [P in keyof T]: jest.Mocked> }; + +type DeeplyMockedKeys = { + [P in keyof T]: T[P] extends (...args: any[]) => any + ? jest.MockInstance, Parameters> + : DeeplyMockedKeys; +} & + T; +// https://github.com/styled-components/jest-styled-components/issues/264 declare namespace jest { interface AsymmetricMatcher { $$typeof: Symbol; //eslint-disable-line diff --git a/yarn.lock b/yarn.lock index 587af6ba2e99d..034a2ed08b4e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1953,10 +1953,10 @@ tabbable "^1.1.0" uuid "^3.1.0" -"@elastic/eui@18.2.0": - version "18.2.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-18.2.0.tgz#db9c2af87b2bdd0af7a39802d31507139b543482" - integrity sha512-wZXFBzETsaMTw6VuqFu168NKiwXMI5MVT7SdTIRufCBrT6uhUpbx43ZiDCcg372RHXosunQaiD5eVm3aodO+Jg== +"@elastic/eui@18.2.1": + version "18.2.1" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-18.2.1.tgz#6ce6d0bd1d0541052d21f2918305524d71e91678" + integrity sha512-6C5tnWJTlBB++475i0vRoCsnz4JaYznb4zMNFLc+z5GY3vA3/E3AXTjmmBwybEicCCi3h1SnpJxZsgMakiZwRA== dependencies: "@types/chroma-js" "^1.4.3" "@types/lodash" "^4.14.116" @@ -16849,10 +16849,10 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== -io-ts@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-2.0.1.tgz#1261c12f915c2f48d16393a36966636b48a45aa1" - integrity sha512-RezD+WcCfW4VkMkEcQWL/Nmy/nqsWTvTYg7oUmTGzglvSSV2P9h2z1PVeREPFf0GWNzruYleAt1XCMQZSg1xxQ== +io-ts@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-2.0.5.tgz#e6e3db9df8b047f9cbd6b69e7d2ad3e6437a0b13" + integrity sha512-pL7uUptryanI5Glv+GUv7xh+aLBjxGEDmLwmEYNSx0yOD3djK0Nw5Bt0N6BAkv9LadOUU7QKpRsLcqnTh3UlLA== ip-regex@^2.1.0: version "2.1.0"