diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ab05b32ab063e2..c32bf8cbaa1c3e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,6 +13,7 @@ /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/kibana-app /src/legacy/core_plugins/vis_type_vislib/ @elastic/kibana-app /src/plugins/vis_type_xy/ @elastic/kibana-app +/src/plugins/vis_type_table/ @elastic/kibana-app /src/plugins/kibana_legacy/ @elastic/kibana-app /src/plugins/vis_type_timelion/ @elastic/kibana-app /src/plugins/dashboard/ @elastic/kibana-app diff --git a/.i18nrc.json b/.i18nrc.json index 4a516f23ebf05d..d4286a7bd50e0f 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -48,7 +48,7 @@ "visDefaultEditor": "src/plugins/vis_default_editor", "visTypeMarkdown": "src/plugins/vis_type_markdown", "visTypeMetric": "src/plugins/vis_type_metric", - "visTypeTable": "src/legacy/core_plugins/vis_type_table", + "visTypeTable": "src/plugins/vis_type_table", "visTypeTagCloud": "src/legacy/core_plugins/vis_type_tagcloud", "visTypeTimeseries": ["src/legacy/core_plugins/vis_type_timeseries", "src/plugins/vis_type_timeseries"], "visTypeVega": "src/legacy/core_plugins/vis_type_vega", diff --git a/docs/images/clone_panel.gif b/docs/images/clone_panel.gif new file mode 100644 index 00000000000000..e521e438d051a9 Binary files /dev/null and b/docs/images/clone_panel.gif differ diff --git a/docs/images/vega_lite_default.png b/docs/images/vega_lite_default.png new file mode 100644 index 00000000000000..1ce1592d0738c6 Binary files /dev/null and b/docs/images/vega_lite_default.png differ diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index f62a4d28dfc0df..7081590931a992 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -217,6 +217,8 @@ might increase the search time. This setting is off by default. Users must opt-i [horizontal] `siem:defaultAnomalyScore`:: The threshold above which Machine Learning job anomalies are displayed in the SIEM app. `siem:defaultIndex`:: A comma-delimited list of Elasticsearch indices from which the SIEM app collects events. +`siem:ipReputationLinks`:: A JSON array containing links for verifying the reputation of an IP address. The links are displayed on +{siem-guide}/siem-ui-overview.html#network-ui[IP detail] pages. `siem:enableNewsFeed`:: Enables the security news feed on the SIEM *Overview* page. `siem:newsFeedUrl`:: The URL from which the security news feed content is diff --git a/docs/settings/ml-settings.asciidoc b/docs/settings/ml-settings.asciidoc index 6528568e868900..b71e1c672756ac 100644 --- a/docs/settings/ml-settings.asciidoc +++ b/docs/settings/ml-settings.asciidoc @@ -8,7 +8,6 @@ You do not need to configure any settings to use {kib} {ml-features}. They are enabled by default. -[float] [[general-ml-settings-kb]] ==== General {ml} settings @@ -19,3 +18,11 @@ If set to `false` in `kibana.yml`, the {ml} icon is hidden in this {kib} instance. If `xpack.ml.enabled` is set to `true` in `elasticsearch.yml`, however, you can still use the {ml} APIs. To disable {ml} entirely, see the {ref}/ml-settings.html[{es} {ml} settings]. + +[[data-visualizer-settings]] +==== {data-viz} settings + +`xpack.ml.file_data_visualizer.max_file_size`:: +Sets the file size limit when importing data in the {data-viz}. The default +value is `100MB`. The highest supported value for this setting is `1GB`. + diff --git a/docs/setup/production.asciidoc b/docs/setup/production.asciidoc index eef2b11e53d85f..19f9d64d13623d 100644 --- a/docs/setup/production.asciidoc +++ b/docs/setup/production.asciidoc @@ -133,7 +133,8 @@ server.port Settings that must be the same: -------- xpack.security.encryptionKey //decrypting session cookies -xpack.reporting.encryptionKey //decrypting reports stored in Elasticsearch +xpack.reporting.encryptionKey //decrypting reports +xpack.encryptedSavedObjects.encryptionKey // decrypting saved objects -------- Separate configuration files can be used from the command line by using the `-c` flag: diff --git a/docs/siem/images/cases-ui.png b/docs/siem/images/cases-ui.png new file mode 100644 index 00000000000000..b513efb6647407 Binary files /dev/null and b/docs/siem/images/cases-ui.png differ diff --git a/docs/siem/siem-ui.asciidoc b/docs/siem/siem-ui.asciidoc index 85253daaf29330..985138756622d2 100644 --- a/docs/siem/siem-ui.asciidoc +++ b/docs/siem/siem-ui.asciidoc @@ -35,7 +35,7 @@ image::siem/images/network-ui.png[] [float] [[detections-ui]] -=== Detections (Beta) +=== Detections (beta) The Detections feature automatically searches for threats and creates signals when they are detected. Signal detection rules define the conditions @@ -50,6 +50,22 @@ or the Detections API. [role="screenshot"] image::siem/images/detections-ui.png[] +[float] +[[cases-ui]] +=== Cases (beta) + +Cases are used to open and track security issues directly in SIEM. +Cases list the original reporter and all users who contribute to a case +(`participants`). Case comments support Markdown syntax, and allow linking to +saved Timelines. Additionally, you can send cases to external systems from +within SIEM (currently ServiceNow). + +For information about opening, updating, and closing cases, see +{siem-guide}/cases-overview.html[Cases] in the SIEM Guide. + +[role="screenshot"] +image::siem/images/cases-ui.png[] + [float] [[timelines-ui]] === Timeline diff --git a/docs/user/alerting/action-types/pagerduty.asciidoc b/docs/user/alerting/action-types/pagerduty.asciidoc index 50a1f31e4a9ae1..da34c6e0855d76 100644 --- a/docs/user/alerting/action-types/pagerduty.asciidoc +++ b/docs/user/alerting/action-types/pagerduty.asciidoc @@ -4,19 +4,142 @@ The PagerDuty action type uses the https://v2.developer.pagerduty.com/docs/events-api-v2[v2 Events API] to trigger, acknowledge, and resolve PagerDuty alerts. +* <> +* <> +* <> + +[float] +[[pagerduty-benefits]] +=== PagerDuty + Elastic integration benefits + +By integrating PagerDuty with alerts, you can: + +* Route your alerts to the right PagerDuty responder within your team, based on your structure, escalation policies, and workflows. +* Automatically generate incidents of different types and severity based on each alert’s context. +* Tailor the incident data to match your needs by easily passing the alerting context from Kibana to PagerDuty. + +[float] +[[pagerduty-how-it-works]] +==== How it works + +{kib} allows you to create alerts to notify you of a significant move +in your dataset. +You can create alerts for all your Observability, Security, and Elastic Stack use cases. +Alerts will trigger a new incident on the corresponding PagerDuty service. + +[float] +==== Requirements + +In the `kibana.yml` configuration file, you must add the <>. +This is required to encrypt parameters that must be secured, for example PagerDuty’s integration key. + +If you have security enabled: + +* You must have +application privileges to access Metrics, APM, Uptime, or SIEM. +* If you are using a self-managed deployment with security, you must have +Transport Security Layer (TLS) enabled for communication <>. +Alerts uses API keys to secure background alert checks and actions, +and API keys require {ref}/configuring-tls.html#tls-http[TLS on the HTTP interface]. + +Although not a requirement, to harden the integrations security you might want to +review the <> that are available to you. + +[float] +[[pagerduty-support]] +==== Support +If you need help with this integration, get in touch with the {kib} team by visiting +https://support.elastic.co[support.elastic.co] or by using the *Ask Elastic* option in the {kib} Help menu. +You can also select the {kib} category at https://discuss.elastic.co/[discuss.elastic.co]. + +[float] +[[pagerduty-integration-walkthrough]] +==== Integration with PagerDuty walkthrough + +[float] +[[pagerduty-in-pagerduty]] +===== In PagerDuty + +. From the *Configuration* menu, select *Services*. +. Add an integration to a service: ++ +* If you are adding your integration to an existing service, +click the name of the service you want to add the integration to. +Then, select the *Integrations* tab and click the *New Integration* button. +* If you are creating a new service for your integration, +go to +https://support.pagerduty.com/docs/services-and-integrations#section-configuring-services-and-integrations[Configuring Services and Integrations] +and follow the steps outlined in the *Create a New Service* section, selecting *Elastic* as the *Integration Type* in step 4. +Continue with the <> section once you have finished these steps. + +. Enter an *Integration Name* in the format Elastic-service-name (for example, Elastic-Alerting or Kibana-APM-Alerting) +and select Elastic from the *Integration Type* menu. +. Click *Add Integration* to save your new integration. ++ +You will be redirected to the *Integrations* tab for your service. An Integration Key is generated on this screen. ++ +[role="screenshot"] +image::user/alerting/images/pagerduty-integration.png[PagerDuty Integrations tab] + +. Save this key, as you will use it when you configure the integration with Elastic in the next section. + +[float] +[[pagerduty-in-elastic]] +===== In Elastic + +. Create a PagerDuty Connector in Kibana. You can: ++ +* Create a connector as part of creating an alert by selecting PagerDuty in the *Actions* +section of the alert configuration and selecting *Add new*. +* Alternatively, create a connector by navigating to *Management* from the {kib} navbar and selecting +*Alerts and Actions*. Then, select the *Connectors* tab, click the *Create connector* button, and select the PagerDuty option. + +. Configure the connector by giving it a name and optionally entering the API URL and Routing Key, or using the defaults. ++ +See <> for how to obtain the endpoint and key information from PagerDuty and +<> for more details. + +. Save the Connector. + +. Create an alert using *Management > Alerts and Actions* or the application of your choice. + +. Set up an action using your PagerDuty connector, by determining: ++ +* The action’s type: Trigger, Resolve, or Acknowledge. +* The event’s severity: Info, warning, error, or critical. +* An array of different fields, including the timestamp, group, class, component, and your dedup key. +Depending on your custom needs, assign them variables from the alerting context. +To see the available context variables, click on the *Add alert variable* icon next +to each corresponding field. For more details on these parameters, see the +<> and the PagerDuty +https://v2.developer.pagerduty.com/v2/docs/send-an-event-events-api-v2[API v2 documentation]. + + +[float] +[[pagerduty-uninstall]] +==== How to uninstall +To remove a PagerDuty connector from an alert, simply remove it +from the *Actions* section of that alert, using the remove (x) icon. +This will disable the integration for the particular alert. + +To delete the connector entirely, go to *Management > Alerts and Actions*. +Select the *Connectors* tab, and then click on the delete icon. +This is an irreversible action and impacts all alerts that use this connector. + + [float] [[pagerduty-connector-configuration]] -==== Connector configuration +=== Connector configuration PagerDuty connectors have the following configuration properties: Name:: The name of the connector. The name is used to identify a connector in the management UI connector listing, or in the connector list when configuring an action. -API URL:: An optional PagerDuty event URL. Defaults to `https://events.pagerduty.com/v2/enqueue`. If you are using the <> setting, make sure the hostname is whitelisted. +API URL:: An optional PagerDuty event URL. Defaults to `https://events.pagerduty.com/v2/enqueue`. If you are using the <> setting, make sure the hostname is whitelisted. Routing Key:: A 32 character PagerDuty Integration Key for an integration on a service or on a global ruleset. [float] [[pagerduty-action-configuration]] -==== Action configuration +=== Action configuration PagerDuty actions have the following properties: @@ -26,8 +149,8 @@ Dedup Key:: All actions sharing this key will be associated with the same Pa Timestamp:: An *optional* https://v2.developer.pagerduty.com/v2/docs/types#datetime[ISO-8601 format date-time], indicating the time the event was detected or generated. Component:: An *optional* value indicating the component of the source machine that is responsible for the event, for example `mysql` or `eth0`. Group:: An *optional* value indicating the logical grouping of components of a service, for example `app-stack`. -Source:: An *optional* value indicating the affected system, preferably a hostname or fully qualified domain name. Defaults to the {kib} saved object id of the action. +Source:: An *optional* value indicating the affected system, preferably a hostname or fully qualified domain name. Defaults to the {kib} saved object id of the action. Summary:: An *optional* text summary of the event, defaults to `No summary provided`. The maximum length is 1024 characters. Class:: An *optional* value indicating the class/type of the event, for example `ping failure` or `cpu load`. -For more details on these properties, see https://v2.developer.pagerduty.com/v2/docs/send-an-event-events-api-v2[PagerDuty v2 event parameters]. \ No newline at end of file +For more details on these properties, see https://v2.developer.pagerduty.com/v2/docs/send-an-event-events-api-v2[PagerDuty v2 event parameters]. diff --git a/docs/user/alerting/images/pagerduty-integration.png b/docs/user/alerting/images/pagerduty-integration.png new file mode 100755 index 00000000000000..0a270de866b36e Binary files /dev/null and b/docs/user/alerting/images/pagerduty-integration.png differ diff --git a/docs/user/dashboard.asciidoc b/docs/user/dashboard.asciidoc index ab529a533d5e37..de714ae40086be 100644 --- a/docs/user/dashboard.asciidoc +++ b/docs/user/dashboard.asciidoc @@ -98,6 +98,24 @@ to the new dimensions. * To delete a panel, open the panel menu and select *Delete from dashboard.* Deleting a panel from a dashboard does *not* delete the saved visualization or search. +[float] +[[cloning-a-panel]] +=== Clone dashboard elements + +In *Edit* mode, you can clone any panel on a dashboard. + +To clone an existing panel, open the panel menu of the element you wish to clone, then select *Clone panel*. + +* Cloned panels appear beside the original, and will move other panels down to make room if necessary. + +* Clones support all of the original panel's functionality, including renaming, editing, and cloning. + +* All cloned visualizations will appear in the visualization list. + +[role="screenshot"] +image:images/clone_panel.gif[clone panel] + + [float] [[viewing-detailed-information]] === Inspect and edit elements diff --git a/docs/user/ml/images/ml-data-visualizer-sample.jpg b/docs/user/ml/images/ml-data-visualizer-sample.jpg index 6c2e018932717f..ce2bb660d7da1a 100644 Binary files a/docs/user/ml/images/ml-data-visualizer-sample.jpg and b/docs/user/ml/images/ml-data-visualizer-sample.jpg differ diff --git a/docs/user/ml/index.asciidoc b/docs/user/ml/index.asciidoc index cca0dc5e4530fe..e9ef4a55b2b3ae 100644 --- a/docs/user/ml/index.asciidoc +++ b/docs/user/ml/index.asciidoc @@ -4,31 +4,31 @@ [partintro] -- -As datasets increase in size and complexity, the human effort required to +As data sets increase in size and complexity, the human effort required to inspect dashboards or maintain rules for spotting infrastructure problems, cyber attacks, or business issues becomes impractical. Elastic {ml-features} such as {anomaly-detect} and {oldetection} make it easier to notice suspicious activities with minimal human interference. -If you have a basic license, you can use the *Data Visualizer* to learn more -about your data. In particular, if your data is stored in {es} and contains a -time field, you can use the *Data Visualizer* to identify possible fields for -{anomaly-detect}: +{kib} includes a free *{data-viz}* to learn more about your data. In particular, +if your data is stored in {es} and contains a time field, you can use the +*{data-viz}* to identify possible fields for {anomaly-detect}: [role="screenshot"] -image::user/ml/images/ml-data-visualizer-sample.jpg[Data Visualizer for sample flight data] +image::user/ml/images/ml-data-visualizer-sample.jpg[{data-viz} for sample flight data] -experimental[] You can also upload a CSV, NDJSON, or log file (up to 100 MB in -size). The *Data Visualizer* identifies the file format and field mappings. You -can then optionally import that data into an {es} index. +experimental[] You can also upload a CSV, NDJSON, or log file. The *{data-viz}* +identifies the file format and field mappings. You can then optionally import +that data into an {es} index. To change the default file size limit, see +<>. -You need the following permissions to use the Data Visualizer with file upload: +You need the following permissions to use the {data-viz} with file upload: * cluster privileges: `monitor`, `manage_ingest_pipelines` * index privileges: `read`, `manage`, `index` For more information, see {ref}/security-privileges.html[Security privileges] -and {ref}/built-in-roles.html[Built-in roles]. +and {ml-docs}/setup.html[Set up {ml-features}]. -- diff --git a/docs/visualize/vega.asciidoc b/docs/visualize/vega.asciidoc index b8c0d1dbe3ddaf..e0d9955f0c3dbf 100644 --- a/docs/visualize/vega.asciidoc +++ b/docs/visualize/vega.asciidoc @@ -1,81 +1,80 @@ [[vega-graph]] -== Vega Graphs -experimental[] +== Vega -You can build https://vega.github.io/vega/examples/[Vega] and -https://vega.github.io/vega-lite/examples/[Vega-Lite] data visualizations -into Kibana, either standalone, or on top of a map. To see Vega in action, -watch this -https://www.youtube.com/watch?v=lQGCipY3th8[short introduction video]. +experimental[] -Currently Vega version 4.3 and Vega-Lite version 2.6 are supported. +Build custom visualizations from multiple data sources using Vega +and Vega-Lite. -NOTE: In Vega it is possible to load data dynamically, e.g. by setting signals as data urls. This is not supported in Kibana as all data is fetched at once prior to passing it to the Vega renderer. +* *Vega* — A declarative format to create visualizations using JSON. + Generate interactive displays using D3. +* *Vega-Lite* — An easier format to use than Vega that enables more rapid + data analysis. Compiles into Vega. -[[vega-quick-demo]] -=== Getting Started with Vega +For more information about Vega and Vega-Lite, refer to +<>. -* To experiment using sample data, first click the {kib} logo in the upper left hand corner -and then click the link next to *Sample Data*. -* Once you have data loaded, go to *Visualize*, click *+*, and select *Vega* to see an example graph. -*Note*: The default graph is written in Vega-Lite, but you can build visualizations -in either language. See <> for more information. -* Try changing `mark` from `line` to `point`, `area`, `bar`, `circle`, -or `square`. Check out the -https://vega.github.io/vega-lite/docs/mark.html#mark-def[Vega-Lite docs] for more information. -* Explore other available https://vega.github.io/vega/examples/[Vega] or -https://vega.github.io/vega-lite/examples/[Vega-Lite] visualizations. -*Note*: You might need to make URLs absolute, for example, replace -`"url": "data/world-110m.json"` with -`"url": "https://vega.github.io/editor/data/world-110m.json"`. -See <>. -* For more information on getting started, check out this https://www.elastic.co/blog/getting-started-with-vega-visualizations-in-kibana[blog post]. +[float] +[[create-vega-viz]] +=== Create Vega visualizations +You create Vega visualizations by using the text editor, which is +preconfigured with the options you need. -[[vega-vs-vegalite]] -=== Vega vs Vega-Lite +[role="screenshot"] +image::images/vega_lite_default.png[] -The Vega visualization in {kib} supports both Vega and Vega-Lite. You can use the -`schema` value to define which language you would like to use and its minimum -required version. +[float] +[[vega-schema]] +==== Change the Vega version -For example: +The default visualization uses Vega-Lite version 2. To use Vega version 4, edit +the `schema`. -* Vega-Lite v2: `$schema: https://vega.github.io/schema/vega-lite/v2.json` -* Vega v4: `$schema: https://vega.github.io/schema/vega/v4.json` +Go to `$schema`, enter `https://vega.github.io/schema/vega/v4.json`, then click +*Update*. -The `schema` URL is only used for identification, and does not need to be accessible by {kib}. +[float] +[[vega-type]] +==== Change the visualization type -Vega-Lite is a simplified version of Vega; it automates some constructions and has -much shorter specifications than Vega. Vega-Lite is automatically converted into -Vega before rendering, but it has some limitations, and there are some visualizations -that can be expressed in Vega that cannot be expressed in Vega-Lite. You can learn more -in the https://vega.github.io/vega-lite/[Vega-Lite documentation]. +The default visualization is a line chart. To change the visualization type, +change the `mark` value. The supported visualization types are listed in the +text editor. -You can use https://vega.github.io/editor/[this editor] to convert Vega-Lite into -Vega. +Go to `mark`, change the value to a different visualization type, then click +*Update*. -When you create a Vega visualization in {kib}, you can edit the `schema` -value in the dev tools to the left of the graph to define which of the two expression -languages you would like to use. +[float] +[[vega-sizing-and-positioning]] +==== Change the layout +By default, Vega visualizations use the `autosize = { type: 'fit', contains: 'padding' }` layout. +`fit` uses all available space, ignores `width` and `height` values, +and respects the padding values. To override this behavior, change the +`autosize` value. [[vega-querying-elasticsearch]] -=== Querying Elasticsearch +=== Query {es} -By default, Vega's https://vega.github.io/vega/docs/data/[data] element -can use embedded and external data with a `"url"` parameter. Kibana adds support for the direct Elasticsearch queries by overloading +experimental[] Vega https://vega.github.io/vega/docs/data/[data] elements +use embedded and external data with a `"url"` parameter. {kib} adds support for +direct {es} queries by overloading the `"url"` value. -Here is an example of an Elasticsearch query that counts the number of documents in all indexes. The query uses *@timestamp* field to filter the time range, and break it into histogram buckets. +NOTE: With Vega, you dynamically load your data by setting signals as data URLs. +Since {kib} is unable to support dynamically loaded data, all data is fetched +before it's passed to the Vega renderer. + +For example, count the number of documents in all indices: [source,yaml] ---- -// An object instead of a string for the url value +// An object instead of a string for the URL value // is treated as a context-aware Elasticsearch query. url: { - // Specify the time filter (upper right corner) with this field + // Specify the time filter. %timefield%: @timestamp // Apply dashboard context filters when set %context%: true @@ -88,8 +87,8 @@ url: { time_buckets: { date_histogram: { // Use date histogram aggregation on @timestamp field - field: @timestamp - // interval value will depend on the daterange picker + field: @timestamp <1> + // interval value will depend on the time filter // Use an integer to set approximate bucket count interval: { %autointerval%: true } // Make sure we get an entire range, even if it has no data @@ -109,7 +108,10 @@ url: { } ---- -The full result has this kind of structure: +<1> `@timestamp` — Filters the time range and breaks it into histogram +buckets. + +The full result includes the following structure: [source,yaml] ---- @@ -118,23 +120,24 @@ The full result has this kind of structure: "time_buckets": { "buckets": [{ "key_as_string": "2015-11-30T22:00:00.000Z", - "key": 1448920800000, + "key": 1448920800000,<1> "doc_count": 28 }, { "key_as_string": "2015-11-30T23:00:00.000Z", - "key": 1448924400000, + "key": 1448924400000, <1> "doc_count": 330 }, ... ---- -Note that `"key"` is a unix timestamp, and can be used without conversions by the +<1> `"key"` — The unix timestamp you can use without conversions by the Vega date expressions. -For most graphs we only need the list of the bucket values, so we use `format: {property: "aggregations.time_buckets.buckets"}` expression to focus on just the data we need. +For most visualizations, you only need the list of bucket values. To focus on +only the data you need, use `format: {property: "aggregations.time_buckets.buckets"}`. -Query may be specified with individual range and dashboard context as -well. This query is equivalent to `"%context%": true, "%timefield%": "@timestamp"`, -except that the timerange is shifted back by 10 minutes: +Specify a query with individual range and dashboard context. The query is +equivalent to `"%context%": true, "%timefield%": "@timestamp"`, +except that the time range is shifted back by 10 minutes: [source,yaml] ---- @@ -185,9 +188,9 @@ on the currently picked range: `"interval": {"%autointerval%": 10}` will try to get about 10-15 data points (buckets). [[vega-esmfiles]] -=== Elastic Map Files +=== Access Elastic Map Service files -It is possible to access Elastic Map Service's files via the same mechanism +experimental[] Access the Elastic Map Service files via the same mechanism: [source,yaml] ---- @@ -203,11 +206,8 @@ url: { format: {property: "features"} ---- -[[vega-with-a-map]] -=== Vega with a Map - -Kibana's default map can be used as a base of the Vega graph. To enable, -the graph must specify `type=map` in the host configuration: +To enable Elastic Maps, the graph must specify `type=map` in the host +configuration: [source,yaml] ---- @@ -247,42 +247,47 @@ the graph must specify `type=map` in the host configuration: } ---- -This visualization will automatically inject a projection called -`"projection"`. Use it to calculate positioning of all geo-aware marks. -Additionally, you may use `latitude`, `longitude`, and `zoom` signals. +The visualization automatically injects a `"projection"`, which you can use to +calculate the position of all geo-aware marks. +Additionally, you can use `latitude`, `longitude`, and `zoom` signals. These signals can be used in the graph, or can be updated to modify the -positioning of the map. +position of the map. + +Vega visualization ignore the `autosize`, `width`, `height`, and `padding` +values, using `fit` model with zero padding. [[vega-debugging]] -=== Debugging +=== Debugging Vega [[vega-browser-debugging-console]] -==== Browser Debugging console +==== Browser debugging console -Use browser debugging tools (e.g. F12 or Ctrl+Shift+J in Chrome) to +experimental[] Use browser debugging tools (for example, F12 or Ctrl+Shift+J in Chrome) to inspect the `VEGA_DEBUG` variable: -* `view` - access to the Vega View object. See https://vega.github.io/vega/docs/api/debugging/[Vega Debugging Guide] - on how to inspect data and signals at runtime. For Vega-Lite, `VEGA_DEBUG.view.data('source_0')` gets the main data set. - For Vega, it uses the data name as defined in your Vega spec. -* `vega_spec` - Vega JSON graph specification after some modifications by Kibana. In case ++ +* `view` — Access to the Vega View object. See https://vega.github.io/vega/docs/api/debugging/[Vega Debugging Guide] +on how to inspect data and signals at runtime. For Vega-Lite, `VEGA_DEBUG.view.data('source_0')` gets the main data set. +For Vega, it uses the data name as defined in your Vega spec. + +* `vega_spec` — Vega JSON graph specification after some modifications by {kib}. In case of Vega-Lite, this is the output of the Vega-Lite compiler. -* `vegalite_spec` - If this is a Vega-Lite graph, JSON specification of the graph before + +* `vegalite_spec` — If this is a Vega-Lite graph, JSON specification of the graph before Vega-Lite compilation. [[vega-data]] ==== Data -If you are using Elasticsearch query, make sure your resulting data is -what you expected. The easiest way to view it is by using "networking" -tab in the browser debugging tools (e.g. F12). Modify the graph slightly +experimental[] If you are using an {es} query, make sure your resulting data is +what you expected. The easiest way to view it is by using the "networking" +tab in the browser debugging tools (for example, F12). Modify the graph slightly so that it makes a search request, and view the response from the server. Another approach is to use -https://www.elastic.co/guide/en/kibana/current/console-kibana.html[Kibana -Dev Tools] tab - place the index name into the first line: -`GET /_search`, and add your query as the following lines -(just the value of the `"query"` field) +https://www.elastic.co/guide/en/kibana/current/console-kibana.html[Dev Tools]. Place the index name into the first line: +`GET /_search`, then add your query as the following lines +(just the value of the `"query"` field). -If you need to share your graph with someone, you may want to copy the +If you need to share your graph with someone, copy the raw data response to https://gist.github.com/[gist.github.com], possibly with a `.json` extension, use the `[raw]` button, and use that url directly in your graph. @@ -292,9 +297,11 @@ to your kibana.yml file. [[vega-notes]] [[vega-useful-links]] -=== Useful Links +=== Resources and examples + +experimental[] To learn more about Vega and Vega-List, refer to the resources and examples. -==== Vega Editor +==== Vega editor The https://vega.github.io/editor/[Vega Editor] includes examples for Vega & Vega-Lite, but does not support any {kib}-specific features like {es} requests and interactive base maps. @@ -308,28 +315,15 @@ The https://vega.github.io/editor/[Vega Editor] includes examples for Vega & Veg * https://vega.github.io/vega/docs/[Docs] * https://vega.github.io/vega/examples/[Examples] -==== Elastic blog posts -* https://www.elastic.co/blog/getting-started-with-vega-visualizations-in-kibana[Getting Started with Vega Visualizations in Kibana] -* https://www.elastic.co/blog/custom-vega-visualizations-in-kibana[Custom Vega Visualizations in Kibana] -* https://www.elastic.co/blog/sankey-visualization-with-vega-in-kibana[Sankey Visualization with Vega in Kibana] - - -[[vega-using-vega-and-vegalite-examples]] -==== Using Vega and Vega-Lite examples - -When using https://vega.github.io/vega/examples/[Vega] and -https://vega.github.io/vega-lite/examples/[VegaLite] examples, you may +TIP: When you use the examples, you may need to modify the "data" section to use absolute URL. For example, replace `"url": "data/world-110m.json"` with -`"url": "https://vega.github.io/editor/data/world-110m.json"`. Also, -regular Vega examples use `"autosize": "pad"` layout model, whereas -Kibana uses `fit`. Remove all `autosize`, `width`, and `height` -values. See link:#sizing-and-positioning[sizing and positioning]. +`"url": "https://vega.github.io/editor/data/world-110m.json"`. [[vega-additional-configuration-options]] ==== Additional configuration options -These options are specific to the Kibana. link:#vega-with-a-map[Map support] has +These options are specific to the {kib}. link:#vega-with-a-map[Map support] has additional configuration options. [source,yaml] @@ -351,21 +345,3 @@ additional configuration options. /* the rest of Vega code */ } ---- - -[[vega-sizing-and-positioning]] -==== Sizing and positioning - -[[vega-and-vegalite]] -===== Vega and Vega-Lite - -By default, Kibana Vega graphs will use -`autosize = { type: 'fit', contains: 'padding' }` layout model for Vega -and Vega-Lite graphs. The `fit` model uses all available space, ignores -`width` and `height` values, but respects the padding values. You may -override this behaviour by specifying a different `autosize` value. - -[[vega-on-a-map]] -===== Vega on a map - -All Vega graphs will ignore `autosize`, `width`, `height`, and `padding` -values, using `fit` model with zero padding. diff --git a/examples/state_containers_examples/public/todo/app.tsx b/examples/state_containers_examples/public/todo/app.tsx index 319680d07f9bc8..f2183613e4a12c 100644 --- a/examples/state_containers_examples/public/todo/app.tsx +++ b/examples/state_containers_examples/public/todo/app.tsx @@ -20,7 +20,7 @@ import { AppMountParameters } from 'kibana/public'; import ReactDOM from 'react-dom'; import React from 'react'; -import { createHashHistory, createBrowserHistory } from 'history'; +import { createHashHistory } from 'history'; import { TodoAppPage } from './todo'; export interface AppOptions { @@ -35,13 +35,10 @@ export enum History { } export const renderApp = ( - { appBasePath, element }: AppMountParameters, + { appBasePath, element, history: platformHistory }: AppMountParameters, { appInstanceId, appTitle, historyType }: AppOptions ) => { - const history = - historyType === History.Browser - ? createBrowserHistory({ basename: appBasePath }) - : createHashHistory(); + const history = historyType === History.Browser ? platformHistory : createHashHistory(); ReactDOM.render( = ({ filter }) => { return ( <>
- + All - + Completed - + Not Completed @@ -121,6 +124,7 @@ const TodoApp: React.FC = ({ filter }) => { }); }} label={todo.text} + data-test-subj={`todoCheckbox-${todo.id}`} /> { - const history = createBrowserHistory({ basename: appBasePath }); const kbnUrlStateStorage = createKbnUrlStateStorage({ useHash: false, history }); ReactDOM.render( diff --git a/packages/kbn-config-schema/src/types/object_type.test.ts b/packages/kbn-config-schema/src/types/object_type.test.ts index 47a0f5f7a5491c..5ab59d1c020779 100644 --- a/packages/kbn-config-schema/src/types/object_type.test.ts +++ b/packages/kbn-config-schema/src/types/object_type.test.ts @@ -18,6 +18,7 @@ */ import { schema } from '..'; +import { TypeOf } from './object_type'; test('returns value by default', () => { const type = schema.object({ @@ -350,3 +351,26 @@ test('unknowns = `ignore` affects only own keys', () => { }) ).toThrowErrorMatchingInlineSnapshot(`"[foo.baz]: definition for this key is missing"`); }); + +test('handles optional properties', () => { + const type = schema.object({ + required: schema.string(), + optional: schema.maybe(schema.string()), + }); + + type SchemaType = TypeOf; + + let foo: SchemaType = { + required: 'foo', + }; + foo = { + required: 'hello', + optional: undefined, + }; + foo = { + required: 'hello', + optional: 'bar', + }; + + expect(foo).toBeDefined(); +}); diff --git a/packages/kbn-config-schema/src/types/object_type.ts b/packages/kbn-config-schema/src/types/object_type.ts index 5a50e714a59311..fee2d02c1bfb96 100644 --- a/packages/kbn-config-schema/src/types/object_type.ts +++ b/packages/kbn-config-schema/src/types/object_type.ts @@ -26,9 +26,26 @@ export type Props = Record>; export type TypeOf> = RT['type']; +type OptionalProperties = Pick< + Base, + { + [Key in keyof Base]: undefined extends TypeOf ? Key : never; + }[keyof Base] +>; + +type RequiredProperties = Pick< + Base, + { + [Key in keyof Base]: undefined extends TypeOf ? never : Key; + }[keyof Base] +>; + // Because of https://github.com/Microsoft/TypeScript/issues/14041 // this might not have perfect _rendering_ output, but it will be typed. -export type ObjectResultType

= Readonly<{ [K in keyof P]: TypeOf }>; +export type ObjectResultType

= Readonly< + { [K in keyof OptionalProperties

]?: TypeOf } & + { [K in keyof RequiredProperties

]: TypeOf } +>; interface UnknownOptions { /** @@ -40,9 +57,7 @@ interface UnknownOptions { unknowns?: 'allow' | 'ignore' | 'forbid'; } -export type ObjectTypeOptions

= TypeOptions< - { [K in keyof P]: TypeOf } -> & +export type ObjectTypeOptions

= TypeOptions> & UnknownOptions; export class ObjectType

extends Type> { diff --git a/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts b/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts index 03075dd4081fd4..3a66ba22ccf3d4 100644 --- a/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts +++ b/packages/kbn-test/src/functional_test_runner/functional_test_runner.ts @@ -30,6 +30,7 @@ import { setupMocha, runTests, Config, + SuiteTracker, } from './lib'; export class FunctionalTestRunner { @@ -52,6 +53,8 @@ export class FunctionalTestRunner { async run() { return await this._run(async (config, coreProviders) => { + SuiteTracker.startTracking(this.lifecycle, this.configFile); + const providers = new ProviderCollection(this.log, [ ...coreProviders, ...readProviderSpec('Service', config.get('services')), diff --git a/packages/kbn-test/src/functional_test_runner/lib/index.ts b/packages/kbn-test/src/functional_test_runner/lib/index.ts index 8940eccad503a4..2e534974e1d76c 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/index.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/index.ts @@ -23,3 +23,4 @@ export { readConfigFile, Config } from './config'; export { readProviderSpec, ProviderCollection, Provider } from './providers'; export { runTests, setupMocha } from './mocha'; export { FailureMetadata } from './failure_metadata'; +export { SuiteTracker } from './suite_tracker'; diff --git a/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts b/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts new file mode 100644 index 00000000000000..b6c2c0a6d511d1 --- /dev/null +++ b/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts @@ -0,0 +1,197 @@ +/* + * 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 fs from 'fs'; +import { join, resolve } from 'path'; + +jest.mock('fs'); +jest.mock('@kbn/dev-utils', () => { + return { REPO_ROOT: '/dev/null/root' }; +}); + +import { REPO_ROOT } from '@kbn/dev-utils'; +import { Lifecycle } from './lifecycle'; +import { SuiteTracker } from './suite_tracker'; + +const DEFAULT_TEST_METADATA_PATH = join(REPO_ROOT, 'target', 'test_metadata.json'); +const MOCK_CONFIG_PATH = join('test', 'config.js'); +const MOCK_TEST_PATH = join('test', 'apps', 'test.js'); +const ENVS_TO_RESET = ['TEST_METADATA_PATH']; + +describe('SuiteTracker', () => { + const originalEnvs: Record = {}; + + beforeEach(() => { + for (const env of ENVS_TO_RESET) { + if (env in process.env) { + originalEnvs[env] = process.env[env] || ''; + delete process.env[env]; + } + } + }); + + afterEach(() => { + for (const env of ENVS_TO_RESET) { + delete process.env[env]; + } + + for (const env of Object.keys(originalEnvs)) { + process.env[env] = originalEnvs[env]; + } + + jest.resetAllMocks(); + }); + + let MOCKS: Record; + + const createMock = (overrides = {}) => { + return { + file: resolve(REPO_ROOT, MOCK_TEST_PATH), + title: 'A Test', + suiteTag: MOCK_TEST_PATH, + ...overrides, + }; + }; + + const runLifecycleWithMocks = async (mocks: object[], fn: (objs: any) => any = () => {}) => { + const lifecycle = new Lifecycle(); + const suiteTracker = SuiteTracker.startTracking( + lifecycle, + resolve(REPO_ROOT, MOCK_CONFIG_PATH) + ); + + const ret = { lifecycle, suiteTracker }; + + for (const mock of mocks) { + await lifecycle.beforeTestSuite.trigger(mock); + } + + if (fn) { + fn(ret); + } + + for (const mock of mocks.reverse()) { + await lifecycle.afterTestSuite.trigger(mock); + } + + return ret; + }; + + beforeEach(() => { + MOCKS = { + WITH_TESTS: createMock({ tests: [{}] }), // i.e. a describe with tests in it + WITHOUT_TESTS: createMock(), // i.e. a describe with only other describes in it + }; + }); + + it('collects metadata for a single suite with multiple describe()s', async () => { + const { suiteTracker } = await runLifecycleWithMocks([MOCKS.WITHOUT_TESTS, MOCKS.WITH_TESTS]); + + const suites = suiteTracker.getAllFinishedSuites(); + expect(suites.length).toBe(1); + const suite = suites[0]; + + expect(suite).toMatchObject({ + config: MOCK_CONFIG_PATH, + file: MOCK_TEST_PATH, + tag: MOCK_TEST_PATH, + hasTests: true, + success: true, + }); + }); + + it('writes metadata to a file when cleanup is triggered', async () => { + const { lifecycle, suiteTracker } = await runLifecycleWithMocks([MOCKS.WITH_TESTS]); + await lifecycle.cleanup.trigger(); + + const suites = suiteTracker.getAllFinishedSuites(); + + const call = (fs.writeFileSync as jest.Mock).mock.calls[0]; + expect(call[0]).toEqual(DEFAULT_TEST_METADATA_PATH); + expect(call[1]).toEqual(JSON.stringify(suites, null, 2)); + }); + + it('respects TEST_METADATA_PATH env var for metadata target override', async () => { + process.env.TEST_METADATA_PATH = resolve(REPO_ROOT, '../fake-test-path'); + const { lifecycle } = await runLifecycleWithMocks([MOCKS.WITH_TESTS]); + await lifecycle.cleanup.trigger(); + + expect((fs.writeFileSync as jest.Mock).mock.calls[0][0]).toEqual( + process.env.TEST_METADATA_PATH + ); + }); + + it('identifies suites with tests as leaf suites', async () => { + const root = createMock({ title: 'root', file: join(REPO_ROOT, 'root.js') }); + const parent = createMock({ parent: root }); + const withTests = createMock({ parent, tests: [{}] }); + + const { suiteTracker } = await runLifecycleWithMocks([root, parent, withTests]); + const suites = suiteTracker.getAllFinishedSuites(); + + const finishedRoot = suites.find(s => s.title === 'root'); + const finishedWithTests = suites.find(s => s.title !== 'root'); + + expect(finishedRoot).toBeTruthy(); + expect(finishedRoot?.hasTests).toBeFalsy(); + expect(finishedWithTests?.hasTests).toBe(true); + }); + + describe('with a failing suite', () => { + let root: any; + let parent: any; + let failed: any; + + beforeEach(() => { + root = createMock({ file: join(REPO_ROOT, 'root.js') }); + parent = createMock({ parent: root }); + failed = createMock({ parent, tests: [{}] }); + }); + + it('marks parent suites as not successful when a test fails', async () => { + const { suiteTracker } = await runLifecycleWithMocks( + [root, parent, failed], + async ({ lifecycle }) => { + await lifecycle.testFailure.trigger(Error('test'), { parent: failed }); + } + ); + + const suites = suiteTracker.getAllFinishedSuites(); + expect(suites.length).toBe(2); + for (const suite of suites) { + expect(suite.success).toBeFalsy(); + } + }); + + it('marks parent suites as not successful when a test hook fails', async () => { + const { suiteTracker } = await runLifecycleWithMocks( + [root, parent, failed], + async ({ lifecycle }) => { + await lifecycle.testHookFailure.trigger(Error('test'), { parent: failed }); + } + ); + + const suites = suiteTracker.getAllFinishedSuites(); + expect(suites.length).toBe(2); + for (const suite of suites) { + expect(suite.success).toBeFalsy(); + } + }); + }); +}); diff --git a/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.ts b/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.ts new file mode 100644 index 00000000000000..8967251ea78de7 --- /dev/null +++ b/packages/kbn-test/src/functional_test_runner/lib/suite_tracker.ts @@ -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 fs from 'fs'; +import { dirname, relative, resolve } from 'path'; + +import { REPO_ROOT } from '@kbn/dev-utils'; + +import { Lifecycle } from './lifecycle'; + +export interface SuiteInProgress { + startTime?: Date; + endTime?: Date; + success?: boolean; +} + +export interface SuiteWithMetadata { + config: string; + file: string; + tag: string; + title: string; + startTime: Date; + endTime: Date; + duration: number; + success: boolean; + hasTests: boolean; +} + +const getTestMetadataPath = () => { + return process.env.TEST_METADATA_PATH || resolve(REPO_ROOT, 'target', 'test_metadata.json'); +}; + +export class SuiteTracker { + finishedSuitesByConfig: Record> = {}; + inProgressSuites: Map = new Map(); + + static startTracking(lifecycle: Lifecycle, configPath: string): SuiteTracker { + const suiteTracker = new SuiteTracker(lifecycle, configPath); + return suiteTracker; + } + + getTracked(suite: object): SuiteInProgress { + if (!this.inProgressSuites.has(suite)) { + this.inProgressSuites.set(suite, { success: undefined } as SuiteInProgress); + } + return this.inProgressSuites.get(suite)!; + } + + constructor(lifecycle: Lifecycle, configPathAbsolute: string) { + if (fs.existsSync(getTestMetadataPath())) { + fs.unlinkSync(getTestMetadataPath()); + } else { + fs.mkdirSync(dirname(getTestMetadataPath()), { recursive: true }); + } + + const config = relative(REPO_ROOT, configPathAbsolute); + + lifecycle.beforeTestSuite.add(suite => { + const tracked = this.getTracked(suite); + tracked.startTime = new Date(); + }); + + // If a test fails, we want to make sure all of the ancestors, all the way up to the root, get marked as failed + // This information is not available on the mocha objects without traversing all descendants of a given node + const handleFailure = (_: any, test: any) => { + let parent = test.parent; + + // Infinite loop protection, just in case + for (let i = 0; i < 500 && parent; i++) { + if (this.inProgressSuites.has(parent)) { + this.getTracked(parent).success = false; + } + parent = parent.parent; + } + }; + + lifecycle.testFailure.add(handleFailure); + lifecycle.testHookFailure.add(handleFailure); + + lifecycle.afterTestSuite.add(suite => { + const tracked = this.getTracked(suite); + tracked.endTime = new Date(); + + // The suite ended without any children failing, so we can mark it as successful + if (typeof tracked.success === 'undefined') { + tracked.success = true; + } + + let duration = tracked.endTime.getTime() - (tracked.startTime || new Date()).getTime(); + duration = Math.floor(duration / 1000); + + const file = relative(REPO_ROOT, suite.file); + + this.finishedSuitesByConfig[config] = this.finishedSuitesByConfig[config] || {}; + + // This will get called multiple times for a test file that has multiple describes in it or similar + // This is okay, because the last one that fires is always the root of the file, which is is the one we ultimately want + this.finishedSuitesByConfig[config][file] = { + ...tracked, + duration, + config, + file, + tag: suite.suiteTag, + title: suite.title, + hasTests: !!( + (suite.tests && suite.tests.length) || + // The below statement is so that `hasTests` will bubble up nested describes in the same file + (this.finishedSuitesByConfig[config][file] && + this.finishedSuitesByConfig[config][file].hasTests) + ), + } as SuiteWithMetadata; + }); + + lifecycle.cleanup.add(() => { + const suites = this.getAllFinishedSuites(); + + fs.writeFileSync(getTestMetadataPath(), JSON.stringify(suites, null, 2)); + }); + } + + getAllFinishedSuites() { + const flattened: SuiteWithMetadata[] = []; + for (const byFile of Object.values(this.finishedSuitesByConfig)) { + for (const suite of Object.values(byFile)) { + flattened.push(suite); + } + } + + flattened.sort((a, b) => b.duration - a.duration); + return flattened; + } +} diff --git a/scripts/prettier_on_changed.js b/scripts/prettier_on_changed.js new file mode 100644 index 00000000000000..f9598110f91fd3 --- /dev/null +++ b/scripts/prettier_on_changed.js @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +require('../src/setup_node_env/babel_register'); +require('../src/dev/run_prettier_on_changed'); diff --git a/src/core/public/plugins/plugin_loader.test.ts b/src/core/public/plugins/plugin_loader.test.ts index 18cc2d7a6f182e..b4e2c3095f14a7 100644 --- a/src/core/public/plugins/plugin_loader.test.ts +++ b/src/core/public/plugins/plugin_loader.test.ts @@ -62,7 +62,7 @@ test('`loadPluginBundles` creates a script tag and loads initializer', async () const fakeScriptTag = createdScriptTags[0]; expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith( 'src', - '/bundles/plugin:plugin-a/plugin-a.plugin.js' + '/bundles/plugin/plugin-a/plugin-a.plugin.js' ); expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith('id', 'kbn-plugin-plugin-a'); expect(fakeScriptTag.onload).toBeInstanceOf(Function); @@ -85,7 +85,7 @@ test('`loadPluginBundles` includes the basePath', async () => { const fakeScriptTag = createdScriptTags[0]; expect(fakeScriptTag.setAttribute).toHaveBeenCalledWith( 'src', - '/mybasepath/bundles/plugin:plugin-a/plugin-a.plugin.js' + '/mybasepath/bundles/plugin/plugin-a/plugin-a.plugin.js' ); }); @@ -96,7 +96,7 @@ test('`loadPluginBundles` rejects if script.onerror is called', async () => { fakeScriptTag1.onerror(new Error('Whoa there!')); await expect(loadPromise).rejects.toThrowErrorMatchingInlineSnapshot( - `"Failed to load \\"plugin-a\\" bundle (/bundles/plugin:plugin-a/plugin-a.plugin.js)"` + `"Failed to load \\"plugin-a\\" bundle (/bundles/plugin/plugin-a/plugin-a.plugin.js)"` ); }); @@ -105,7 +105,7 @@ test('`loadPluginBundles` rejects if timeout is reached', async () => { // Override the timeout to 1 ms for testi. loadPluginBundle(addBasePath, 'plugin-a', { timeoutMs: 1 }) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"Timeout reached when loading \\"plugin-a\\" bundle (/bundles/plugin:plugin-a/plugin-a.plugin.js)"` + `"Timeout reached when loading \\"plugin-a\\" bundle (/bundles/plugin/plugin-a/plugin-a.plugin.js)"` ); }); @@ -115,11 +115,11 @@ test('`loadPluginBundles` rejects if bundle does attach an initializer to window const fakeScriptTag1 = createdScriptTags[0]; // Setup a fake initializer as if a plugin bundle had actually been loaded. - coreWindow.__kbnBundles__['plugin:plugin-a'] = undefined; + coreWindow.__kbnBundles__['plugin/plugin-a'] = undefined; // Call the onload callback fakeScriptTag1.onload(); await expect(loadPromise).rejects.toThrowErrorMatchingInlineSnapshot( - `"Definition of plugin \\"plugin-a\\" should be a function (/bundles/plugin:plugin-a/plugin-a.plugin.js)."` + `"Definition of plugin \\"plugin-a\\" should be a function (/bundles/plugin/plugin-a/plugin-a.plugin.js)."` ); }); diff --git a/src/core/public/plugins/plugin_loader.ts b/src/core/public/plugins/plugin_loader.ts index 9b35588dfe7262..bf7711055e97ba 100644 --- a/src/core/public/plugins/plugin_loader.ts +++ b/src/core/public/plugins/plugin_loader.ts @@ -93,7 +93,7 @@ export const loadPluginBundle: LoadPluginBundle = < const script = document.createElement('script'); // Assumes that all plugin bundles get put into the bundles/plugins subdirectory - const bundlePath = addBasePath(`/bundles/plugin:${pluginName}/${pluginName}.plugin.js`); + const bundlePath = addBasePath(`/bundles/plugin/${pluginName}/${pluginName}.plugin.js`); script.setAttribute('src', bundlePath); script.setAttribute('id', `kbn-plugin-${pluginName}`); script.setAttribute('async', ''); diff --git a/src/core/server/http/router/validator/validator.ts b/src/core/server/http/router/validator/validator.ts index 6c766e69f0f37f..a2299b47ae253d 100644 --- a/src/core/server/http/router/validator/validator.ts +++ b/src/core/server/http/router/validator/validator.ts @@ -17,7 +17,14 @@ * under the License. */ -import { ValidationError, Type, schema, ObjectType, isConfigSchema } from '@kbn/config-schema'; +import { + ValidationError, + Type, + schema, + ObjectType, + TypeOf, + isConfigSchema, +} from '@kbn/config-schema'; import { Stream } from 'stream'; import { RouteValidationError } from './validator_error'; @@ -85,7 +92,7 @@ type RouteValidationResultType | undefined> = T extends RouteValidationFunction ? ReturnType['value'] : T extends Type - ? ReturnType + ? TypeOf : undefined >; @@ -170,7 +177,7 @@ export class RouteValidator

{ * @internal */ public getParams(data: unknown, namespace?: string): Readonly

{ - return this.validate(this.config.params, this.options.unsafe?.params, data, namespace); + return this.validate(this.config.params, this.options.unsafe?.params, data, namespace) as P; } /** @@ -178,7 +185,7 @@ export class RouteValidator

{ * @internal */ public getQuery(data: unknown, namespace?: string): Readonly { - return this.validate(this.config.query, this.options.unsafe?.query, data, namespace); + return this.validate(this.config.query, this.options.unsafe?.query, data, namespace) as Q; } /** @@ -186,7 +193,7 @@ export class RouteValidator

{ * @internal */ public getBody(data: unknown, namespace?: string): Readonly { - return this.validate(this.config.body, this.options.unsafe?.body, data, namespace); + return this.validate(this.config.body, this.options.unsafe?.body, data, namespace) as B; } /** diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker index d4d2e86e1e96bf..38acfb15d3ece2 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker @@ -195,6 +195,7 @@ kibana_vars=( xpack.reporting.capture.viewport.width xpack.reporting.capture.zoom xpack.reporting.csv.checkForFormulas + xpack.reporting.csv.escapeFormulaValues xpack.reporting.csv.enablePanelActionDownload xpack.reporting.csv.maxSizeBytes xpack.reporting.csv.scroll.duration diff --git a/src/dev/eslint/index.js b/src/dev/eslint/index.ts similarity index 100% rename from src/dev/eslint/index.js rename to src/dev/eslint/index.ts diff --git a/src/dev/eslint/lint_files.js b/src/dev/eslint/lint_files.ts similarity index 90% rename from src/dev/eslint/lint_files.js rename to src/dev/eslint/lint_files.ts index a76edeb2eb865f..80c493233f39ab 100644 --- a/src/dev/eslint/lint_files.js +++ b/src/dev/eslint/lint_files.ts @@ -19,7 +19,8 @@ import { CLIEngine } from 'eslint'; -import { createFailError } from '@kbn/dev-utils'; +import { createFailError, ToolingLog } from '@kbn/dev-utils'; +import { File } from '../file'; import { REPO_ROOT } from '../constants'; /** @@ -30,7 +31,7 @@ import { REPO_ROOT } from '../constants'; * @param {Array} files * @return {undefined} */ -export function lintFiles(log, files, { fix } = {}) { +export function lintFiles(log: ToolingLog, files: File[], { fix }: { fix?: boolean } = {}) { const cli = new CLIEngine({ cache: true, cwd: REPO_ROOT, diff --git a/src/dev/eslint/pick_files_to_lint.js b/src/dev/eslint/pick_files_to_lint.ts similarity index 88% rename from src/dev/eslint/pick_files_to_lint.js rename to src/dev/eslint/pick_files_to_lint.ts index e3212c00d9e0de..b96781fc3a611f 100644 --- a/src/dev/eslint/pick_files_to_lint.js +++ b/src/dev/eslint/pick_files_to_lint.ts @@ -16,9 +16,11 @@ * specific language governing permissions and limitations * under the License. */ - import { CLIEngine } from 'eslint'; +import { ToolingLog } from '@kbn/dev-utils'; +import { File } from '../file'; + /** * Filters a list of files to only include lintable files. * @@ -26,8 +28,8 @@ import { CLIEngine } from 'eslint'; * @param {Array} files * @return {Array} */ -export function pickFilesToLint(log, files) { - const cli = new CLIEngine(); +export function pickFilesToLint(log: ToolingLog, files: File[]) { + const cli = new CLIEngine({}); return files.filter(file => { if (!file.isJs() && !file.isTypescript()) { diff --git a/src/dev/run_prettier_on_changed.ts b/src/dev/run_prettier_on_changed.ts new file mode 100644 index 00000000000000..deca4fa1be3ce5 --- /dev/null +++ b/src/dev/run_prettier_on_changed.ts @@ -0,0 +1,87 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import execa from 'execa'; +// @ts-ignore +import SimpleGit from 'simple-git'; +import { run } from '@kbn/dev-utils'; +import dedent from 'dedent'; +import Util from 'util'; + +import pkg from '../../package.json'; +import { REPO_ROOT } from './constants'; +import { File } from './file'; +import * as Eslint from './eslint'; + +run(async function getChangedFiles({ log }) { + const simpleGit = new SimpleGit(REPO_ROOT); + + const getStatus = Util.promisify(simpleGit.status.bind(simpleGit)); + const gitStatus = await getStatus(); + + if (gitStatus.files.length > 0) { + throw new Error( + dedent(`You should run prettier formatter on a clean branch. + Found not committed changes to: + ${gitStatus.files.map((f: { path: string }) => f.path).join('\n')}`) + ); + } + + const revParse = Util.promisify(simpleGit.revparse.bind(simpleGit)); + const currentBranch = await revParse(['--abbrev-ref', 'HEAD']); + const headBranch = pkg.branch; + + const diff = Util.promisify(simpleGit.diff.bind(simpleGit)); + + const changedFileStatuses: string = await diff([ + '--name-status', + `${headBranch}...${currentBranch}`, + ]); + + const changedFiles = changedFileStatuses + .split('\n') + // Ignore blank lines + .filter(line => line.trim().length > 0) + // git diff --name-status outputs lines with two OR three parts + // separated by a tab character + .map(line => line.trim().split('\t')) + .map(([status, ...paths]) => { + // ignore deleted files + if (status === 'D') { + return undefined; + } + + // the status is always in the first column + // .. If the file is edited the line will only have two columns + // .. If the file is renamed it will have three columns + // .. In any case, the last column is the CURRENT path to the file + return new File(paths[paths.length - 1]); + }) + .filter((file): file is File => Boolean(file)); + + const pathsToLint = Eslint.pickFilesToLint(log, changedFiles).map(f => f.getAbsolutePath()); + + if (pathsToLint.length > 0) { + log.debug('[prettier] run on %j files: ', pathsToLint.length, pathsToLint); + } + + while (pathsToLint.length > 0) { + await execa('npx', ['prettier@2.0.4', '--write', ...pathsToLint.splice(0, 100)]); + } +}); diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/agg_table.js similarity index 94% rename from src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/agg_table.js index a23407a599ae27..b212ecf578dd1f 100644 --- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/agg_table.js @@ -22,11 +22,18 @@ import moment from 'moment'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; import sinon from 'sinon'; -import { npStart } from '../../legacy_imports'; +import './legacy'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { npStart } from 'ui/new_platform'; import { round } from 'lodash'; -import { getAngularModule } from '../../get_inner_angular'; -import { initTableVisLegacyModule } from '../../table_vis_legacy_module'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getInnerAngular } from '../../../../../../plugins/vis_type_table/public/get_inner_angular'; + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { initTableVisLegacyModule } from '../../../../../../plugins/vis_type_table/public/table_vis_legacy_module'; import { tabifiedData } from './tabified_data'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { configureAppAngularModule } from '../../../../../../plugins/kibana_legacy/public/angular'; describe('Table Vis - AggTable Directive', function() { let $rootScope; @@ -34,7 +41,8 @@ describe('Table Vis - AggTable Directive', function() { let settings; const initLocalAngular = () => { - const tableVisModule = getAngularModule('kibana/table_vis', npStart.core); + const tableVisModule = getInnerAngular('kibana/table_vis', npStart.core); + configureAppAngularModule(tableVisModule, npStart.core, true); initTableVisLegacyModule(tableVisModule); }; diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/agg_table_group.js similarity index 81% rename from src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/agg_table_group.js index 40a0993ccb0175..3cd7de393d66a8 100644 --- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/agg_table_group.js @@ -20,17 +20,24 @@ import $ from 'jquery'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; -import { npStart } from '../../legacy_imports'; -import { getAngularModule } from '../../get_inner_angular'; -import { initTableVisLegacyModule } from '../../table_vis_legacy_module'; +import './legacy'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getInnerAngular } from '../../../../../../plugins/vis_type_table/public/get_inner_angular'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { initTableVisLegacyModule } from '../../../../../../plugins/vis_type_table/public/table_vis_legacy_module'; import { tabifiedData } from './tabified_data'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { npStart } from 'ui/new_platform'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { configureAppAngularModule } from '../../../../../../plugins/kibana_legacy/public/angular'; describe('Table Vis - AggTableGroup Directive', function() { let $rootScope; let $compile; const initLocalAngular = () => { - const tableVisModule = getAngularModule('kibana/table_vis', npStart.core); + const tableVisModule = getInnerAngular('kibana/table_vis', npStart.core); + configureAppAngularModule(tableVisModule, npStart.core, true); initTableVisLegacyModule(tableVisModule); }; diff --git a/src/legacy/core_plugins/vis_type_table/public/legacy.ts b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/legacy.ts similarity index 69% rename from src/legacy/core_plugins/vis_type_table/public/legacy.ts rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/legacy.ts index 3d5f8c1b3efe92..c6467a5beae686 100644 --- a/src/legacy/core_plugins/vis_type_table/public/legacy.ts +++ b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/legacy.ts @@ -16,19 +16,23 @@ * specific language governing permissions and limitations * under the License. */ - import { PluginInitializerContext } from 'kibana/public'; -import { npSetup, npStart } from './legacy_imports'; -import { plugin } from '.'; - -import { TablePluginSetupDependencies } from './plugin'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { npStart, npSetup } from 'ui/new_platform'; +import { + TableVisPlugin, + TablePluginSetupDependencies, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../../plugins/vis_type_table/public/plugin'; const plugins: Readonly = { expressions: npSetup.plugins.expressions, visualizations: npSetup.plugins.visualizations, }; -const pluginInstance = plugin({} as PluginInitializerContext); +const pluginInstance = new TableVisPlugin({} as PluginInitializerContext); export const setup = pluginInstance.setup(npSetup.core, plugins); -export const start = pluginInstance.start(npStart.core, { data: npStart.plugins.data }); +export const start = pluginInstance.start(npStart.core, { + data: npStart.plugins.data, +}); diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/tabified_data.js b/src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/tabified_data.js similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/tabified_data.js rename to src/legacy/core_plugins/kibana/public/__tests__/vis_type_table/tabified_data.js diff --git a/src/legacy/core_plugins/kibana/public/discover/build_services.ts b/src/legacy/core_plugins/kibana/public/discover/build_services.ts index a3a99a0ded523c..c56e50f3b27ffe 100644 --- a/src/legacy/core_plugins/kibana/public/discover/build_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/build_services.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { createHashHistory, History } from 'history'; +import { History } from 'history'; import { Capabilities, @@ -51,7 +51,7 @@ export interface DiscoverServices { data: DataPublicPluginStart; docLinks: DocLinksStart; DocViewer: DocViewerComponent; - history: History; + history: () => History; theme: ChartsPluginStart['theme']; filterManager: FilterManager; indexPatterns: IndexPatternsContract; @@ -67,7 +67,8 @@ export interface DiscoverServices { } export async function buildServices( core: CoreStart, - plugins: DiscoverStartPlugins + plugins: DiscoverStartPlugins, + getHistory: () => History ): Promise { const services = { savedObjectsClient: core.savedObjects.client, @@ -77,6 +78,7 @@ export async function buildServices( overlays: core.overlays, }; const savedObjectService = createSavedSearchesLoader(services); + return { addBasePath: core.http.basePath.prepend, capabilities: core.application.capabilities, @@ -85,11 +87,11 @@ export async function buildServices( data: plugins.data, docLinks: core.docLinks, DocViewer: plugins.discover.docViews.DocViewer, - history: createHashHistory(), theme: plugins.charts.theme, filterManager: plugins.data.query.filterManager, getSavedSearchById: async (id: string) => savedObjectService.get(id), getSavedSearchUrlById: async (id: string) => savedObjectService.urlFor(id), + history: getHistory, indexPatterns: plugins.data.indexPatterns, inspector: plugins.inspector, // @ts-ignore diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index 0a81ca0222b0a0..156267bdfa87e0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { createHashHistory } from 'history'; import { DiscoverServices } from './build_services'; import { createGetterSetter } from '../../../../../plugins/kibana_utils/public'; import { search } from '../../../../../plugins/data/public'; @@ -52,6 +53,11 @@ export const [getUrlTracker, setUrlTracker] = createGetterSetter<{ setTrackedUrl: (url: string) => void; }>('urlTracker'); +/** + * Makes sure discover and context are using one instance of history + */ +export const getHistory = _.once(() => createHashHistory()); + export const { getRequestInspectorStats, getResponseInspectorStats, tabifyAggResponse } = search; export { unhashUrl, diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js index 5b03b313e4e3e6..032ec7af09a301 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js @@ -81,6 +81,7 @@ function ContextAppRouteController($routeParams, $scope, $route) { defaultStepSize: getServices().uiSettings.get('context:defaultSize'), timeFieldName: indexPattern.timeFieldName, storeInSessionStorage: getServices().uiSettings.get('state:storeInSessionStorage'), + history: getServices().history(), }); this.state = { ...appState.getState() }; this.anchorId = $routeParams.id; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts index ed59143b163f69..b46995d44d826d 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts @@ -17,7 +17,7 @@ * under the License. */ import _ from 'lodash'; -import { createBrowserHistory, History } from 'history'; +import { History } from 'history'; import { createStateContainer, createKbnUrlStateStorage, @@ -71,9 +71,9 @@ interface GetStateParams { */ storeInSessionStorage?: boolean; /** - * Browser history used for testing + * History instance to use */ - history?: History; + history: History; } interface GetStateReturn { @@ -126,7 +126,7 @@ export function getState({ }: GetStateParams): GetStateReturn { const stateStorage = createKbnUrlStateStorage({ useHash: storeInSessionStorage, - history: history ? history : createBrowserHistory(), + history, }); const globalStateInitial = stateStorage.get(GLOBAL_STATE_URL_KEY) as GlobalState; 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 72276a38f6ac21..567cfda45cc0d1 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 @@ -57,8 +57,7 @@ const { core, chrome, data, - docTitle, - history, + history: getHistory, indexPatterns, filterManager, share, @@ -117,6 +116,7 @@ app.config($routeProvider => { reloadOnSearch: false, resolve: { savedObjects: function($route, Promise) { + const history = getHistory(); const savedSearchId = $route.current.params.id; return ensureDefaultIndexPattern(core, data, history).then(() => { const { appStateContainer } = getState({ history }); @@ -205,6 +205,8 @@ function discoverController( return isDefaultType($scope.indexPattern) ? $scope.indexPattern.timeFieldName : undefined; }; + const history = getHistory(); + const { appStateContainer, startSync: startStateSync, @@ -214,6 +216,7 @@ function discoverController( isAppStateDirty, kbnUrlStateStorage, getPreviousAppState, + resetInitialAppState, } = getState({ defaultAppState: getStateDefaults(), storeInSessionStorage: config.get('state:storeInSessionStorage'), @@ -373,6 +376,8 @@ function discoverController( // If the save wasn't successful, put the original values back. if (!response.id || response.error) { savedSearch.title = currentTitle; + } else { + resetInitialAppState(); } return response; }); @@ -758,7 +763,7 @@ function discoverController( } else { // Update defaults so that "reload saved query" functions correctly setAppState(getStateDefaults()); - docTitle.change(savedSearch.lastSavedTitle); + chrome.docTitle.change(savedSearch.lastSavedTitle); } } }); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field.test.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field.test.tsx index 9a6bd65813d184..fdae2c0c16c9f2 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field.test.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_field.test.tsx @@ -31,11 +31,11 @@ import { IndexPatternField } from '../../../../../../../../plugins/data/public'; jest.mock('../../../kibana_services', () => ({ getServices: () => ({ - history: { + history: () => ({ location: { search: '', }, - }, + }), capabilities: { visualize: { show: true, diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.test.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.test.tsx index 0df14515adc6d4..29451c075bcad5 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.test.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.test.tsx @@ -36,11 +36,11 @@ import { SavedObject } from '../../../../../../../../core/types'; jest.mock('../../../kibana_services', () => ({ getServices: () => ({ - history: { + history: () => ({ location: { search: '', }, - }, + }), capabilities: { visualize: { show: true, diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/visualize_url_utils.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/visualize_url_utils.ts index d146d212055b76..968ceeeab73a5f 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/visualize_url_utils.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/lib/visualize_url_utils.ts @@ -125,7 +125,7 @@ export function getVisualizeUrl( services: DiscoverServices ) { const aggsTermSize = services.uiSettings.get('discover:aggs:terms:size'); - const urlParams = parse(services.history.location.search) as Record; + const urlParams = parse(services.history().location.search) as Record; if ( (field.type === KBN_FIELD_TYPES.GEO_POINT || field.type === KBN_FIELD_TYPES.GEO_SHAPE) && diff --git a/src/legacy/core_plugins/kibana/public/discover/plugin.ts b/src/legacy/core_plugins/kibana/public/discover/plugin.ts index d05e96ccaaf0b0..42883abe98171b 100644 --- a/src/legacy/core_plugins/kibana/public/discover/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/discover/plugin.ts @@ -31,7 +31,7 @@ import { registerFeature } from './np_ready/register_feature'; import './kibana_services'; import { EmbeddableStart, EmbeddableSetup } from '../../../../../plugins/embeddable/public'; import { getInnerAngularModule, getInnerAngularModuleEmbeddable } from './get_inner_angular'; -import { setAngularModule, setServices, setUrlTracker } from './kibana_services'; +import { getHistory, setAngularModule, setServices, setUrlTracker } from './kibana_services'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; import { ChartsPluginStart } from '../../../../../plugins/charts/public'; import { buildServices } from './build_services'; @@ -98,6 +98,10 @@ export class DiscoverPlugin implements Plugin { stop: stopUrlTracker, setActiveUrl: setTrackedUrl, } = createKbnUrlTracker({ + // we pass getter here instead of plain `history`, + // so history is lazily created (when app is mounted) + // this prevents redundant `#` when not in discover app + getHistory, baseUrl: core.http.basePath.prepend('/app/kibana'), defaultSubUrl: '#/discover', storageKey: `lastUrl:${core.http.basePath.get()}:discover`, @@ -174,7 +178,7 @@ export class DiscoverPlugin implements Plugin { if (this.servicesInitialized) { return { core, plugins }; } - const services = await buildServices(core, plugins); + const services = await buildServices(core, plugins, getHistory); setServices(services); this.servicesInitialized = true; diff --git a/src/legacy/core_plugins/kibana/public/kibana.js b/src/legacy/core_plugins/kibana/public/kibana.js index 20c46765dcb302..ea0d5ad3790b13 100644 --- a/src/legacy/core_plugins/kibana/public/kibana.js +++ b/src/legacy/core_plugins/kibana/public/kibana.js @@ -49,8 +49,9 @@ import { showAppRedirectNotification } from '../../../../plugins/kibana_legacy/p import 'leaflet'; import { localApplicationService } from './local_application_service'; -npSetup.plugins.kibanaLegacy.forwardApp('doc', 'discover', { keepPrefix: true }); -npSetup.plugins.kibanaLegacy.forwardApp('context', 'discover', { keepPrefix: true }); +npSetup.plugins.kibanaLegacy.registerLegacyAppAlias('doc', 'discover', { keepPrefix: true }); +npSetup.plugins.kibanaLegacy.registerLegacyAppAlias('context', 'discover', { keepPrefix: true }); + localApplicationService.attachToAngular(routes); routes.enable(); diff --git a/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts b/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts index 14564cfd9ee784..f38c410e6832f7 100644 --- a/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts +++ b/src/legacy/core_plugins/kibana/public/local_application_service/local_application_service.ts @@ -98,14 +98,29 @@ export class LocalApplicationService { } }); - npStart.plugins.kibanaLegacy.getForwards().forEach(({ legacyAppId, newAppId, keepPrefix }) => { - angularRouteManager.when(matchAllWithPrefix(legacyAppId), { - resolveRedirectTo: ($location: ILocationService) => { - const url = $location.url(); - return `/${newAppId}${keepPrefix ? url : url.replace(legacyAppId, '')}`; + npStart.plugins.kibanaLegacy.getForwards().forEach(forwardDefinition => { + angularRouteManager.when(matchAllWithPrefix(forwardDefinition.legacyAppId), { + outerAngularWrapperRoute: true, + reloadOnSearch: false, + reloadOnUrl: false, + template: '', + controller($location: ILocationService) { + const newPath = forwardDefinition.rewritePath($location.url()); + npStart.core.application.navigateToApp(forwardDefinition.newAppId, { path: newPath }); }, }); }); + + npStart.plugins.kibanaLegacy + .getLegacyAppAliases() + .forEach(({ legacyAppId, newAppId, keepPrefix }) => { + angularRouteManager.when(matchAllWithPrefix(legacyAppId), { + resolveRedirectTo: ($location: ILocationService) => { + const url = $location.url(); + return `/${newAppId}${keepPrefix ? url : url.replace(legacyAppId, '')}`; + }, + }); + }); } } diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/constants.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/constants.ts new file mode 100644 index 00000000000000..56da031eb4ee86 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/constants.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 const TAB_INDEXED_FIELDS = 'indexedFields'; +export const TAB_SCRIPTED_FIELDS = 'scriptedFields'; +export const TAB_SOURCE_FILTERS = 'sourceFilters'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.html b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field.html similarity index 100% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.html rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field.html diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js deleted file mode 100644 index 95d6cb6878e532..00000000000000 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js +++ /dev/null @@ -1,180 +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 { IndexPatternField } from '../../../../../../../../../plugins/data/public'; -import { RegistryFieldFormatEditorsProvider } from 'ui/registry/field_format_editors'; -import { docTitle } from 'ui/doc_title'; -import { KbnUrlProvider } from 'ui/url'; -import uiRoutes from 'ui/routes'; -import { toastNotifications } from 'ui/notify'; -import { npStart } from 'ui/new_platform'; - -import template from './create_edit_field.html'; -import { getEditFieldBreadcrumbs, getCreateFieldBreadcrumbs } from '../../breadcrumbs'; - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { FieldEditor } from 'ui/field_editor'; -import { I18nContext } from 'ui/i18n'; -import { i18n } from '@kbn/i18n'; - -import { IndexHeader } from '../index_header'; - -const REACT_FIELD_EDITOR_ID = 'reactFieldEditor'; -const renderFieldEditor = ( - $scope, - indexPattern, - field, - { getConfig, $http, fieldFormatEditors, redirectAway } -) => { - $scope.$$postDigest(() => { - const node = document.getElementById(REACT_FIELD_EDITOR_ID); - if (!node) { - return; - } - - render( - - - - , - node - ); - }); -}; - -const destroyFieldEditor = () => { - const node = document.getElementById(REACT_FIELD_EDITOR_ID); - node && unmountComponentAtNode(node); -}; - -uiRoutes - .when('/management/kibana/index_patterns/:indexPatternId/field/:fieldName*', { - mode: 'edit', - k7Breadcrumbs: getEditFieldBreadcrumbs, - }) - .when('/management/kibana/index_patterns/:indexPatternId/create-field/', { - mode: 'create', - k7Breadcrumbs: getCreateFieldBreadcrumbs, - }) - .defaults(/management\/kibana\/index_patterns\/[^\/]+\/(field|create-field)(\/|$)/, { - template, - mapBreadcrumbs($route, breadcrumbs) { - const { indexPattern } = $route.current.locals; - return breadcrumbs.map(crumb => { - if (crumb.id !== indexPattern.id) { - return crumb; - } - - return { - ...crumb, - display: indexPattern.title, - }; - }); - }, - resolve: { - indexPattern: function($route, Promise, redirectWhenMissing) { - const { indexPatterns } = npStart.plugins.data; - return Promise.resolve(indexPatterns.get($route.current.params.indexPatternId)).catch( - redirectWhenMissing('/management/kibana/index_patterns') - ); - }, - }, - controllerAs: 'fieldSettings', - controller: function FieldEditorPageController( - $scope, - $route, - $timeout, - $http, - Private, - config - ) { - const getConfig = (...args) => config.get(...args); - const fieldFormatEditors = Private(RegistryFieldFormatEditorsProvider); - const kbnUrl = Private(KbnUrlProvider); - - this.mode = $route.current.mode; - this.indexPattern = $route.current.locals.indexPattern; - - if (this.mode === 'edit') { - const fieldName = $route.current.params.fieldName; - this.field = this.indexPattern.fields.getByName(fieldName); - - if (!this.field) { - const message = i18n.translate('kbn.management.editIndexPattern.scripted.noFieldLabel', { - defaultMessage: - "'{indexPatternTitle}' index pattern doesn't have a scripted field called '{fieldName}'", - values: { indexPatternTitle: this.indexPattern.title, fieldName }, - }); - toastNotifications.add(message); - - kbnUrl.redirectToRoute(this.indexPattern, 'edit'); - return; - } - } else if (this.mode === 'create') { - this.field = new IndexPatternField(this.indexPattern, { - scripted: true, - type: 'number', - }); - } else { - const errorMessage = i18n.translate( - 'kbn.management.editIndexPattern.scripted.unknownModeErrorMessage', - { - defaultMessage: 'unknown fieldSettings mode {mode}', - values: { mode: this.mode }, - } - ); - throw new Error(errorMessage); - } - - const fieldName = - this.field.name || - i18n.translate('kbn.management.editIndexPattern.scripted.newFieldPlaceholder', { - defaultMessage: 'New Scripted Field', - }); - docTitle.change([fieldName, this.indexPattern.title]); - - renderFieldEditor($scope, this.indexPattern, this.field, { - getConfig, - $http, - fieldFormatEditors, - redirectAway: () => { - $timeout(() => { - kbnUrl.changeToRoute( - this.indexPattern, - this.field.scripted ? 'scriptedFields' : 'indexedFields' - ); - }); - }, - }); - - $scope.$on('$destroy', () => { - destroyFieldEditor(); - }); - }, - }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.tsx new file mode 100644 index 00000000000000..4839870f0f3c8a --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.tsx @@ -0,0 +1,111 @@ +/* + * 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 { withRouter, RouteComponentProps } from 'react-router-dom'; +// @ts-ignore +import { FieldEditor } from 'ui/field_editor'; + +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { IndexHeader } from '../index_header'; +import { IndexPattern, IndexPatternField } from '../../../../../../../../../plugins/data/public'; +import { ChromeDocTitle, NotificationsStart } from '../../../../../../../../../core/public'; +import { TAB_SCRIPTED_FIELDS, TAB_INDEXED_FIELDS } from '../constants'; + +interface CreateEditFieldProps extends RouteComponentProps { + indexPattern: IndexPattern; + mode?: string; + fieldName?: string; + fieldFormatEditors: any; + getConfig: (name: string) => any; + services: { + notifications: NotificationsStart; + docTitle: ChromeDocTitle; + http: Function; + }; +} + +const newFieldPlaceholder = i18n.translate( + 'kbn.management.editIndexPattern.scripted.newFieldPlaceholder', + { + defaultMessage: 'New Scripted Field', + } +); + +export const CreateEditField = withRouter( + ({ + indexPattern, + mode, + fieldName, + fieldFormatEditors, + getConfig, + services, + history, + }: CreateEditFieldProps) => { + const field = + mode === 'edit' && fieldName + ? indexPattern.fields.getByName(fieldName) + : new IndexPatternField(indexPattern, { + scripted: true, + type: 'number', + }); + + const url = `/management/kibana/index_patterns/${indexPattern.id}`; + + if (mode === 'edit') { + if (!field) { + const message = i18n.translate('kbn.management.editIndexPattern.scripted.noFieldLabel', { + defaultMessage: + "'{indexPatternTitle}' index pattern doesn't have a scripted field called '{fieldName}'", + values: { indexPatternTitle: indexPattern.title, fieldName }, + }); + services.notifications.toasts.addWarning(message); + history.push(url); + } + } + + const docFieldName = field?.name || newFieldPlaceholder; + + services.docTitle.change([docFieldName, indexPattern.title]); + + const redirectAway = () => { + history.push(`${url}?_a=(tab:${field?.scripted ? TAB_SCRIPTED_FIELDS : TAB_INDEXED_FIELDS})`); + }; + + return ( + + + + + + + + + ); + } +); diff --git a/src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/index.ts similarity index 93% rename from src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/index.ts index 1030e971d64504..473a8f5b57c825 100644 --- a/src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { npSetup, npStart } from 'ui/new_platform'; +export { CreateEditField } from './create_edit_field'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js index 69184a513f53a5..3239a17f109e43 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js @@ -18,15 +18,18 @@ */ import _ from 'lodash'; +import { HashRouter } from 'react-router-dom'; import { IndexHeader } from './index_header'; -import './create_edit_field'; +import { CreateEditField } from './create_edit_field'; import { docTitle } from 'ui/doc_title'; import { KbnUrlProvider } from 'ui/url'; import { IndicesEditSectionsProvider } from './edit_sections'; import { fatalError, toastNotifications } from 'ui/notify'; +import { RegistryFieldFormatEditorsProvider } from 'ui/registry/field_format_editors'; import uiRoutes from 'ui/routes'; import { uiModules } from 'ui/modules'; import template from './edit_index_pattern.html'; +import createEditFieldtemplate from './create_edit_field.html'; import { fieldWildcardMatcher } from '../../../../../../../../plugins/kibana_utils/public'; import { subscribeWithScope } from '../../../../../../../../plugins/kibana_legacy/public'; import React from 'react'; @@ -37,8 +40,12 @@ import { ScriptedFieldsTable } from './scripted_fields_table'; import { i18n } from '@kbn/i18n'; import { I18nContext } from 'ui/i18n'; import { npStart } from 'ui/new_platform'; - -import { getEditBreadcrumbs } from '../breadcrumbs'; +import { + getEditBreadcrumbs, + getEditFieldBreadcrumbs, + getCreateFieldBreadcrumbs, +} from '../breadcrumbs'; +import { TAB_INDEXED_FIELDS, TAB_SCRIPTED_FIELDS, TAB_SOURCE_FILTERS } from './constants'; import { createEditIndexPatternPageStateContainer } from './edit_index_pattern_state_container'; const REACT_SOURCE_FILTERS_DOM_ELEMENT_ID = 'reactSourceFiltersTable'; @@ -46,10 +53,6 @@ const REACT_INDEXED_FIELDS_DOM_ELEMENT_ID = 'reactIndexedFieldsTable'; const REACT_SCRIPTED_FIELDS_DOM_ELEMENT_ID = 'reactScriptedFieldsTable'; const REACT_INDEX_HEADER_DOM_ELEMENT_ID = 'reactIndexHeader'; -const TAB_INDEXED_FIELDS = 'indexedFields'; -const TAB_SCRIPTED_FIELDS = 'scriptedFields'; -const TAB_SOURCE_FILTERS = 'sourceFilters'; - const EDIT_FIELD_PATH = '/management/kibana/index_patterns/{{indexPattern.id}}/field/{{name}}'; function updateSourceFiltersTable($scope) { @@ -425,3 +428,84 @@ uiModules renderIndexHeader($scope, config); }); + +// routes for create edit field. Will be removed after migartion all component to react. +const REACT_FIELD_EDITOR_ID = 'reactFieldEditor'; +const renderCreateEditField = ($scope, $route, getConfig, $http, fieldFormatEditors) => { + $scope.$$postDigest(() => { + const node = document.getElementById(REACT_FIELD_EDITOR_ID); + if (!node) { + return; + } + + render( + + + + + , + node + ); + }); +}; + +const destroyCreateEditField = () => { + const node = document.getElementById(REACT_FIELD_EDITOR_ID); + node && unmountComponentAtNode(node); +}; + +uiRoutes + .when('/management/kibana/index_patterns/:indexPatternId/field/:fieldName*', { + mode: 'edit', + k7Breadcrumbs: getEditFieldBreadcrumbs, + }) + .when('/management/kibana/index_patterns/:indexPatternId/create-field/', { + mode: 'create', + k7Breadcrumbs: getCreateFieldBreadcrumbs, + }) + .defaults(/management\/kibana\/index_patterns\/[^\/]+\/(field|create-field)(\/|$)/, { + template: createEditFieldtemplate, + mapBreadcrumbs($route, breadcrumbs) { + const { indexPattern } = $route.current.locals; + return breadcrumbs.map(crumb => { + if (crumb.id !== indexPattern.id) { + return crumb; + } + + return { + ...crumb, + display: indexPattern.title, + }; + }); + }, + resolve: { + indexPattern: function($route, Promise, redirectWhenMissing) { + const { indexPatterns } = npStart.plugins.data; + return Promise.resolve(indexPatterns.get($route.current.params.indexPatternId)).catch( + redirectWhenMissing('/management/kibana/index_patterns') + ); + }, + }, + controllerAs: 'fieldSettings', + controller: function FieldEditorPageController($scope, $route, $http, Private, config) { + const getConfig = (...args) => config.get(...args); + const fieldFormatEditors = Private(RegistryFieldFormatEditorsProvider); + + renderCreateEditField($scope, $route, getConfig, $http, fieldFormatEditors); + + $scope.$on('$destroy', () => { + destroyCreateEditField(); + }); + }, + }); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.tsx index 866d10ecb0e19d..deac85d9a32e9a 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.tsx +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/index_header/index_header.tsx @@ -30,8 +30,8 @@ import { import { IIndexPattern } from '../../../../../../../../../plugins/data/public'; interface IndexHeaderProps { - defaultIndex: string; indexPattern: IIndexPattern; + defaultIndex?: string; setDefault?: () => void; refreshFields?: () => void; deleteIndexPattern?: () => void; @@ -77,7 +77,7 @@ export function IndexHeader({ )} - +

{indexPattern.title}

diff --git a/src/legacy/core_plugins/vis_type_table/index.ts b/src/legacy/core_plugins/vis_type_table/index.ts deleted file mode 100644 index 04ca9da7de32b8..00000000000000 --- a/src/legacy/core_plugins/vis_type_table/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { resolve } from 'path'; -import { Legacy } from 'kibana'; - -import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types'; - -const tableVisPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => - new Plugin({ - id: 'table_vis', - require: ['kibana', 'elasticsearch'], - publicDir: resolve(__dirname, 'public'), - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - 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 tableVisPluginInitializer; diff --git a/src/legacy/core_plugins/vis_type_table/package.json b/src/legacy/core_plugins/vis_type_table/package.json deleted file mode 100644 index 2809b0e0478365..00000000000000 --- a/src/legacy/core_plugins/vis_type_table/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "table_vis", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/_index.scss b/src/legacy/core_plugins/vis_type_table/public/agg_table/_index.scss deleted file mode 100644 index b19d4a887a7f39..00000000000000 --- a/src/legacy/core_plugins/vis_type_table/public/agg_table/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'agg_table'; diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/_index.scss b/src/legacy/core_plugins/vis_type_table/public/paginated_table/_index.scss deleted file mode 100644 index 9473b847d3c2bd..00000000000000 --- a/src/legacy/core_plugins/vis_type_table/public/paginated_table/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './table_cell_filter'; diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts b/src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts deleted file mode 100644 index 43816121bc23bc..00000000000000 --- a/src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts +++ /dev/null @@ -1,98 +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 { i18n } from '@kbn/i18n'; -import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../../../plugins/vis_default_editor/public'; -import { Vis } from '../../../../plugins/visualizations/public'; -import { tableVisResponseHandler } from './table_vis_response_handler'; -// @ts-ignore -import tableVisTemplate from './table_vis.html'; -import { TableOptions } from './components/table_vis_options'; -import { TableVisualizationController } from './vis_controller'; - -export const tableVisTypeDefinition = { - type: 'table', - name: 'table', - title: i18n.translate('visTypeTable.tableVisTitle', { - defaultMessage: 'Data Table', - }), - icon: 'visTable', - description: i18n.translate('visTypeTable.tableVisDescription', { - defaultMessage: 'Display values in a table', - }), - visualization: TableVisualizationController, - visConfig: { - defaults: { - perPage: 10, - showPartialRows: false, - showMetricsAtAllLevels: false, - sort: { - columnIndex: null, - direction: null, - }, - showTotal: false, - totalFunc: 'sum', - percentageCol: '', - }, - template: tableVisTemplate, - }, - editorConfig: { - optionsTemplate: TableOptions, - schemas: new Schemas([ - { - group: AggGroupNames.Metrics, - name: 'metric', - title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.metricTitle', { - defaultMessage: 'Metric', - }), - aggFilter: ['!geo_centroid', '!geo_bounds'], - aggSettings: { - top_hits: { - allowStrings: true, - }, - }, - min: 1, - defaults: [{ type: 'count', schema: 'metric' }], - }, - { - group: AggGroupNames.Buckets, - name: 'bucket', - title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.bucketTitle', { - defaultMessage: 'Split rows', - }), - aggFilter: ['!filter'], - }, - { - group: AggGroupNames.Buckets, - name: 'split', - title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.splitTitle', { - defaultMessage: 'Split table', - }), - min: 0, - max: 1, - aggFilter: ['!filter'], - }, - ]), - }, - responseHandler: tableVisResponseHandler, - hierarchicalData: (vis: Vis) => { - return Boolean(vis.params.showPartialRows || vis.params.showMetricsAtAllLevels); - }, -}; diff --git a/src/legacy/core_plugins/vis_type_table/public/vis_controller.ts b/src/legacy/core_plugins/vis_type_table/public/vis_controller.ts deleted file mode 100644 index 5bb730d2f9b105..00000000000000 --- a/src/legacy/core_plugins/vis_type_table/public/vis_controller.ts +++ /dev/null @@ -1,104 +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 angular, { IModule, auto, IRootScopeService, IScope, ICompileService } from 'angular'; -import $ from 'jquery'; - -import { VisParams, ExprVis } from '../../../../plugins/visualizations/public'; -import { npStart } from './legacy_imports'; -import { getAngularModule } from './get_inner_angular'; -import { initTableVisLegacyModule } from './table_vis_legacy_module'; - -const innerAngularName = 'kibana/table_vis'; - -export class TableVisualizationController { - private tableVisModule: IModule | undefined; - private injector: auto.IInjectorService | undefined; - el: JQuery; - vis: ExprVis; - $rootScope: IRootScopeService | null = null; - $scope: (IScope & { [key: string]: any }) | undefined; - $compile: ICompileService | undefined; - - constructor(domeElement: Element, vis: ExprVis) { - this.el = $(domeElement); - this.vis = vis; - } - - getInjector() { - if (!this.injector) { - const mountpoint = document.createElement('div'); - mountpoint.setAttribute('style', 'height: 100%; width: 100%;'); - this.injector = angular.bootstrap(mountpoint, [innerAngularName]); - this.el.append(mountpoint); - } - - return this.injector; - } - - initLocalAngular() { - if (!this.tableVisModule) { - this.tableVisModule = getAngularModule(innerAngularName, npStart.core); - initTableVisLegacyModule(this.tableVisModule); - } - } - - async render(esResponse: object, visParams: VisParams) { - this.initLocalAngular(); - - return new Promise(async (resolve, reject) => { - if (!this.$rootScope) { - const $injector = this.getInjector(); - this.$rootScope = $injector.get('$rootScope'); - this.$compile = $injector.get('$compile'); - } - const updateScope = () => { - if (!this.$scope) { - return; - } - this.$scope.vis = this.vis; - this.$scope.visState = { params: visParams }; - this.$scope.esResponse = esResponse; - - this.$scope.visParams = visParams; - this.$scope.renderComplete = resolve; - this.$scope.renderFailed = reject; - this.$scope.resize = Date.now(); - this.$scope.$apply(); - }; - - if (!this.$scope && this.$compile) { - this.$scope = this.$rootScope.$new(); - this.$scope.uiState = this.vis.getUiState(); - updateScope(); - this.el.find('div').append(this.$compile(this.vis.type!.visConfig.template)(this.$scope)); - this.$scope.$apply(); - } else { - updateScope(); - } - }); - } - - destroy() { - if (this.$rootScope) { - this.$rootScope.$destroy(); - this.$rootScope = null; - } - } -} diff --git a/src/legacy/ui/public/i18n/index.tsx b/src/legacy/ui/public/i18n/index.tsx index 4d0f5d3a5bd567..c918554563fcb7 100644 --- a/src/legacy/ui/public/i18n/index.tsx +++ b/src/legacy/ui/public/i18n/index.tsx @@ -44,7 +44,7 @@ export function wrapInI18nContext

(ComponentToWrap: React.ComponentType

) { } uiModules - .get('i18n') + .get('i18n', ['ngSanitize']) .provider('i18n', I18nProvider) .filter('i18n', i18nFilter) .directive('i18nId', i18nDirective); diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index f577a29ce90b91..f14f26613ef019 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -242,6 +242,7 @@ export const npSetup = { }, kibanaLegacy: { registerLegacyApp: () => {}, + registerLegacyAppAlias: () => {}, forwardApp: () => {}, config: { defaultAppId: 'home', @@ -362,6 +363,7 @@ export const npStart = { kibanaLegacy: { getApps: () => [], getForwards: () => [], + getLegacyAppAliases: () => [], config: { defaultAppId: 'home', }, diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index 4557d911620a23..1093153edbbf7e 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -78,10 +78,10 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { '{{this}}', {{/each}} '{{regularBundlePath}}/commons.bundle.js', - {{!-- '{{regularBundlePath}}/plugin:data/data.plugin.js', --}} - '{{regularBundlePath}}/plugin:kibanaUtils/kibanaUtils.plugin.js', - '{{regularBundlePath}}/plugin:esUiShared/esUiShared.plugin.js', - '{{regularBundlePath}}/plugin:kibanaReact/kibanaReact.plugin.js' + {{!-- '{{regularBundlePath}}/plugin/data/data.plugin.js', --}} + '{{regularBundlePath}}/plugin/kibanaUtils/kibanaUtils.plugin.js', + '{{regularBundlePath}}/plugin/esUiShared/esUiShared.plugin.js', + '{{regularBundlePath}}/plugin/kibanaReact/kibanaReact.plugin.js' ], function () { load([ '{{regularBundlePath}}/{{appId}}.bundle.js', diff --git a/src/optimize/bundles_route/bundles_route.js b/src/optimize/bundles_route/bundles_route.js index f4e3108f80a3b8..0c2e98b5acd63c 100644 --- a/src/optimize/bundles_route/bundles_route.js +++ b/src/optimize/bundles_route/bundles_route.js @@ -79,8 +79,8 @@ export function createBundlesRoute({ ), ...npUiPluginPublicDirs.map(({ id, path }) => buildRouteForBundles( - `${basePublicPath}/bundles/plugin:${id}/`, - `/bundles/plugin:${id}/`, + `${basePublicPath}/bundles/plugin/${id}/`, + `/bundles/plugin/${id}/`, path, fileHashCache ) diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index b62b728beca35f..05a4141483587f 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -293,6 +293,7 @@ import { convertIPRangeToString, intervalOptions, // only used in Discover isDateHistogramBucketAggConfig, + isNumberType, isStringType, isType, parentPipelineType, @@ -392,6 +393,7 @@ export const search = { InvalidEsCalendarIntervalError, InvalidEsIntervalFormatError, isDateHistogramBucketAggConfig, + isNumberType, isStringType, isType, isValidEsInterval, diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 427c4f7864554d..6383f618641469 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1545,8 +1545,9 @@ export const search: { InvalidEsCalendarIntervalError: typeof InvalidEsCalendarIntervalError; InvalidEsIntervalFormatError: typeof InvalidEsIntervalFormatError; isDateHistogramBucketAggConfig: typeof isDateHistogramBucketAggConfig; + isNumberType: (agg: import("./search").AggConfig) => boolean; isStringType: (agg: import("./search").AggConfig) => boolean; - isType: (type: string) => (agg: import("./search").AggConfig) => boolean; + isType: (...types: string[]) => (agg: import("./search").AggConfig) => boolean; isValidEsInterval: typeof isValidEsInterval; isValidInterval: typeof isValidInterval; parentPipelineType: string; @@ -1874,21 +1875,21 @@ export type TSearchStrategyProvider = (context: ISearc // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getRoutes" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:404:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:405:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:408:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:414:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:33:33 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:37:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromEvent" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts b/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts index 0beeb1c3722752..116f8cfad60f6d 100644 --- a/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts +++ b/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts @@ -21,20 +21,22 @@ import { isString, isObject } from 'lodash'; import { IBucketAggConfig, BucketAggType, BucketAggParam } from './bucket_agg_type'; import { IAggConfig } from '../agg_config'; -export const isType = (type: string) => { +export const isType = (...types: string[]) => { return (agg: IAggConfig): boolean => { const field = agg.params.field; - return field && field.type === type; + return types.some(type => field && field.type === type); }; }; +export const isNumberType = isType('number'); export const isStringType = isType('string'); +export const isStringOrNumberType = isType('string', 'number'); export const migrateIncludeExcludeFormat = { serialize(this: BucketAggParam, value: any, agg: IBucketAggConfig) { if (this.shouldShow && !this.shouldShow(agg)) return; - if (!value || isString(value)) return value; + if (!value || isString(value) || Array.isArray(value)) return value; else return value.pattern; }, write( @@ -44,7 +46,12 @@ export const migrateIncludeExcludeFormat = { ) { const value = aggConfig.getParam(this.name); - if (isObject(value)) { + if (Array.isArray(value) && value.length > 0 && isNumberType(aggConfig)) { + const parsedValue = value.filter((val): val is number => Number.isFinite(val)); + if (parsedValue.length) { + output.params[this.name] = parsedValue; + } + } else if (isObject(value)) { output.params[this.name] = value.pattern; } else if (value && isStringType(aggConfig)) { output.params[this.name] = value; diff --git a/src/plugins/data/public/search/aggs/buckets/terms.test.ts b/src/plugins/data/public/search/aggs/buckets/terms.test.ts index 0dc052bd1fdf6d..9769efb6da7494 100644 --- a/src/plugins/data/public/search/aggs/buckets/terms.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/terms.test.ts @@ -75,5 +75,65 @@ describe('Terms Agg', () => { expect(params.include).toBe('404'); expect(params.exclude).toBe('400'); }); + + test('accepts string from string field type and writes this value', () => { + const aggConfigs = getAggConfigs({ + include: 'include value', + exclude: 'exclude value', + field: { + name: 'string_field', + type: 'string', + }, + orderAgg: { + type: 'count', + }, + }); + + const { [BUCKET_TYPES.TERMS]: params } = aggConfigs.aggs[0].toDsl(); + + expect(params.field).toBe('string_field'); + expect(params.include).toBe('include value'); + expect(params.exclude).toBe('exclude value'); + }); + + test('accepts empty array from number field type and does not write a value', () => { + const aggConfigs = getAggConfigs({ + include: [], + exclude: [], + field: { + name: 'empty_number_field', + type: 'number', + }, + orderAgg: { + type: 'count', + }, + }); + + const { [BUCKET_TYPES.TERMS]: params } = aggConfigs.aggs[0].toDsl(); + + expect(params.field).toBe('empty_number_field'); + expect(params.include).toBe(undefined); + expect(params.exclude).toBe(undefined); + }); + + test('filters array with empty strings from number field type and writes only numbers', () => { + const aggConfigs = getAggConfigs({ + include: [1.1, 2, '', 3.33, ''], + exclude: ['', 4, 5.555, '', 6], + field: { + name: 'number_field', + type: 'number', + }, + orderAgg: { + type: 'count', + }, + }); + + const { [BUCKET_TYPES.TERMS]: params } = aggConfigs.aggs[0].toDsl(); + + expect(params.field).toBe('number_field'); + expect(params.include).toStrictEqual([1.1, 2, 3.33]); + expect(params.exclude).toStrictEqual([4, 5.555, 6]); + }); }); }); diff --git a/src/plugins/data/public/search/aggs/buckets/terms.ts b/src/plugins/data/public/search/aggs/buckets/terms.ts index 5baa38af0e8d61..698e0dfb1d3404 100644 --- a/src/plugins/data/public/search/aggs/buckets/terms.ts +++ b/src/plugins/data/public/search/aggs/buckets/terms.ts @@ -22,7 +22,10 @@ import { i18n } from '@kbn/i18n'; import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { createFilterTerms } from './create_filter/terms'; -import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exclude_format'; +import { + isStringOrNumberType, + migrateIncludeExcludeFormat, +} from './migrate_include_exclude_format'; import { IAggConfigs } from '../agg_configs'; import { Adapters } from '../../../../../inspector/public'; @@ -266,7 +269,7 @@ export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDe }), type: 'string', advanced: true, - shouldShow: isStringType, + shouldShow: isStringOrNumberType, ...migrateIncludeExcludeFormat, }, { @@ -276,7 +279,7 @@ export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDe }), type: 'string', advanced: true, - shouldShow: isStringType, + shouldShow: isStringOrNumberType, ...migrateIncludeExcludeFormat, }, ], diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/authorization_provider.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/authorization_provider.tsx new file mode 100644 index 00000000000000..68f1cf2045efb2 --- /dev/null +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/authorization_provider.tsx @@ -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 { HttpSetup } from 'kibana/public'; +import React, { createContext, useContext } from 'react'; + +import { useRequest } from '../../../public'; + +import { Error as CustomError } from './section_error'; + +import { Privileges } from '../types'; + +interface Authorization { + isLoading: boolean; + apiError: CustomError | null; + privileges: Privileges; +} + +const initialValue: Authorization = { + isLoading: true, + apiError: null, + privileges: { + hasAllPrivileges: true, + missingPrivileges: {}, + }, +}; + +export const AuthorizationContext = createContext(initialValue); + +export const useAuthorizationContext = () => { + const ctx = useContext(AuthorizationContext); + if (!ctx) { + throw new Error('AuthorizationContext can only be used inside of AuthorizationProvider!'); + } + return ctx; +}; + +interface Props { + privilegesEndpoint: string; + children: React.ReactNode; + httpClient: HttpSetup; +} + +export const AuthorizationProvider = ({ privilegesEndpoint, httpClient, children }: Props) => { + const { isLoading, error, data: privilegesData } = useRequest(httpClient, { + path: privilegesEndpoint, + method: 'get', + }); + + const value = { + isLoading, + privileges: isLoading ? { hasAllPrivileges: true, missingPrivileges: {} } : privilegesData, + apiError: error ? error : null, + } as Authorization; + + return {children}; +}; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/index.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/index.ts new file mode 100644 index 00000000000000..71be3cc6152ca8 --- /dev/null +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/index.ts @@ -0,0 +1,30 @@ +/* + * 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 { + AuthorizationProvider, + AuthorizationContext, + useAuthorizationContext, +} from './authorization_provider'; + +export { WithPrivileges } from './with_privileges'; + +export { NotAuthorizedSection } from './not_authorized_section'; + +export { Error, SectionError } from './section_error'; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/not_authorized_section.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/not_authorized_section.tsx new file mode 100644 index 00000000000000..c35f674ef9ec42 --- /dev/null +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/not_authorized_section.tsx @@ -0,0 +1,30 @@ +/* + * 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 { EuiEmptyPrompt } from '@elastic/eui'; + +interface Props { + title: React.ReactNode; + message: React.ReactNode | string; +} + +export const NotAuthorizedSection = ({ title, message }: Props) => ( + {title}} body={

{message}

} /> +); diff --git a/x-pack/plugins/snapshot_restore/public/application/components/section_error.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/section_error.tsx similarity index 54% rename from x-pack/plugins/snapshot_restore/public/application/components/section_error.tsx rename to src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/section_error.tsx index bd9e48796779e7..3d56309adae97f 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/section_error.tsx +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/section_error.tsx @@ -1,7 +1,20 @@ /* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. + * 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 { EuiCallOut, EuiSpacer } from '@elastic/eui'; diff --git a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/with_privileges.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/with_privileges.tsx similarity index 70% rename from x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/with_privileges.tsx rename to src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/with_privileges.tsx index 223a2882c3cab3..8f4b2b976d1411 100644 --- a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/with_privileges.tsx +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/with_privileges.tsx @@ -1,13 +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. + * 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 { useContext } from 'react'; +import { MissingPrivileges } from '../types'; -import { MissingPrivileges } from '../../../../../common/types'; -import { AuthorizationContext } from './authorization_provider'; +import { useAuthorizationContext } from './authorization_provider'; interface Props { /** @@ -29,7 +41,7 @@ const toArray = (value: string | string[]): string[] => Array.isArray(value) ? (value as string[]) : ([value] as string[]); export const WithPrivileges = ({ privileges: requiredPrivileges, children }: Props) => { - const { isLoading, privileges } = useContext(AuthorizationContext); + const { isLoading, privileges } = useAuthorizationContext(); const privilegesToArray: Privilege[] = toArray(requiredPrivileges).map(p => { const [section, privilege] = p.split('.'); diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/index.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/index.ts new file mode 100644 index 00000000000000..ad89052b3bb541 --- /dev/null +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/index.ts @@ -0,0 +1,30 @@ +/* + * 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 { + WithPrivileges, + NotAuthorizedSection, + AuthorizationProvider, + AuthorizationContext, + SectionError, + Error, + useAuthorizationContext, +} from './components'; + +export { Privileges, MissingPrivileges } from './types'; diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/types.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/types.ts new file mode 100644 index 00000000000000..cdc2052122688a --- /dev/null +++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/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 interface MissingPrivileges { + [key: string]: string[] | undefined; +} + +export interface Privileges { + hasAllPrivileges: boolean; + missingPrivileges: MissingPrivileges; +} diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/index.js b/src/plugins/es_ui_shared/public/authorization/index.ts similarity index 92% rename from src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/index.js rename to src/plugins/es_ui_shared/public/authorization/index.ts index 890a3b2622577b..3a02c0d2694f3d 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/index.js +++ b/src/plugins/es_ui_shared/public/authorization/index.ts @@ -17,4 +17,4 @@ * under the License. */ -import './create_edit_field'; +export * from '../../__packages_do_not_import__/authorization'; diff --git a/src/plugins/es_ui_shared/public/index.ts b/src/plugins/es_ui_shared/public/index.ts index a0371bf351193c..7e5510d7c9c651 100644 --- a/src/plugins/es_ui_shared/public/index.ts +++ b/src/plugins/es_ui_shared/public/index.ts @@ -47,6 +47,18 @@ export { expandLiteralStrings, } from './console_lang'; +export { + AuthorizationContext, + AuthorizationProvider, + NotAuthorizedSection, + WithPrivileges, + Privileges, + MissingPrivileges, + SectionError, + Error, + useAuthorizationContext, +} from './authorization'; + /** dummy plugin, we just want esUiShared to have its own bundle */ export function plugin() { return new (class EsUiSharedPlugin { diff --git a/src/plugins/kibana_legacy/public/angular/angular_config.tsx b/src/plugins/kibana_legacy/public/angular/angular_config.tsx index 71cd57ef2d72ec..295cf27688c805 100644 --- a/src/plugins/kibana_legacy/public/angular/angular_config.tsx +++ b/src/plugins/kibana_legacy/public/angular/angular_config.tsx @@ -92,9 +92,9 @@ export const configureAppAngularModule = ( ) => { const core = 'core' in newPlatform ? newPlatform.core : newPlatform; const packageInfo = - 'injectedMetadata' in newPlatform - ? newPlatform.injectedMetadata.getLegacyMetadata() - : newPlatform.env.packageInfo; + 'env' in newPlatform + ? newPlatform.env.packageInfo + : newPlatform.injectedMetadata.getLegacyMetadata(); if ('injectedMetadata' in newPlatform) { forOwn(newPlatform.injectedMetadata.getInjectedVars(), (val, name) => { diff --git a/src/plugins/kibana_legacy/public/mocks.ts b/src/plugins/kibana_legacy/public/mocks.ts index 8e9a05b186191f..2fdd0d8b4be590 100644 --- a/src/plugins/kibana_legacy/public/mocks.ts +++ b/src/plugins/kibana_legacy/public/mocks.ts @@ -25,6 +25,7 @@ export type Start = jest.Mocked>; const createSetupContract = (): Setup => ({ forwardApp: jest.fn(), + registerLegacyAppAlias: jest.fn(), registerLegacyApp: jest.fn(), config: { defaultAppId: 'home', @@ -37,6 +38,7 @@ const createSetupContract = (): Setup => ({ const createStartContract = (): Start => ({ getApps: jest.fn(), + getLegacyAppAliases: jest.fn(), getForwards: jest.fn(), config: { defaultAppId: 'home', diff --git a/src/plugins/kibana_legacy/public/plugin.ts b/src/plugins/kibana_legacy/public/plugin.ts index 2ad620f355848a..831fc3f0d4a71f 100644 --- a/src/plugins/kibana_legacy/public/plugin.ts +++ b/src/plugins/kibana_legacy/public/plugin.ts @@ -28,12 +28,18 @@ import { Observable } from 'rxjs'; import { ConfigSchema } from '../config'; import { getDashboardConfig } from './dashboard_config'; -interface ForwardDefinition { +interface LegacyAppAliasDefinition { legacyAppId: string; newAppId: string; keepPrefix: boolean; } +interface ForwardDefinition { + legacyAppId: string; + newAppId: string; + rewritePath: (legacyPath: string) => string; +} + export type AngularRenderedAppUpdater = ( app: AppBase ) => Partial | undefined; @@ -54,7 +60,8 @@ export interface AngularRenderedApp extends App { export class KibanaLegacyPlugin { private apps: AngularRenderedApp[] = []; - private forwards: ForwardDefinition[] = []; + private legacyAppAliases: LegacyAppAliasDefinition[] = []; + private forwardDefinitions: ForwardDefinition[] = []; constructor(private readonly initializerContext: PluginInitializerContext) {} @@ -94,17 +101,55 @@ export class KibanaLegacyPlugin { * renaming or nesting plugins. For route changes after the prefix, please * use the routing mechanism of your app. * + * This method just redirects URLs within the legacy `kibana` app. + * * @param legacyAppId The name of the old app to forward URLs from * @param newAppId The name of the new app that handles the URLs now * @param options Whether the prefix of the old app is kept to nest the legacy * path into the new path */ - forwardApp: ( + registerLegacyAppAlias: ( legacyAppId: string, newAppId: string, options: { keepPrefix: boolean } = { keepPrefix: false } ) => { - this.forwards.push({ legacyAppId, newAppId, ...options }); + this.legacyAppAliases.push({ legacyAppId, newAppId, ...options }); + }, + + /** + * Forwards URLs within the legacy `kibana` app to a new platform application. + * + * @param legacyAppId The name of the old app to forward URLs from + * @param newAppId The name of the new app that handles the URLs now + * @param rewritePath Function to rewrite the legacy sub path of the app to the new path in the core app + * path into the new path + * + * Example usage: + * ``` + * kibanaLegacy.forwardApp( + * 'old', + * 'new', + * path => { + * const [, id] = /old/item\/(.*)$/.exec(path) || []; + * if (!id) { + * return '#/home'; + * } + * return '#/items/${id}'; + * } + * ); + * ``` + * This will cause the following redirects: + * + * * app/kibana#/old/ -> app/new#/home + * * app/kibana#/old/item/123 -> app/new#/items/123 + * + */ + forwardApp: ( + legacyAppId: string, + newAppId: string, + rewritePath: (legacyPath: string) => string + ) => { + this.forwardDefinitions.push({ legacyAppId, newAppId, rewritePath }); }, /** @@ -132,7 +177,12 @@ export class KibanaLegacyPlugin { * @deprecated * Just exported for wiring up with legacy platform, should not be used. */ - getForwards: () => this.forwards, + getLegacyAppAliases: () => this.legacyAppAliases, + /** + * @deprecated + * Just exported for wiring up with legacy platform, should not be used. + */ + getForwards: () => this.forwardDefinitions, config: this.initializerContext.config.get(), dashboardConfig: getDashboardConfig(!application.capabilities.dashboard.showWriteControls), }; diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts index 6e4c505c62ebc8..513c70e60048a4 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts @@ -31,6 +31,7 @@ import { setStateToKbnUrl, getStateFromKbnUrl, } from './kbn_url_storage'; +import { ScopedHistory } from '../../../../../core/public'; describe('kbn_url_storage', () => { describe('getStateFromUrl & setStateToUrl', () => { @@ -187,23 +188,54 @@ describe('kbn_url_storage', () => { urlControls.update('/', true); }); - const getCurrentUrl = () => window.location.href; + const getCurrentUrl = () => history.createHref(history.location); it('should flush async url updates', async () => { const pr1 = urlControls.updateAsync(() => '/1', false); const pr2 = urlControls.updateAsync(() => '/2', false); const pr3 = urlControls.updateAsync(() => '/3', false); - expect(getCurrentUrl()).toBe('http://localhost/'); - expect(urlControls.flush()).toBe('http://localhost/3'); - expect(getCurrentUrl()).toBe('http://localhost/3'); + expect(getCurrentUrl()).toBe('/'); + expect(urlControls.flush()).toBe('/3'); + expect(getCurrentUrl()).toBe('/3'); + await Promise.all([pr1, pr2, pr3]); + expect(getCurrentUrl()).toBe('/3'); + }); + + it('flush() should return undefined, if no url updates happened', () => { + expect(urlControls.flush()).toBeUndefined(); + urlControls.updateAsync(() => '/1', false); + urlControls.updateAsync(() => '/', false); + expect(urlControls.flush()).toBeUndefined(); + }); + }); + + describe('urlControls - scoped history integration', () => { + let history: History; + let urlControls: IKbnUrlControls; + beforeEach(() => { + const parentHistory = createBrowserHistory(); + parentHistory.replace('/app/kibana/'); + history = new ScopedHistory(parentHistory, '/app/kibana/'); + urlControls = createKbnUrlControls(history); + }); + + const getCurrentUrl = () => history.createHref(history.location); + + it('should flush async url updates', async () => { + const pr1 = urlControls.updateAsync(() => '/app/kibana/1', false); + const pr2 = urlControls.updateAsync(() => '/app/kibana/2', false); + const pr3 = urlControls.updateAsync(() => '/app/kibana/3', false); + expect(getCurrentUrl()).toBe('/app/kibana/'); + expect(urlControls.flush()).toBe('/app/kibana/3'); + expect(getCurrentUrl()).toBe('/app/kibana/3'); await Promise.all([pr1, pr2, pr3]); - expect(getCurrentUrl()).toBe('http://localhost/3'); + expect(getCurrentUrl()).toBe('/app/kibana/3'); }); it('flush() should return undefined, if no url updates happened', () => { expect(urlControls.flush()).toBeUndefined(); - urlControls.updateAsync(() => 'http://localhost/1', false); - urlControls.updateAsync(() => 'http://localhost/', false); + urlControls.updateAsync(() => '/app/kibana/1', false); + urlControls.updateAsync(() => '/app/kibana/', false); expect(urlControls.flush()).toBeUndefined(); }); }); diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts index 40a411d425a54f..337d122e2854be 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts @@ -154,7 +154,7 @@ export const createKbnUrlControls = ( let shouldReplace = true; function updateUrl(newUrl: string, replace = false): string | undefined { - const currentUrl = getCurrentUrl(); + const currentUrl = getCurrentUrl(history); if (newUrl === currentUrl) return undefined; // skip update const historyPath = getRelativeToHistoryPath(newUrl, history); @@ -165,7 +165,7 @@ export const createKbnUrlControls = ( history.push(historyPath); } - return getCurrentUrl(); + return getCurrentUrl(history); } // queue clean up @@ -187,7 +187,10 @@ export const createKbnUrlControls = ( function getPendingUrl() { if (updateQueue.length === 0) return undefined; - const resultUrl = updateQueue.reduce((url, nextUpdate) => nextUpdate(url), getCurrentUrl()); + const resultUrl = updateQueue.reduce( + (url, nextUpdate) => nextUpdate(url), + getCurrentUrl(history) + ); return resultUrl; } diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts index af8811b1969e69..8adbbfb06e1edf 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts @@ -57,6 +57,7 @@ export function createKbnUrlTracker({ navLinkUpdater$, toastNotifications, history, + getHistory, storage, shouldTrackUrlUpdate = pathname => { const currentAppName = defaultSubUrl.slice(2); // cut hash and slash symbols @@ -103,6 +104,12 @@ export function createKbnUrlTracker({ * History object to use to track url changes. If this isn't provided, a local history instance will be created. */ history?: History; + + /** + * Lazily retrieve history instance + */ + getHistory?: () => History; + /** * Storage object to use to persist currently active url. If this isn't provided, the browser wide session storage instance will be used. */ @@ -158,7 +165,7 @@ export function createKbnUrlTracker({ function onMountApp() { unsubscribe(); - const historyInstance = history || createHashHistory(); + const historyInstance = history || (getHistory && getHistory()) || createHashHistory(); // track current hash when within app unsubscribeURLHistory = historyInstance.listen(location => { if (shouldTrackUrlUpdate(location.pathname)) { diff --git a/src/plugins/kibana_utils/public/state_management/url/parse.ts b/src/plugins/kibana_utils/public/state_management/url/parse.ts index 95041d0662f56d..6339002ea5c68b 100644 --- a/src/plugins/kibana_utils/public/state_management/url/parse.ts +++ b/src/plugins/kibana_utils/public/state_management/url/parse.ts @@ -18,12 +18,11 @@ */ import { parse as _parseUrl } from 'url'; +import { History } from 'history'; export const parseUrl = (url: string) => _parseUrl(url, true); export const parseUrlHash = (url: string) => { const hash = parseUrl(url).hash; return hash ? parseUrl(hash.slice(1)) : null; }; -export const getCurrentUrl = () => window.location.href; -export const parseCurrentUrl = () => parseUrl(getCurrentUrl()); -export const parseCurrentUrlHash = () => parseUrlHash(getCurrentUrl()); +export const getCurrentUrl = (history: History) => history.createHref(history.location); diff --git a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts index cc3f1df7c1e00a..8a9a4ea71ee9ab 100644 --- a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts +++ b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.test.ts @@ -21,6 +21,7 @@ import { createKbnUrlStateStorage, IKbnUrlStateStorage } from './create_kbn_url_ import { History, createBrowserHistory } from 'history'; import { takeUntil, toArray } from 'rxjs/operators'; import { Subject } from 'rxjs'; +import { ScopedHistory } from '../../../../../core/public'; describe('KbnUrlStateStorage', () => { describe('useHash: false', () => { @@ -132,4 +133,78 @@ describe('KbnUrlStateStorage', () => { expect(await result).toEqual([{ test: 'test', ok: 1 }, { test: 'test', ok: 2 }, null]); }); }); + + describe('ScopedHistory integration', () => { + let urlStateStorage: IKbnUrlStateStorage; + let history: ScopedHistory; + const getCurrentUrl = () => history.createHref(history.location); + beforeEach(() => { + const parentHistory = createBrowserHistory(); + parentHistory.push('/kibana/app/'); + history = new ScopedHistory(parentHistory, '/kibana/app/'); + urlStateStorage = createKbnUrlStateStorage({ useHash: false, history }); + }); + + it('should persist state to url', async () => { + const state = { test: 'test', ok: 1 }; + const key = '_s'; + await urlStateStorage.set(key, state); + expect(getCurrentUrl()).toMatchInlineSnapshot(`"/kibana/app/#?_s=(ok:1,test:test)"`); + expect(urlStateStorage.get(key)).toEqual(state); + }); + + it('should flush state to url', () => { + const state = { test: 'test', ok: 1 }; + const key = '_s'; + urlStateStorage.set(key, state); + expect(getCurrentUrl()).toMatchInlineSnapshot(`"/kibana/app/"`); + expect(urlStateStorage.flush()).toBe(true); + expect(getCurrentUrl()).toMatchInlineSnapshot(`"/kibana/app/#?_s=(ok:1,test:test)"`); + expect(urlStateStorage.get(key)).toEqual(state); + + expect(urlStateStorage.flush()).toBe(false); // nothing to flush, not update + }); + + it('should cancel url updates', async () => { + const state = { test: 'test', ok: 1 }; + const key = '_s'; + const pr = urlStateStorage.set(key, state); + expect(getCurrentUrl()).toMatchInlineSnapshot(`"/kibana/app/"`); + urlStateStorage.cancel(); + await pr; + expect(getCurrentUrl()).toMatchInlineSnapshot(`"/kibana/app/"`); + expect(urlStateStorage.get(key)).toEqual(null); + }); + + it('should cancel url updates if synchronously returned to the same state', async () => { + const state1 = { test: 'test', ok: 1 }; + const state2 = { test: 'test', ok: 2 }; + const key = '_s'; + const pr1 = urlStateStorage.set(key, state1); + await pr1; + const historyLength = history.length; + const pr2 = urlStateStorage.set(key, state2); + const pr3 = urlStateStorage.set(key, state1); + await Promise.all([pr2, pr3]); + expect(history.length).toBe(historyLength); + }); + + it('should notify about url changes', async () => { + expect(urlStateStorage.change$).toBeDefined(); + const key = '_s'; + const destroy$ = new Subject(); + const result = urlStateStorage.change$!(key) + .pipe(takeUntil(destroy$), toArray()) + .toPromise(); + + history.push(`/#?${key}=(ok:1,test:test)`); + history.push(`/?query=test#?${key}=(ok:2,test:test)&some=test`); + history.push(`/?query=test#?some=test`); + + destroy$.next(); + destroy$.complete(); + + expect(await result).toEqual([{ test: 'test', ok: 1 }, { test: 'test', ok: 2 }, null]); + }); + }); }); diff --git a/src/plugins/management/public/management_service.test.ts b/src/plugins/management/public/management_service.test.ts index ceb91837921eb5..18569ef285ff3a 100644 --- a/src/plugins/management/public/management_service.test.ts +++ b/src/plugins/management/public/management_service.test.ts @@ -29,9 +29,8 @@ test('Provides default sections', () => { () => {}, coreMock.createSetup().getStartServices ); - expect(service.getAllSections().length).toEqual(3); + expect(service.getAllSections().length).toEqual(2); expect(service.getSection('kibana')).not.toBeUndefined(); - expect(service.getSection('logstash')).not.toBeUndefined(); expect(service.getSection('elasticsearch')).not.toBeUndefined(); }); diff --git a/src/plugins/management/public/management_service.ts b/src/plugins/management/public/management_service.ts index ed31a22992da85..8fc207e32e6ce7 100644 --- a/src/plugins/management/public/management_service.ts +++ b/src/plugins/management/public/management_service.ts @@ -80,7 +80,6 @@ export class ManagementService { ); register({ id: 'kibana', title: 'Kibana', order: 30, euiIconType: 'logoKibana' }); - register({ id: 'logstash', title: 'Logstash', order: 30, euiIconType: 'logoLogstash' }); register({ id: 'elasticsearch', title: 'Elasticsearch', diff --git a/src/plugins/vis_default_editor/public/components/agg_params_map.ts b/src/plugins/vis_default_editor/public/components/agg_params_map.ts index 5af3cfc5b09289..9bc3146b9903bb 100644 --- a/src/plugins/vis_default_editor/public/components/agg_params_map.ts +++ b/src/plugins/vis_default_editor/public/components/agg_params_map.ts @@ -58,6 +58,8 @@ const buckets = { size: controls.SizeParamEditor, }, [BUCKET_TYPES.TERMS]: { + include: controls.IncludeExcludeParamEditor, + exclude: controls.IncludeExcludeParamEditor, orderBy: controls.OrderByParamEditor, orderAgg: controls.OrderAggParamEditor, order: wrapWithInlineComp(controls.OrderParamEditor), diff --git a/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts b/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts index dac86249ebbb99..5cb594ade8dba7 100644 --- a/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts +++ b/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts @@ -119,7 +119,7 @@ function getNextModel(list: NumberRowModel[], range: NumberListRange): NumberRow }; } -function getInitModelList(list: Array): NumberRowModel[] { +function getInitModelList(list: Array): NumberRowModel[] { return list.length ? list.map(num => ({ value: (num === undefined ? EMPTY_STRING : num) as NumberRowModel['value'], diff --git a/src/plugins/vis_default_editor/public/components/controls/components/simple_number_list.tsx b/src/plugins/vis_default_editor/public/components/controls/components/simple_number_list.tsx new file mode 100644 index 00000000000000..becf8e47ef5736 --- /dev/null +++ b/src/plugins/vis_default_editor/public/components/controls/components/simple_number_list.tsx @@ -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 React, { Fragment, useCallback, useEffect, useMemo, useState } from 'react'; +import { isArray } from 'lodash'; +import { EuiButtonEmpty, EuiFlexItem, EuiFormRow, EuiSpacer, htmlIdGenerator } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EMPTY_STRING, getInitModelList, getRange, parse } from './number_list/utils'; +import { NumberRow, NumberRowModel } from './number_list/number_row'; +import { AggParamEditorProps } from '../../agg_param_props'; + +const generateId = htmlIdGenerator(); + +function SimpleNumberList({ + agg, + aggParam, + value, + setValue, + setTouched, +}: AggParamEditorProps>) { + const [numbers, setNumbers] = useState( + getInitModelList(value && isArray(value) ? value : [EMPTY_STRING]) + ); + const numberRange = useMemo(() => getRange('[-Infinity,Infinity]'), []); + + // This useEffect is needed to discard changes, it sets numbers a mapped value if they are different + useEffect(() => { + if ( + isArray(value) && + (value.length !== numbers.length || + !value.every((numberValue, index) => numberValue === numbers[index].value)) + ) { + setNumbers( + value.map(numberValue => ({ + id: generateId(), + value: numberValue, + isInvalid: false, + })) + ); + } + }, [numbers, value]); + + const onUpdate = useCallback( + (numberList: NumberRowModel[]) => { + setNumbers(numberList); + setValue(numberList.map(({ value: numberValue }) => numberValue)); + }, + [setValue] + ); + + const onChangeValue = useCallback( + (numberField: { id: string; value: string }) => { + onUpdate( + numbers.map(number => + number.id === numberField.id + ? { + id: numberField.id, + value: parse(numberField.value), + isInvalid: false, + } + : number + ) + ); + }, + [numbers, onUpdate] + ); + + // Add an item to the end of the list + const onAdd = useCallback(() => { + const newArray = [ + ...numbers, + { + id: generateId(), + value: EMPTY_STRING as '', + isInvalid: false, + }, + ]; + onUpdate(newArray); + }, [numbers, onUpdate]); + + const onDelete = useCallback( + (id: string) => onUpdate(numbers.filter(number => number.id !== id)), + [numbers, onUpdate] + ); + + return ( + + <> + {numbers.map((number, arrayIndex) => ( + + + {numbers.length - 1 !== arrayIndex && } + + ))} + + + + + + + + + ); +} + +export { SimpleNumberList }; diff --git a/src/plugins/vis_default_editor/public/components/controls/include_exclude.tsx b/src/plugins/vis_default_editor/public/components/controls/include_exclude.tsx new file mode 100644 index 00000000000000..f60f6ce7ce249b --- /dev/null +++ b/src/plugins/vis_default_editor/public/components/controls/include_exclude.tsx @@ -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 React, { useEffect } from 'react'; +import { AggParamEditorProps } from '../agg_param_props'; +import { StringParamEditor } from './string'; +import { search } from '../../../../data/public'; +import { SimpleNumberList } from './components/simple_number_list'; +const { isNumberType } = search.aggs; + +export function IncludeExcludeParamEditor(props: AggParamEditorProps>) { + const { agg, value, setValue } = props; + const isAggOfNumberType = isNumberType(agg); + + // This useEffect converts value from string type to number and back when the field type is changed + useEffect(() => { + if (isAggOfNumberType && !Array.isArray(value) && value !== undefined) { + const numberArray = value + .split('|') + .map(item => parseFloat(item)) + .filter(number => Number.isFinite(number)); + setValue(numberArray.length ? numberArray : ['']); + } else if (!isAggOfNumberType && Array.isArray(value) && value !== undefined) { + setValue(value.filter(item => item !== '').join('|')); + } + }, [isAggOfNumberType, setValue, value]); + + return isAggOfNumberType ? ( + } /> + ) : ( + + ); +} diff --git a/src/plugins/vis_default_editor/public/components/controls/index.ts b/src/plugins/vis_default_editor/public/components/controls/index.ts index e8944aa667853f..cfb236e5e22e37 100644 --- a/src/plugins/vis_default_editor/public/components/controls/index.ts +++ b/src/plugins/vis_default_editor/public/components/controls/index.ts @@ -24,6 +24,7 @@ export { ExtendedBoundsParamEditor } from './extended_bounds'; export { FieldParamEditor } from './field'; export { FiltersParamEditor } from './filters'; export { HasExtendedBoundsParamEditor } from './has_extended_bounds'; +export { IncludeExcludeParamEditor } from './include_exclude'; export { IpRangesParamEditor } from './ip_ranges'; export { IpRangeTypeParamEditor } from './ip_range_type'; export { IsFilteredByCollarParamEditor } from './is_filtered_by_collar'; diff --git a/src/plugins/vis_type_table/config.ts b/src/plugins/vis_type_table/config.ts new file mode 100644 index 00000000000000..6749bd83de39f9 --- /dev/null +++ b/src/plugins/vis_type_table/config.ts @@ -0,0 +1,26 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; + +export const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), +}); + +export type ConfigSchema = TypeOf; diff --git a/src/plugins/vis_type_table/kibana.json b/src/plugins/vis_type_table/kibana.json new file mode 100644 index 00000000000000..bb0f6478a42408 --- /dev/null +++ b/src/plugins/vis_type_table/kibana.json @@ -0,0 +1,11 @@ +{ + "id": "visTypeTable", + "version": "kibana", + "server": true, + "ui": true, + "requiredPlugins": [ + "expressions", + "visualizations", + "data" + ] +} diff --git a/src/legacy/core_plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap b/src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap rename to src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap diff --git a/src/legacy/core_plugins/vis_type_table/public/_table_vis.scss b/src/plugins/vis_type_table/public/_table_vis.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/_table_vis.scss rename to src/plugins/vis_type_table/public/_table_vis.scss diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/_agg_table.scss b/src/plugins/vis_type_table/public/agg_table/_agg_table.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/agg_table/_agg_table.scss rename to src/plugins/vis_type_table/public/agg_table/_agg_table.scss diff --git a/src/plugins/vis_type_table/public/agg_table/_index.scss b/src/plugins/vis_type_table/public/agg_table/_index.scss new file mode 100644 index 00000000000000..340e08a76f1bdb --- /dev/null +++ b/src/plugins/vis_type_table/public/agg_table/_index.scss @@ -0,0 +1 @@ +@import './agg_table'; diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table.html b/src/plugins/vis_type_table/public/agg_table/agg_table.html similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table.html rename to src/plugins/vis_type_table/public/agg_table/agg_table.html diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table.js b/src/plugins/vis_type_table/public/agg_table/agg_table.js similarity index 98% rename from src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table.js rename to src/plugins/vis_type_table/public/agg_table/agg_table.js index b9e79f96e4fc13..0cd501e2d0344c 100644 --- a/src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table.js +++ b/src/plugins/vis_type_table/public/agg_table/agg_table.js @@ -238,9 +238,9 @@ export function KbnAggTable(config, RecursionHelper) { } /** - * @param {[]Object} columns - the formatted columns that will be displayed + * @param {Object[]} columns - the formatted columns that will be displayed * @param {String} title - the title of the column to add to - * @param {[]Object} rows - the row data for the columns + * @param {Object[]} rows - the row data for the columns * @param {Number} insertAtIndex - the index to insert the percentage column at * @returns {Object} - cols and rows for the table to render now included percentage column(s) */ diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table_group.html b/src/plugins/vis_type_table/public/agg_table/agg_table_group.html similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table_group.html rename to src/plugins/vis_type_table/public/agg_table/agg_table_group.html diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table_group.js b/src/plugins/vis_type_table/public/agg_table/agg_table_group.js similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table_group.js rename to src/plugins/vis_type_table/public/agg_table/agg_table_group.js diff --git a/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx b/src/plugins/vis_type_table/public/components/table_vis_options.tsx similarity index 96% rename from src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx rename to src/plugins/vis_type_table/public/components/table_vis_options.tsx index 265528f33f9cd0..68348d5ef10604 100644 --- a/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_options.tsx @@ -24,12 +24,8 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; -import { search } from '../../../../../plugins/data/public'; -import { - SwitchOption, - SelectOption, - NumberInputOption, -} from '../../../../../plugins/charts/public'; +import { search } from '../../../data/public'; +import { SwitchOption, SelectOption, NumberInputOption } from '../../../charts/public'; import { TableVisParams } from '../types'; import { totalAggregations } from './utils'; diff --git a/src/legacy/core_plugins/vis_type_table/public/components/utils.ts b/src/plugins/vis_type_table/public/components/utils.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/components/utils.ts rename to src/plugins/vis_type_table/public/components/utils.ts diff --git a/src/legacy/core_plugins/vis_type_table/public/get_inner_angular.ts b/src/plugins/vis_type_table/public/get_inner_angular.ts similarity index 91% rename from src/legacy/core_plugins/vis_type_table/public/get_inner_angular.ts rename to src/plugins/vis_type_table/public/get_inner_angular.ts index 6208e358b4184f..d69b9bba31b032 100644 --- a/src/legacy/core_plugins/vis_type_table/public/get_inner_angular.ts +++ b/src/plugins/vis_type_table/public/get_inner_angular.ts @@ -23,7 +23,7 @@ import angular from 'angular'; import 'angular-recursion'; import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; -import { CoreStart, LegacyCoreStart, IUiSettingsClient } from 'kibana/public'; +import { CoreStart, IUiSettingsClient, PluginInitializerContext } from 'kibana/public'; import { initAngularBootstrap, PaginateDirectiveProvider, @@ -32,15 +32,15 @@ import { watchMultiDecorator, KbnAccessibleClickProvider, configureAppAngularModule, -} from '../../../../plugins/kibana_legacy/public'; +} from '../../kibana_legacy/public'; initAngularBootstrap(); const thirdPartyAngularDependencies = ['ngSanitize', 'ui.bootstrap', 'RecursionHelper']; -export function getAngularModule(name: string, core: CoreStart) { +export function getAngularModule(name: string, core: CoreStart, context: PluginInitializerContext) { const uiModule = getInnerAngular(name, core); - configureAppAngularModule(uiModule, core as LegacyCoreStart, true); + configureAppAngularModule(uiModule, { core, env: context.env }, true); return uiModule; } diff --git a/src/legacy/core_plugins/vis_type_table/public/index.scss b/src/plugins/vis_type_table/public/index.scss similarity index 61% rename from src/legacy/core_plugins/vis_type_table/public/index.scss rename to src/plugins/vis_type_table/public/index.scss index 54124ebc42620b..0972c85e0dbe03 100644 --- a/src/legacy/core_plugins/vis_type_table/public/index.scss +++ b/src/plugins/vis_type_table/public/index.scss @@ -1,5 +1,3 @@ -@import 'src/legacy/ui/public/styles/styling_constants'; - // Prefix all styles with "tbv" to avoid conflicts. // Examples // tbvChart @@ -7,6 +5,6 @@ // tbvChart__legend--small // tbvChart__legend-isLoading -@import 'agg_table/index'; -@import 'paginated_table/index'; +@import './agg_table/index'; +@import './paginated_table/index'; @import './table_vis'; diff --git a/src/legacy/core_plugins/vis_type_table/public/index.ts b/src/plugins/vis_type_table/public/index.ts similarity index 92% rename from src/legacy/core_plugins/vis_type_table/public/index.ts rename to src/plugins/vis_type_table/public/index.ts index efbaf69659ea29..5621fdb0947724 100644 --- a/src/legacy/core_plugins/vis_type_table/public/index.ts +++ b/src/plugins/vis_type_table/public/index.ts @@ -16,8 +16,8 @@ * specific language governing permissions and limitations * under the License. */ - -import { PluginInitializerContext } from '../../../../core/public'; +import './index.scss'; +import { PluginInitializerContext } from 'kibana/public'; import { TableVisPlugin as Plugin } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/src/plugins/vis_type_table/public/paginated_table/_index.scss b/src/plugins/vis_type_table/public/paginated_table/_index.scss new file mode 100644 index 00000000000000..23d56c09b2818a --- /dev/null +++ b/src/plugins/vis_type_table/public/paginated_table/_index.scss @@ -0,0 +1 @@ +@import './_table_cell_filter'; diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss b/src/plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss rename to src/plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.html b/src/plugins/vis_type_table/public/paginated_table/paginated_table.html similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.html rename to src/plugins/vis_type_table/public/paginated_table/paginated_table.html diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.js b/src/plugins/vis_type_table/public/paginated_table/paginated_table.js similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.js rename to src/plugins/vis_type_table/public/paginated_table/paginated_table.js diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.test.ts b/src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts similarity index 98% rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.test.ts rename to src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts index 7352236f03febf..23e4aee0378dc6 100644 --- a/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.test.ts +++ b/src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts @@ -25,10 +25,9 @@ import 'angular-mocks'; import { getAngularModule } from '../get_inner_angular'; import { initTableVisLegacyModule } from '../table_vis_legacy_module'; -import { coreMock } from '../../../../../core/public/mocks'; +import { coreMock } from '../../../../core/public/mocks'; -jest.mock('ui/new_platform'); -jest.mock('../../../../../plugins/kibana_legacy/public/angular/angular_config', () => ({ +jest.mock('../../../kibana_legacy/public/angular/angular_config', () => ({ configureAppAngularModule: () => {}, })); @@ -73,7 +72,11 @@ describe('Table Vis - Paginated table', () => { let paginatedTable: any; const initLocalAngular = () => { - const tableVisModule = getAngularModule('kibana/table_vis', coreMock.createStart()); + const tableVisModule = getAngularModule( + 'kibana/table_vis', + coreMock.createStart(), + coreMock.createPluginInitializerContext() + ); initTableVisLegacyModule(tableVisModule); }; diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/rows.js b/src/plugins/vis_type_table/public/paginated_table/rows.js similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/rows.js rename to src/plugins/vis_type_table/public/paginated_table/rows.js diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/table_cell_filter.html b/src/plugins/vis_type_table/public/paginated_table/table_cell_filter.html similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/table_cell_filter.html rename to src/plugins/vis_type_table/public/paginated_table/table_cell_filter.html diff --git a/src/legacy/core_plugins/vis_type_table/public/plugin.ts b/src/plugins/vis_type_table/public/plugin.ts similarity index 80% rename from src/legacy/core_plugins/vis_type_table/public/plugin.ts rename to src/plugins/vis_type_table/public/plugin.ts index ea12a5320a14d4..a41d939523bcca 100644 --- a/src/legacy/core_plugins/vis_type_table/public/plugin.ts +++ b/src/plugins/vis_type_table/public/plugin.ts @@ -16,14 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public'; -import { VisualizationsSetup } from '../../../../plugins/visualizations/public'; - -import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public'; +import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public'; +import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public'; +import { VisualizationsSetup } from '../../visualizations/public'; import { createTableVisFn } from './table_vis_fn'; -import { tableVisTypeDefinition } from './table_vis_type'; -import { DataPublicPluginStart } from '../../../../plugins/data/public'; +import { getTableVisTypeDefinition } from './table_vis_type'; +import { DataPublicPluginStart } from '../../data/public'; import { setFormatService } from './services'; /** @internal */ @@ -40,6 +39,7 @@ export interface TablePluginStartDependencies { /** @internal */ export class TableVisPlugin implements Plugin, void> { initializerContext: PluginInitializerContext; + createBaseVisualization: any; constructor(initializerContext: PluginInitializerContext) { this.initializerContext = initializerContext; @@ -50,8 +50,9 @@ export class TableVisPlugin implements Plugin, void> { { expressions, visualizations }: TablePluginSetupDependencies ) { expressions.registerFunction(createTableVisFn); - - visualizations.createBaseVisualization(tableVisTypeDefinition); + visualizations.createBaseVisualization( + getTableVisTypeDefinition(core, this.initializerContext) + ); } public start(core: CoreStart, { data }: TablePluginStartDependencies) { diff --git a/src/legacy/core_plugins/vis_type_table/public/services.ts b/src/plugins/vis_type_table/public/services.ts similarity index 86% rename from src/legacy/core_plugins/vis_type_table/public/services.ts rename to src/plugins/vis_type_table/public/services.ts index b4b491ac7a5552..3aaffe75e27f14 100644 --- a/src/legacy/core_plugins/vis_type_table/public/services.ts +++ b/src/plugins/vis_type_table/public/services.ts @@ -17,8 +17,8 @@ * under the License. */ -import { createGetterSetter } from '../../../../plugins/kibana_utils/public'; -import { DataPublicPluginStart } from '../../../../plugins/data/public'; +import { createGetterSetter } from '../../kibana_utils/public'; +import { DataPublicPluginStart } from '../../data/public'; export const [getFormatService, setFormatService] = createGetterSetter< DataPublicPluginStart['fieldFormats'] diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis.html b/src/plugins/vis_type_table/public/table_vis.html similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/table_vis.html rename to src/plugins/vis_type_table/public/table_vis.html diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.js b/src/plugins/vis_type_table/public/table_vis_controller.js similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/table_vis_controller.js rename to src/plugins/vis_type_table/public/table_vis_controller.js diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts b/src/plugins/vis_type_table/public/table_vis_controller.test.ts similarity index 89% rename from src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts rename to src/plugins/vis_type_table/public/table_vis_controller.test.ts index 8d6f88bf8dd4af..4607324ca150c6 100644 --- a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts +++ b/src/plugins/vis_type_table/public/table_vis_controller.test.ts @@ -26,24 +26,23 @@ import $ from 'jquery'; import StubIndexPattern from 'test_utils/stub_index_pattern'; import { getAngularModule } from './get_inner_angular'; import { initTableVisLegacyModule } from './table_vis_legacy_module'; -import { tableVisTypeDefinition } from './table_vis_type'; -import { Vis } from '../../../../plugins/visualizations/public'; +import { getTableVisTypeDefinition } from './table_vis_type'; +import { Vis } from '../../visualizations/public'; // eslint-disable-next-line -import { stubFields } from '../../../../plugins/data/public/stubs'; +import { stubFields } from '../../data/public/stubs'; // eslint-disable-next-line import { tableVisResponseHandler } from './table_vis_response_handler'; -import { coreMock } from '../../../../core/public/mocks'; +import { coreMock } from '../../../core/public/mocks'; +import { IAggConfig, search } from '../../data/public'; +// TODO: remove linting disable // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { npStart } from './legacy_imports'; -import { IAggConfig, search } from '../../../../plugins/data/public'; +import { searchStartMock } from '../../data/public/search/mocks'; -// should be mocked once get rid of 'ui/new_platform' legacy imports -const { createAggConfigs } = npStart.plugins.data.search.aggs; +const { createAggConfigs } = searchStartMock.aggs; const { tabifyAggResponse } = search; -jest.mock('ui/new_platform'); -jest.mock('../../../../plugins/kibana_legacy/public/angular/angular_config', () => ({ +jest.mock('../../kibana_legacy/public/angular/angular_config', () => ({ configureAppAngularModule: () => {}, })); @@ -89,7 +88,11 @@ describe('Table Vis - Controller', () => { let stubIndexPattern: any; const initLocalAngular = () => { - const tableVisModule = getAngularModule('kibana/table_vis', coreMock.createStart()); + const tableVisModule = getAngularModule( + 'kibana/table_vis', + coreMock.createStart(), + coreMock.createPluginInitializerContext() + ); initTableVisLegacyModule(tableVisModule); }; @@ -110,9 +113,13 @@ describe('Table Vis - Controller', () => { (cfg: any) => cfg, 'time', stubFields, - coreMock.createStart() + coreMock.createSetup() ); }); + const tableVisTypeDefinition = getTableVisTypeDefinition( + coreMock.createSetup(), + coreMock.createPluginInitializerContext() + ); function getRangeVis(params?: object) { return ({ diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.test.ts b/src/plugins/vis_type_table/public/table_vis_fn.test.ts similarity index 95% rename from src/legacy/core_plugins/vis_type_table/public/table_vis_fn.test.ts rename to src/plugins/vis_type_table/public/table_vis_fn.test.ts index 36392c10f93f32..9accf8950d910b 100644 --- a/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.test.ts +++ b/src/plugins/vis_type_table/public/table_vis_fn.test.ts @@ -21,7 +21,7 @@ import { createTableVisFn } from './table_vis_fn'; import { tableVisResponseHandler } from './table_vis_response_handler'; // eslint-disable-next-line -import { functionWrapper } from '../../../../plugins/expressions/common/expression_functions/specs/tests/utils'; +import { functionWrapper } from '../../expressions/common/expression_functions/specs/tests/utils'; jest.mock('./table_vis_response_handler', () => ({ tableVisResponseHandler: jest.fn().mockReturnValue({ diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.ts b/src/plugins/vis_type_table/public/table_vis_fn.ts similarity index 94% rename from src/legacy/core_plugins/vis_type_table/public/table_vis_fn.ts rename to src/plugins/vis_type_table/public/table_vis_fn.ts index a97e596e89754c..9739a7a284e6cf 100644 --- a/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.ts +++ b/src/plugins/vis_type_table/public/table_vis_fn.ts @@ -19,11 +19,7 @@ import { i18n } from '@kbn/i18n'; import { tableVisResponseHandler, TableContext } from './table_vis_response_handler'; -import { - ExpressionFunctionDefinition, - KibanaDatatable, - Render, -} from '../../../../plugins/expressions/public'; +import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public'; export type Input = KibanaDatatable; diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_legacy_module.ts b/src/plugins/vis_type_table/public/table_vis_legacy_module.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/table_vis_legacy_module.ts rename to src/plugins/vis_type_table/public/table_vis_legacy_module.ts diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_response_handler.ts b/src/plugins/vis_type_table/public/table_vis_response_handler.ts similarity index 100% rename from src/legacy/core_plugins/vis_type_table/public/table_vis_response_handler.ts rename to src/plugins/vis_type_table/public/table_vis_response_handler.ts diff --git a/src/plugins/vis_type_table/public/table_vis_type.ts b/src/plugins/vis_type_table/public/table_vis_type.ts new file mode 100644 index 00000000000000..26e5ac8cfd71ab --- /dev/null +++ b/src/plugins/vis_type_table/public/table_vis_type.ts @@ -0,0 +1,100 @@ +/* + * 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, PluginInitializerContext } from 'kibana/public'; +import { i18n } from '@kbn/i18n'; +import { AggGroupNames } from '../../data/public'; +import { Schemas } from '../../vis_default_editor/public'; +import { Vis } from '../../visualizations/public'; +import { tableVisResponseHandler } from './table_vis_response_handler'; +// @ts-ignore +import tableVisTemplate from './table_vis.html'; +import { TableOptions } from './components/table_vis_options'; +import { getTableVisualizationControllerClass } from './vis_controller'; + +export function getTableVisTypeDefinition(core: CoreSetup, context: PluginInitializerContext) { + return { + type: 'table', + name: 'table', + title: i18n.translate('visTypeTable.tableVisTitle', { + defaultMessage: 'Data Table', + }), + icon: 'visTable', + description: i18n.translate('visTypeTable.tableVisDescription', { + defaultMessage: 'Display values in a table', + }), + visualization: getTableVisualizationControllerClass(core, context), + visConfig: { + defaults: { + perPage: 10, + showPartialRows: false, + showMetricsAtAllLevels: false, + sort: { + columnIndex: null, + direction: null, + }, + showTotal: false, + totalFunc: 'sum', + percentageCol: '', + }, + template: tableVisTemplate, + }, + editorConfig: { + optionsTemplate: TableOptions, + schemas: new Schemas([ + { + group: AggGroupNames.Metrics, + name: 'metric', + title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.metricTitle', { + defaultMessage: 'Metric', + }), + aggFilter: ['!geo_centroid', '!geo_bounds'], + aggSettings: { + top_hits: { + allowStrings: true, + }, + }, + min: 1, + defaults: [{ type: 'count', schema: 'metric' }], + }, + { + group: AggGroupNames.Buckets, + name: 'bucket', + title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.bucketTitle', { + defaultMessage: 'Split rows', + }), + aggFilter: ['!filter'], + }, + { + group: AggGroupNames.Buckets, + name: 'split', + title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.splitTitle', { + defaultMessage: 'Split table', + }), + min: 0, + max: 1, + aggFilter: ['!filter'], + }, + ]), + }, + responseHandler: tableVisResponseHandler, + hierarchicalData: (vis: Vis) => { + return Boolean(vis.params.showPartialRows || vis.params.showMetricsAtAllLevels); + }, + }; +} diff --git a/src/legacy/core_plugins/vis_type_table/public/types.ts b/src/plugins/vis_type_table/public/types.ts similarity index 94% rename from src/legacy/core_plugins/vis_type_table/public/types.ts rename to src/plugins/vis_type_table/public/types.ts index c6de14b9f050c6..39023d1305cb63 100644 --- a/src/legacy/core_plugins/vis_type_table/public/types.ts +++ b/src/plugins/vis_type_table/public/types.ts @@ -17,7 +17,7 @@ * under the License. */ -import { SchemaConfig } from '../../../../plugins/visualizations/public'; +import { SchemaConfig } from '../../visualizations/public'; export enum AggTypes { SUM = 'sum', diff --git a/src/plugins/vis_type_table/public/vis_controller.ts b/src/plugins/vis_type_table/public/vis_controller.ts new file mode 100644 index 00000000000000..d49dd32c8c89c3 --- /dev/null +++ b/src/plugins/vis_type_table/public/vis_controller.ts @@ -0,0 +1,109 @@ +/* + * 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, PluginInitializerContext } from 'kibana/public'; +import angular, { IModule, auto, IRootScopeService, IScope, ICompileService } from 'angular'; +import $ from 'jquery'; + +import { VisParams, ExprVis } from '../../visualizations/public'; +import { getAngularModule } from './get_inner_angular'; +import { initTableVisLegacyModule } from './table_vis_legacy_module'; + +const innerAngularName = 'kibana/table_vis'; + +export function getTableVisualizationControllerClass( + core: CoreSetup, + context: PluginInitializerContext +) { + return class TableVisualizationController { + private tableVisModule: IModule | undefined; + private injector: auto.IInjectorService | undefined; + el: JQuery; + vis: ExprVis; + $rootScope: IRootScopeService | null = null; + $scope: (IScope & { [key: string]: any }) | undefined; + $compile: ICompileService | undefined; + + constructor(domeElement: Element, vis: ExprVis) { + this.el = $(domeElement); + this.vis = vis; + } + + getInjector() { + if (!this.injector) { + const mountpoint = document.createElement('div'); + mountpoint.setAttribute('style', 'height: 100%; width: 100%;'); + this.injector = angular.bootstrap(mountpoint, [innerAngularName]); + this.el.append(mountpoint); + } + + return this.injector; + } + + async initLocalAngular() { + if (!this.tableVisModule) { + const [coreStart] = await core.getStartServices(); + this.tableVisModule = getAngularModule(innerAngularName, coreStart, context); + initTableVisLegacyModule(this.tableVisModule); + } + } + + async render(esResponse: object, visParams: VisParams) { + await this.initLocalAngular(); + + return new Promise(async (resolve, reject) => { + if (!this.$rootScope) { + const $injector = this.getInjector(); + this.$rootScope = $injector.get('$rootScope'); + this.$compile = $injector.get('$compile'); + } + const updateScope = () => { + if (!this.$scope) { + return; + } + this.$scope.vis = this.vis; + this.$scope.visState = { params: visParams }; + this.$scope.esResponse = esResponse; + + this.$scope.visParams = visParams; + this.$scope.renderComplete = resolve; + this.$scope.renderFailed = reject; + this.$scope.resize = Date.now(); + this.$scope.$apply(); + }; + + if (!this.$scope && this.$compile) { + this.$scope = this.$rootScope.$new(); + this.$scope.uiState = this.vis.getUiState(); + updateScope(); + this.el.find('div').append(this.$compile(this.vis.type!.visConfig.template)(this.$scope)); + this.$scope.$apply(); + } else { + updateScope(); + } + }); + } + + destroy() { + if (this.$rootScope) { + this.$rootScope.$destroy(); + this.$rootScope = null; + } + } + }; +} diff --git a/src/plugins/vis_type_table/server/index.ts b/src/plugins/vis_type_table/server/index.ts new file mode 100644 index 00000000000000..882958a28777d1 --- /dev/null +++ b/src/plugins/vis_type_table/server/index.ts @@ -0,0 +1,34 @@ +/* + * 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 { PluginConfigDescriptor } from 'kibana/server'; + +import { configSchema, ConfigSchema } from '../config'; + +export const config: PluginConfigDescriptor = { + schema: configSchema, + deprecations: ({ renameFromRoot }) => [ + renameFromRoot('table_vis.enabled', 'vis_type_table.enabled'), + ], +}; + +export const plugin = () => ({ + setup() {}, + start() {}, +}); diff --git a/src/plugins/vis_type_timeseries/kibana.json b/src/plugins/vis_type_timeseries/kibana.json index 38662c6a7ff89b..9053d2543e0d0e 100644 --- a/src/plugins/vis_type_timeseries/kibana.json +++ b/src/plugins/vis_type_timeseries/kibana.json @@ -4,6 +4,6 @@ "kibanaVersion": "kibana", "server": true, "ui": true, - "requiredPlugins": ["data", "expressions", "visualizations"], + "requiredPlugins": ["charts", "data", "expressions", "visualizations"], "optionalPlugins": ["usageCollection"] } diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js index 024f59c3abb1c3..d29b795b10ec83 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js +++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js @@ -53,7 +53,7 @@ export const TimeseriesConfig = injectI18n(function(props) { point_size: '', value_template: '{{value}}', offset_time: '', - split_color_mode: 'gradient', + split_color_mode: 'kibana', axis_min: '', axis_max: '', stacked: STACKED_OPTIONS.NONE, @@ -140,10 +140,10 @@ export const TimeseriesConfig = injectI18n(function(props) { const splitColorOptions = [ { label: intl.formatMessage({ - id: 'visTypeTimeseries.timeSeries.gradientLabel', - defaultMessage: 'Gradient', + id: 'visTypeTimeseries.timeSeries.defaultPaletteLabel', + defaultMessage: 'Default palette', }), - value: 'gradient', + value: 'kibana', }, { label: intl.formatMessage({ @@ -152,6 +152,13 @@ export const TimeseriesConfig = injectI18n(function(props) { }), value: 'rainbow', }, + { + label: intl.formatMessage({ + id: 'visTypeTimeseries.timeSeries.gradientLabel', + defaultMessage: 'Gradient', + }), + value: 'gradient', + }, ]; const selectedSplitColorOption = splitColorOptions.find(option => { return model.split_color_mode === option.value; diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js index b1c3c7ac6b67ad..5cf1619150e5c7 100644 --- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js @@ -33,7 +33,7 @@ import { import { EuiIcon } from '@elastic/eui'; import { getTimezone } from '../../../lib/get_timezone'; import { eventBus, ACTIVE_CURSOR } from '../../lib/active_cursor'; -import { getUISettings } from '../../../../services'; +import { getUISettings, getChartsSetup } from '../../../../services'; import { GRID_LINE_CONFIG, ICON_TYPES_MAP, STACKED_OPTIONS } from '../../constants'; import { AreaSeriesDecorator } from './decorators/area_decorator'; import { BarSeriesDecorator } from './decorators/bar_decorator'; @@ -94,6 +94,12 @@ export const TimeSeries = ({ // apply legend style change if bgColor is configured const classes = classNames('tvbVisTimeSeries', getChartClasses(backgroundColor)); + // If the color isn't configured by the user, use the color mapping service + // to assign a color from the Kibana palette. Colors will be shared across the + // session, including dashboards. + const { colors } = getChartsSetup(); + colors.mappedColors.mapKeys(series.filter(({ color }) => !color).map(({ label }) => label)); + return ( ; visualizations: VisualizationsSetup; + charts: ChartsPluginSetup; } /** @internal */ @@ -56,10 +59,11 @@ export class MetricsPlugin implements Plugin, void> { public async setup( core: CoreSetup, - { expressions, visualizations }: MetricsPluginSetupDependencies + { expressions, visualizations, charts }: MetricsPluginSetupDependencies ) { expressions.registerFunction(createMetricsFn); setUISettings(core.uiSettings); + setChartsSetup(charts); visualizations.createReactVisualization(metricsVisDefinition); } diff --git a/src/plugins/vis_type_timeseries/public/services.ts b/src/plugins/vis_type_timeseries/public/services.ts index d93a376584eac9..9aa84478fb78bf 100644 --- a/src/plugins/vis_type_timeseries/public/services.ts +++ b/src/plugins/vis_type_timeseries/public/services.ts @@ -19,6 +19,7 @@ import { I18nStart, SavedObjectsStart, IUiSettingsClient, CoreStart } from 'src/core/public'; import { createGetterSetter } from '../../kibana_utils/public'; +import { ChartsPluginSetup } from '../../charts/public'; import { DataPublicPluginStart } from '../../data/public'; export const [getUISettings, setUISettings] = createGetterSetter('UISettings'); @@ -36,3 +37,7 @@ export const [getCoreStart, setCoreStart] = createGetterSetter('CoreS export const [getDataStart, setDataStart] = createGetterSetter('DataStart'); export const [getI18n, setI18n] = createGetterSetter('I18n'); + +export const [getChartsSetup, setChartsSetup] = createGetterSetter( + 'ChartsPluginSetup' +); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_split_colors.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_split_colors.js index ff8d9077b08711..cad8c8f2025a17 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_split_colors.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_split_colors.js @@ -19,7 +19,7 @@ import Color from 'color'; -export function getSplitColors(inputColor, size = 10, style = 'gradient') { +export function getSplitColors(inputColor, size = 10, style = 'kibana') { const color = new Color(inputColor); const colors = []; let workingColor = Color.hsl(color.hsl().object()); @@ -49,7 +49,7 @@ export function getSplitColors(inputColor, size = 10, style = 'gradient') { '#0F1419', '#666666', ]; - } else { + } else if (style === 'gradient') { colors.push(color.string()); const rotateBy = color.luminosity() / (size - 1); for (let i = 0; i < size - 1; i++) { diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js index 0874d944033f5c..376d32d0da13f6 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js @@ -106,7 +106,7 @@ describe('getSplits(resp, panel, series)', () => { ]); }); - test('should return a splits for terms group bys', () => { + describe('terms group bys', () => { const resp = { aggregations: { SERIES: { @@ -126,38 +126,89 @@ describe('getSplits(resp, panel, series)', () => { }, }, }; - const series = { - id: 'SERIES', - color: '#F00', - split_mode: 'terms', - terms_field: 'beat.hostname', - terms_size: 10, - metrics: [ - { id: 'AVG', type: 'avg', field: 'cpu' }, - { id: 'SIBAGG', type: 'avg_bucket', field: 'AVG' }, - ], - }; - const panel = { type: 'timeseries' }; - expect(getSplits(resp, panel, series)).toEqual([ - { - id: 'SERIES:example-01', - key: 'example-01', - label: 'example-01', - meta: { bucketSize: 10 }, - color: 'rgb(255, 0, 0)', - timeseries: { buckets: [] }, - SIBAGG: { value: 1 }, - }, - { - id: 'SERIES:example-02', - key: 'example-02', - label: 'example-02', - meta: { bucketSize: 10 }, - color: 'rgb(147, 0, 0)', - timeseries: { buckets: [] }, - SIBAGG: { value: 2 }, - }, - ]); + + test('should return a splits with no color', () => { + const series = { + id: 'SERIES', + color: '#F00', + split_mode: 'terms', + terms_field: 'beat.hostname', + terms_size: 10, + metrics: [ + { id: 'AVG', type: 'avg', field: 'cpu' }, + { id: 'SIBAGG', type: 'avg_bucket', field: 'AVG' }, + ], + }; + const panel = { type: 'timeseries' }; + expect(getSplits(resp, panel, series)).toEqual([ + { + id: 'SERIES:example-01', + key: 'example-01', + label: 'example-01', + meta: { bucketSize: 10 }, + color: undefined, + timeseries: { buckets: [] }, + SIBAGG: { value: 1 }, + }, + { + id: 'SERIES:example-02', + key: 'example-02', + label: 'example-02', + meta: { bucketSize: 10 }, + color: undefined, + timeseries: { buckets: [] }, + SIBAGG: { value: 2 }, + }, + ]); + }); + + test('should return gradient color', () => { + const series = { + id: 'SERIES', + color: '#F00', + split_mode: 'terms', + split_color_mode: 'gradient', + terms_field: 'beat.hostname', + terms_size: 10, + metrics: [ + { id: 'AVG', type: 'avg', field: 'cpu' }, + { id: 'SIBAGG', type: 'avg_bucket', field: 'AVG' }, + ], + }; + const panel = { type: 'timeseries' }; + expect(getSplits(resp, panel, series)).toEqual([ + expect.objectContaining({ + color: 'rgb(255, 0, 0)', + }), + expect.objectContaining({ + color: 'rgb(147, 0, 0)', + }), + ]); + }); + + test('should return rainbow color', () => { + const series = { + id: 'SERIES', + color: '#F00', + split_mode: 'terms', + split_color_mode: 'rainbow', + terms_field: 'beat.hostname', + terms_size: 10, + metrics: [ + { id: 'AVG', type: 'avg', field: 'cpu' }, + { id: 'SIBAGG', type: 'avg_bucket', field: 'AVG' }, + ], + }; + const panel = { type: 'timeseries' }; + expect(getSplits(resp, panel, series)).toEqual([ + expect.objectContaining({ + color: '#68BC00', + }), + expect.objectContaining({ + color: '#009CE0', + }), + ]); + }); }); test('should return a splits for filters group bys', () => { diff --git a/src/plugins/vis_type_timeseries/server/saved_objects/index.ts b/src/plugins/vis_type_timeseries/server/saved_objects/index.ts new file mode 100644 index 00000000000000..5f7f5767f423de --- /dev/null +++ b/src/plugins/vis_type_timeseries/server/saved_objects/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 { tsvbTelemetrySavedObjectType } from './tsvb_telemetry'; diff --git a/src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts b/src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts similarity index 100% rename from src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts rename to src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts diff --git a/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts b/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts index 779d9441df2fd9..e4b8ca19094e48 100644 --- a/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts +++ b/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts @@ -19,7 +19,7 @@ import { APICaller, CoreSetup, Plugin, PluginInitializerContext } from 'kibana/server'; import { UsageCollectionSetup } from '../../../usage_collection/server'; -import { tsvbTelemetrySavedObjectType } from './saved_object_type'; +import { tsvbTelemetrySavedObjectType } from '../saved_objects'; export interface ValidationTelemetryServiceSetup { logFailedValidation: () => void; diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts index 26f8278cd3d434..83d53d27e41fdc 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts @@ -1460,4 +1460,62 @@ describe('migration visualization', () => { expect(migratedParams.gauge_color_rules[1]).toEqual(params.gauge_color_rules[1]); }); }); + + describe('7.8.0 tsvb split_color_mode', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.8.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + + const generateDoc = (params: any) => ({ + attributes: { + title: 'My Vis', + type: 'visualization', + description: 'This is my super cool vis.', + visState: JSON.stringify(params), + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }); + + it('should change a missing split_color_mode to gradient', () => { + const params = { type: 'metrics', params: { series: [{}] } }; + const testDoc1 = generateDoc(params); + const migratedTestDoc1 = migrate(testDoc1); + const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; + + expect(series[0].split_color_mode).toEqual('gradient'); + }); + + it('should not change the color mode if it is set', () => { + const params = { type: 'metrics', params: { series: [{ split_color_mode: 'gradient' }] } }; + const testDoc1 = generateDoc(params); + const migratedTestDoc1 = migrate(testDoc1); + const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; + + expect(series[0].split_color_mode).toEqual('gradient'); + }); + + it('should not change the color mode if it is non-default', () => { + const params = { type: 'metrics', params: { series: [{ split_color_mode: 'rainbow' }] } }; + const testDoc1 = generateDoc(params); + const migratedTestDoc1 = migrate(testDoc1); + const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series; + + expect(series[0].split_color_mode).toEqual('rainbow'); + }); + + it('should not migrate a visualization of unknown type', () => { + const params = { type: 'unknown', params: { series: [{}] } }; + const doc = generateDoc(params); + const migratedDoc = migrate(doc); + const series = JSON.parse(migratedDoc.attributes.visState).params.series; + + expect(series[0].split_color_mode).toBeUndefined(); + }); + }); }); diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts index 80783e41863eaf..94473e35a942d5 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts @@ -602,7 +602,39 @@ const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { }; } } + return doc; +}; + +// [TSVB] Default color palette is changing, keep the default for older viz +const migrateTsvbDefaultColorPalettes: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + if (visState && visState.type === 'metrics') { + const series: any[] = get(visState, 'params.series') || []; + series.forEach(part => { + // The default value was not saved before + if (!part.split_color_mode) { + part.split_color_mode = 'gradient'; + } + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } return doc; }; @@ -639,4 +671,5 @@ export const visualizationSavedObjectTypeMigrations = { '7.3.1': flow(migrateFiltersAggQueryStringQueries), '7.4.2': flow(transformSplitFiltersStringToQueryObject), '7.7.0': flow(migrateOperatorKeyTypo), + '7.8.0': flow(migrateTsvbDefaultColorPalettes), }; diff --git a/test/examples/config.js b/test/examples/config.js index 49d75da2860753..2be34459d8d06f 100644 --- a/test/examples/config.js +++ b/test/examples/config.js @@ -28,6 +28,7 @@ export default async function({ readConfigFile }) { require.resolve('./search'), require.resolve('./embeddables'), require.resolve('./ui_actions'), + require.resolve('./state_sync'), ], services: { ...functionalConfig.get('services'), diff --git a/test/examples/state_sync/index.ts b/test/examples/state_sync/index.ts new file mode 100644 index 00000000000000..3c524f0feb6192 --- /dev/null +++ b/test/examples/state_sync/index.ts @@ -0,0 +1,39 @@ +/* + * 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 { PluginFunctionalProviderContext } from 'test/plugin_functional/services'; + +// eslint-disable-next-line import/no-default-export +export default function({ + getService, + getPageObjects, + loadTestFile, +}: PluginFunctionalProviderContext) { + const browser = getService('browser'); + const PageObjects = getPageObjects(['common']); + + describe('state sync examples', function() { + before(async () => { + await browser.setWindowSize(1300, 900); + await PageObjects.common.navigateToApp('settings'); + }); + + loadTestFile(require.resolve('./todo_app')); + }); +} diff --git a/test/examples/state_sync/todo_app.ts b/test/examples/state_sync/todo_app.ts new file mode 100644 index 00000000000000..4933d746ca4fd8 --- /dev/null +++ b/test/examples/state_sync/todo_app.ts @@ -0,0 +1,189 @@ +/* + * 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 { PluginFunctionalProviderContext } from 'test/plugin_functional/services'; + +// eslint-disable-next-line import/no-default-export +export default function({ getService, getPageObjects }: PluginFunctionalProviderContext) { + const testSubjects = getService('testSubjects'); + const find = getService('find'); + const retry = getService('retry'); + const appsMenu = getService('appsMenu'); + const browser = getService('browser'); + const PageObjects = getPageObjects(['common']); + const log = getService('log'); + + describe('TODO app', () => { + describe("TODO app with browser history (platform's ScopedHistory)", async () => { + const appId = 'stateContainersExampleBrowserHistory'; + let base: string; + + before(async () => { + base = await PageObjects.common.getHostPort(); + await appsMenu.clickLink('State containers example - browser history routing'); + }); + + it('links are rendered correctly and state is preserved in links', async () => { + const getHrefByLinkTestSubj = async (linkTestSubj: string) => + (await testSubjects.find(linkTestSubj)).getAttribute('href'); + + await expectPathname(await getHrefByLinkTestSubj('filterLinkCompleted'), '/completed'); + await expectPathname( + await getHrefByLinkTestSubj('filterLinkNotCompleted'), + '/not-completed' + ); + await expectPathname(await getHrefByLinkTestSubj('filterLinkAll'), '/'); + }); + + it('TODO app state is synced with url, back navigation works', async () => { + // checking that in initial state checkbox is unchecked and state is synced with url + expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(false); + expect(await browser.getCurrentUrl()).to.contain('completed:!f'); + + // check the checkbox by clicking the label (clicking checkbox directly fails as it is "no intractable") + (await find.byCssSelector('label[for="0"]')).click(); + + // wait for react to update dom and checkbox in checked state + await retry.tryForTime(1000, async () => { + await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(true); + }); + // checking that url is updated with checked state + expect(await browser.getCurrentUrl()).to.contain('completed:!t'); + + // checking back and forward button + await browser.goBack(); + expect(await browser.getCurrentUrl()).to.contain('completed:!f'); + await retry.tryForTime(1000, async () => { + await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(false); + }); + + await browser.goForward(); + expect(await browser.getCurrentUrl()).to.contain('completed:!t'); + await retry.tryForTime(1000, async () => { + await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(true); + }); + }); + + it('links navigation works', async () => { + // click link to filter only not completed + await testSubjects.click('filterLinkNotCompleted'); + await expectPathname(await browser.getCurrentUrl(), '/not-completed'); + // checkbox should be missing because it is "completed" + await testSubjects.missingOrFail('todoCheckbox-0'); + }); + + /** + * Parses app's scoped pathname from absolute url and asserts it against `expectedPathname` + * Also checks that hashes are equal (detail of todo app that state is rendered in links) + * @param absoluteUrl + * @param expectedPathname + */ + async function expectPathname(absoluteUrl: string, expectedPathname: string) { + const scoped = await getScopedUrl(absoluteUrl); + const [pathname, newHash] = scoped.split('#'); + expect(pathname).to.be(expectedPathname); + const [, currentHash] = (await browser.getCurrentUrl()).split('#'); + expect(newHash.replace(/%27/g, "'")).to.be(currentHash.replace(/%27/g, "'")); + } + + /** + * Get's part of url scoped to this app (removed kibana's host and app's pathname) + * @param url - absolute url + */ + async function getScopedUrl(url: string): Promise { + expect(url).to.contain(base); + expect(url).to.contain(appId); + const scopedUrl = url.slice(url.indexOf(appId) + appId.length); + expect(scopedUrl).not.to.contain(appId); // app id in url only once + return scopedUrl; + } + }); + + describe('TODO app with hash history ', async () => { + before(async () => { + await appsMenu.clickLink('State containers example - hash history routing'); + }); + + it('Links are rendered correctly and state is preserved in links', async () => { + const getHrefByLinkTestSubj = async (linkTestSubj: string) => + (await testSubjects.find(linkTestSubj)).getAttribute('href'); + await expectHashPathname(await getHrefByLinkTestSubj('filterLinkCompleted'), '/completed'); + await expectHashPathname( + await getHrefByLinkTestSubj('filterLinkNotCompleted'), + '/not-completed' + ); + await expectHashPathname(await getHrefByLinkTestSubj('filterLinkAll'), '/'); + }); + + it('TODO app state is synced with url, back navigation works', async () => { + // checking that in initial state checkbox is unchecked and state is synced with url + expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(false); + expect(await browser.getCurrentUrl()).to.contain('completed:!f'); + // check the checkbox by clicking the label (clicking checkbox directly fails as it is "no intractable") + (await find.byCssSelector('label[for="0"]')).click(); + + // wait for react to update dom and checkbox in checked state + await retry.tryForTime(1000, async () => { + await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(true); + }); + // checking that url is updated with checked state + expect(await browser.getCurrentUrl()).to.contain('completed:!t'); + + // checking back and forward button + await browser.goBack(); + expect(await browser.getCurrentUrl()).to.contain('completed:!f'); + await retry.tryForTime(1000, async () => { + await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(false); + }); + + await browser.goForward(); + expect(await browser.getCurrentUrl()).to.contain('completed:!t'); + await retry.tryForTime(1000, async () => { + await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(true); + }); + }); + + it('links navigation works', async () => { + // click link to filter only not completed + await testSubjects.click('filterLinkNotCompleted'); + await expectHashPathname(await browser.getCurrentUrl(), '/not-completed'); + // checkbox should be missing because it is "completed" + await testSubjects.missingOrFail('todoCheckbox-0'); + }); + + /** + * Parses app's pathname in hash from absolute url and asserts it against `expectedPathname` + * Also checks that queries in hashes are equal (detail of todo app that state is rendered in links) + * @param absoluteUrl + * @param expectedPathname + */ + async function expectHashPathname(hash: string, expectedPathname: string) { + log.debug(`expect hash pathname ${hash} to be ${expectedPathname}`); + const hashPath = hash.split('#')[1]; + const [hashPathname, hashQuery] = hashPath.split('?'); + const [, currentHash] = (await browser.getCurrentUrl()).split('#'); + const [, currentHashQuery] = currentHash.split('?'); + expect(currentHashQuery.replace(/%27/g, "'")).to.be(hashQuery.replace(/%27/g, "'")); + expect(hashPathname).to.be(expectedPathname); + } + }); + }); +} diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts index fa79190a5bf940..ac89c2b55e5140 100644 --- a/test/functional/apps/visualize/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/_tsvb_time_series.ts @@ -25,7 +25,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const retry = getService('retry'); const log = getService('log'); const kibanaServer = getService('kibanaServer'); - const testSubjects = getService('testSubjects'); describe('visual builder', function describeIndexTests() { beforeEach(async () => { @@ -126,20 +125,18 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { expect(actualCountMin).to.be('3 hours'); }); - // --reversed class is not implemented in @elastic\chart - describe.skip('Dark mode', () => { + describe('Dark mode', () => { before(async () => { await kibanaServer.uiSettings.update({ 'theme:darkMode': true, }); }); - it(`viz should have 'reversed' class when background color is white`, async () => { + it(`viz should have light class when background color is white`, async () => { await visualBuilder.clickPanelOptions('timeSeries'); await visualBuilder.setBackgroundColor('#FFFFFF'); - const classNames = await testSubjects.getAttribute('timeseriesChart', 'class'); - expect(classNames.includes('tvbVisTimeSeries--reversed')).to.be(true); + expect(await visualBuilder.checkTimeSeriesIsLight()).to.be(true); }); after(async () => { diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index b8e6c812b46bd5..12962b3a5cdef7 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -71,6 +71,10 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro } } + public async checkTimeSeriesIsLight() { + return await find.existsByCssSelector('.tvbVisTimeSeriesLight'); + } + public async checkTimeSeriesLegendIsPresent() { const isPresent = await find.existsByCssSelector('.echLegend'); if (!isPresent) { diff --git a/x-pack/index.js b/x-pack/index.js index 7fbd992120ea68..1a78c24b1221bc 100644 --- a/x-pack/index.js +++ b/x-pack/index.js @@ -10,7 +10,6 @@ import { monitoring } from './legacy/plugins/monitoring'; import { reporting } from './legacy/plugins/reporting'; import { security } from './legacy/plugins/security'; import { dashboardMode } from './legacy/plugins/dashboard_mode'; -import { logstash } from './legacy/plugins/logstash'; import { beats } from './legacy/plugins/beats_management'; import { apm } from './legacy/plugins/apm'; import { maps } from './legacy/plugins/maps'; @@ -40,7 +39,6 @@ module.exports = function(kibana) { spaces(kibana), security(kibana), dashboardMode(kibana), - logstash(kibana), beats(kibana), apm(kibana), maps(kibana), diff --git a/x-pack/legacy/plugins/logstash/README.md b/x-pack/legacy/plugins/logstash/README.md deleted file mode 100755 index 7d181249300fa3..00000000000000 --- a/x-pack/legacy/plugins/logstash/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## Logstash Plugin - -This plugin adds Logstash specific UI code to x-pack. Currently this plugin adds just the management features. diff --git a/x-pack/legacy/plugins/logstash/common/lib/__tests__/get_moment.js b/x-pack/legacy/plugins/logstash/common/lib/__tests__/get_moment.js deleted file mode 100755 index 2e63b231bec32d..00000000000000 --- a/x-pack/legacy/plugins/logstash/common/lib/__tests__/get_moment.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { getMoment } from '../get_moment'; - -describe('get_moment', () => { - describe('getMoment', () => { - it(`returns a moment object when passed a date`, () => { - const moment = getMoment('2017-03-30T14:53:08.121Z'); - - expect(moment.constructor.name).to.be('Moment'); - }); - - it(`returns null when passed falsy`, () => { - const results = [ - getMoment(false), - getMoment(0), - getMoment(''), - getMoment(null), - getMoment(undefined), - getMoment(NaN), - ]; - - results.forEach(result => { - expect(result).to.be(null); - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/logstash/common/lib/get_moment.js b/x-pack/legacy/plugins/logstash/common/lib/get_moment.js deleted file mode 100755 index 7a808ec9a0336f..00000000000000 --- a/x-pack/legacy/plugins/logstash/common/lib/get_moment.js +++ /dev/null @@ -1,15 +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 moment from 'moment'; - -export function getMoment(date) { - if (!date) { - return null; - } - - return moment(date); -} diff --git a/x-pack/legacy/plugins/logstash/index.js b/x-pack/legacy/plugins/logstash/index.js deleted file mode 100755 index 29f01032f34131..00000000000000 --- a/x-pack/legacy/plugins/logstash/index.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { resolve } from 'path'; -import { registerLicenseChecker } from './server/lib/register_license_checker'; -import { PLUGIN } from '../../../plugins/logstash/common/constants'; - -export const logstash = kibana => - new kibana.Plugin({ - id: PLUGIN.ID, - publicDir: resolve(__dirname, 'public'), - require: ['kibana', 'elasticsearch', 'xpack_main'], - configPrefix: 'xpack.logstash', - config(Joi) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - uiExports: { - managementSections: [ - 'plugins/logstash/sections/pipeline_list', - 'plugins/logstash/sections/pipeline_edit', - ], - home: ['plugins/logstash/lib/register_home_feature'], - }, - init: server => { - registerLicenseChecker(server); - }, - }); diff --git a/x-pack/legacy/plugins/logstash/public/lib/register_home_feature.ts b/x-pack/legacy/plugins/logstash/public/lib/register_home_feature.ts deleted file mode 100644 index 2e1ee2afb9ce61..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/lib/register_home_feature.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; -import { npSetup } from 'ui/new_platform'; -// @ts-ignore -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -import { FeatureCatalogueCategory } from '../../../../../../src/plugins/home/public'; -// @ts-ignore -import { PLUGIN } from '../../../../../plugins/logstash/common/constants'; - -const { - plugins: { home }, -} = npSetup; - -const enableLinks = Boolean(xpackInfo.get(`features.${PLUGIN.ID}.enableLinks`)); - -if (enableLinks) { - home.featureCatalogue.register({ - id: 'management_logstash', - title: i18n.translate('xpack.logstash.homeFeature.logstashPipelinesTitle', { - defaultMessage: 'Logstash Pipelines', - }), - description: i18n.translate('xpack.logstash.homeFeature.logstashPipelinesDescription', { - defaultMessage: 'Create, delete, update, and clone data ingestion pipelines.', - }), - icon: 'pipelineApp', - path: '/app/kibana#/management/logstash/pipelines', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - }); -} diff --git a/x-pack/legacy/plugins/logstash/public/lib/update_management_sections/update_logstash_sections.js b/x-pack/legacy/plugins/logstash/public/lib/update_management_sections/update_logstash_sections.js deleted file mode 100755 index 8da687529c8463..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/lib/update_management_sections/update_logstash_sections.js +++ /dev/null @@ -1,22 +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 { management } from 'ui/management'; - -export function updateLogstashSections(pipelineId) { - const editSection = management.getSection('logstash/pipelines/pipeline/edit'); - const newSection = management.getSection('logstash/pipelines/pipeline/new'); - - newSection.hide(); - editSection.hide(); - - if (pipelineId) { - editSection.url = `#/management/logstash/pipelines/pipeline/${pipelineId}/edit`; - editSection.show(); - } else { - newSection.show(); - } -} diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/pipeline_edit/pipeline_edit.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/pipeline_edit/pipeline_edit.js deleted file mode 100755 index 83446278fdeca8..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/pipeline_edit/pipeline_edit.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { render } from 'react-dom'; -import { isEmpty } from 'lodash'; -import { uiModules } from 'ui/modules'; -import { npSetup } from 'ui/new_platform'; -import { toastNotifications } from 'ui/notify'; -import { I18nContext } from 'ui/i18n'; -import { PipelineEditor } from '../../../../components/pipeline_editor'; -import 'plugins/logstash/services/license'; -import { logstashSecurity } from 'plugins/logstash/services/security'; -import 'ace'; - -const app = uiModules.get('xpack/logstash'); - -app.directive('pipelineEdit', function($injector) { - const pipelineService = $injector.get('pipelineService'); - const licenseService = $injector.get('logstashLicenseService'); - const kbnUrl = $injector.get('kbnUrl'); - const $route = $injector.get('$route'); - - return { - restrict: 'E', - link: async (scope, el) => { - const close = () => scope.$evalAsync(kbnUrl.change('/management/logstash/pipelines', {})); - const open = id => - scope.$evalAsync(kbnUrl.change(`/management/logstash/pipelines/${id}/edit`)); - - const userResource = logstashSecurity.isSecurityEnabled() - ? await npSetup.plugins.security.authc.getCurrentUser() - : null; - - render( - - - , - el[0] - ); - }, - scope: { - pipeline: '=', - }, - }; -}); diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/upgrade_failure/upgrade_failure.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/upgrade_failure/upgrade_failure.js deleted file mode 100755 index 2ef99d3b476723..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/upgrade_failure/upgrade_failure.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { render } from 'react-dom'; -import { isEmpty } from 'lodash'; -import { uiModules } from 'ui/modules'; -import { I18nContext } from 'ui/i18n'; -import { UpgradeFailure } from '../../../../components/upgrade_failure'; - -const app = uiModules.get('xpack/logstash'); - -app.directive('upgradeFailure', $injector => { - const $route = $injector.get('$route'); - const kbnUrl = $injector.get('kbnUrl'); - - return { - link: (scope, el) => { - const onRetry = () => { - $route.updateParams({ retry: true }); - $route.reload(); - }; - const onClose = () => { - scope.$evalAsync(kbnUrl.change('management/logstash/pipelines', {})); - }; - const isNewPipeline = isEmpty(scope.pipeline.id); - const isManualUpgrade = !!$route.current.params.retry; - - render( - - - , - el[0] - ); - }, - restrict: 'E', - scope: { - pipeline: '=', - }, - }; -}); diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.html b/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.html deleted file mode 100755 index e1c422d46dfdb6..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.js deleted file mode 100755 index 733f7dc3ae2e62..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.js +++ /dev/null @@ -1,87 +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 routes from 'ui/routes'; -import { toastNotifications } from 'ui/notify'; -import { i18n } from '@kbn/i18n'; -import template from './pipeline_edit_route.html'; -import 'plugins/logstash/services/pipeline'; -import 'plugins/logstash/services/license'; -import 'plugins/logstash/services/upgrade'; -import './components/pipeline_edit'; -import './components/upgrade_failure'; -import { updateLogstashSections } from 'plugins/logstash/lib/update_management_sections'; -import { Pipeline } from 'plugins/logstash/models/pipeline'; -import { getPipelineCreateBreadcrumbs, getPipelineEditBreadcrumbs } from '../breadcrumbs'; - -routes - .when('/management/logstash/pipelines/pipeline/:id/edit', { - k7Breadcrumbs: getPipelineEditBreadcrumbs, - }) - .when('/management/logstash/pipelines/new-pipeline', { - k7Breadcrumbs: getPipelineCreateBreadcrumbs, - }) - .defaults(/management\/logstash\/pipelines\/(new-pipeline|pipeline\/:id\/edit)/, { - template: template, - controller: class PipelineEditRouteController { - constructor($injector) { - const $route = $injector.get('$route'); - this.pipeline = $route.current.locals.pipeline; - this.isUpgraded = $route.current.locals.isUpgraded; - } - }, - controllerAs: 'pipelineEditRoute', - resolve: { - logstashTabs: $injector => { - const $route = $injector.get('$route'); - const pipelineId = $route.current.params.id; - updateLogstashSections(pipelineId); - }, - pipeline: function($injector) { - const $route = $injector.get('$route'); - const pipelineService = $injector.get('pipelineService'); - const licenseService = $injector.get('logstashLicenseService'); - const kbnUrl = $injector.get('kbnUrl'); - - const pipelineId = $route.current.params.id; - - if (!pipelineId) return new Pipeline(); - - return pipelineService - .loadPipeline(pipelineId) - .then(pipeline => (!!$route.current.params.clone ? pipeline.clone : pipeline)) - .catch(err => { - return licenseService.checkValidity().then(() => { - if (err.status !== 403) { - toastNotifications.addDanger( - i18n.translate('xpack.logstash.couldNotLoadPipelineErrorNotification', { - defaultMessage: `Couldn't load pipeline. Error: '{errStatusText}'.`, - values: { - errStatusText: err.statusText, - }, - }) - ); - } - - kbnUrl.redirect('/management/logstash/pipelines'); - return Promise.reject(); - }); - }); - }, - checkLicense: $injector => { - const licenseService = $injector.get('logstashLicenseService'); - return licenseService.checkValidity(); - }, - isUpgraded: $injector => { - const upgradeService = $injector.get('upgradeService'); - return upgradeService.executeUpgrade(); - }, - }, - }); - -routes.when('/management/logstash/pipelines/pipeline/:id', { - redirectTo: '/management/logstash/pipelines/pipeline/:id/edit', -}); diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/index.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/index.js deleted file mode 100755 index 7e8ca0e4c2c576..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/index.js +++ /dev/null @@ -1,7 +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 './pipeline_list'; diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/pipeline_list.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/pipeline_list.js deleted file mode 100755 index b856979aed8b6d..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/pipeline_list.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { render } from 'react-dom'; -import { uiModules } from 'ui/modules'; -import { toastNotifications } from 'ui/notify'; -import { I18nContext } from 'ui/i18n'; -import { PipelineList } from '../../../../components/pipeline_list'; -import 'plugins/logstash/services/pipelines'; -import 'plugins/logstash/services/license'; -import 'plugins/logstash/services/cluster'; -import 'plugins/logstash/services/monitoring'; - -const app = uiModules.get('xpack/logstash'); - -app.directive('pipelineList', function($injector) { - const pipelinesService = $injector.get('pipelinesService'); - const licenseService = $injector.get('logstashLicenseService'); - const clusterService = $injector.get('xpackLogstashClusterService'); - const monitoringService = $injector.get('xpackLogstashMonitoringService'); - const kbnUrl = $injector.get('kbnUrl'); - - return { - restrict: 'E', - link: (scope, el) => { - const openPipeline = id => - scope.$evalAsync(kbnUrl.change(`management/logstash/pipelines/pipeline/${id}/edit`)); - const createPipeline = () => - scope.$evalAsync(kbnUrl.change('management/logstash/pipelines/new-pipeline')); - const clonePipeline = id => - scope.$evalAsync(kbnUrl.change(`management/logstash/pipelines/pipeline/${id}/edit?clone`)); - render( - - - , - el[0] - ); - }, - scope: {}, - controllerAs: 'pipelineList', - }; -}); diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/index.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/index.js deleted file mode 100755 index f60decd1378d5c..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/index.js +++ /dev/null @@ -1,8 +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 './register_management_section'; -import './pipeline_list_route'; diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.html b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.html deleted file mode 100755 index 55b3fabd701614..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.html +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.js deleted file mode 100755 index eb593207572c20..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import routes from 'ui/routes'; -import { management } from 'ui/management'; -import template from './pipeline_list_route.html'; -import './components/pipeline_list'; -import 'plugins/logstash/services/license'; -import { getPipelineListBreadcrumbs } from '../breadcrumbs'; - -routes.when('/management/logstash/pipelines/', { - template, - k7Breadcrumbs: getPipelineListBreadcrumbs, -}); - -routes.defaults(/\/management/, { - resolve: { - logstashManagementSection: $injector => { - const licenseService = $injector.get('logstashLicenseService'); - const logstashSection = management.getSection('logstash/pipelines'); - - if (licenseService.enableLinks) { - logstashSection.show(); - logstashSection.enable(); - } else { - logstashSection.hide(); - } - }, - }, -}); diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/register_management_section.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/register_management_section.js deleted file mode 100755 index e285418f5f2ae5..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/register_management_section.js +++ /dev/null @@ -1,36 +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 { management } from 'ui/management'; -import { i18n } from '@kbn/i18n'; - -management.getSection('logstash').register('pipelines', { - display: i18n.translate('xpack.logstash.managementSection.pipelinesTitle', { - defaultMessage: 'Pipelines', - }), - order: 10, - url: '#/management/logstash/pipelines/', -}); - -management.getSection('logstash/pipelines').register('pipeline', { - visible: false, -}); - -management.getSection('logstash/pipelines/pipeline').register('edit', { - display: i18n.translate('xpack.logstash.managementSection.editPipelineTitle', { - defaultMessage: 'Edit pipeline', - }), - order: 1, - visible: false, -}); - -management.getSection('logstash/pipelines/pipeline').register('new', { - display: i18n.translate('xpack.logstash.managementSection.createPipelineTitle', { - defaultMessage: 'Create pipeline', - }), - order: 1, - visible: false, -}); diff --git a/x-pack/legacy/plugins/logstash/public/services/cluster/cluster_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/cluster/cluster_service.factory.js deleted file mode 100755 index 0fee2804c704d4..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/cluster/cluster_service.factory.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { ClusterService } from './cluster_service'; - -uiModules.get('xpack/logstash').factory('xpackLogstashClusterService', $injector => { - const $http = $injector.get('$http'); - return new ClusterService($http); -}); diff --git a/x-pack/legacy/plugins/logstash/public/services/cluster/index.js b/x-pack/legacy/plugins/logstash/public/services/cluster/index.js deleted file mode 100755 index ba52657a27ca8b..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/cluster/index.js +++ /dev/null @@ -1,7 +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 './cluster_service.factory'; diff --git a/x-pack/legacy/plugins/logstash/public/services/license/index.js b/x-pack/legacy/plugins/logstash/public/services/license/index.js deleted file mode 100755 index 8be8fb5ccbc64f..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/license/index.js +++ /dev/null @@ -1,7 +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 './license_service.factory'; diff --git a/x-pack/legacy/plugins/logstash/public/services/license/license_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/license/license_service.factory.js deleted file mode 100755 index 0e131f9b94008d..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/license/license_service.factory.js +++ /dev/null @@ -1,14 +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 { uiModules } from 'ui/modules'; -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -import 'ui/url'; -import { LogstashLicenseService } from './logstash_license_service'; - -uiModules.get('xpack/logstash').factory('logstashLicenseService', ($timeout, kbnUrl) => { - return new LogstashLicenseService(xpackInfo, kbnUrl, $timeout); -}); diff --git a/x-pack/legacy/plugins/logstash/public/services/license/logstash_license_service.js b/x-pack/legacy/plugins/logstash/public/services/license/logstash_license_service.js deleted file mode 100755 index 69cc8614a6ae26..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/license/logstash_license_service.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { toastNotifications } from 'ui/notify'; -import { MarkdownSimple } from '../../../../../../../src/plugins/kibana_react/public'; -import { PLUGIN } from '../../../../../../plugins/logstash/common/constants'; - -export class LogstashLicenseService { - constructor(xpackInfoService, kbnUrlService, $timeout) { - this.xpackInfoService = xpackInfoService; - this.kbnUrlService = kbnUrlService; - this.$timeout = $timeout; - } - - get enableLinks() { - return Boolean(this.xpackInfoService.get(`features.${PLUGIN.ID}.enableLinks`)); - } - - get isAvailable() { - return Boolean(this.xpackInfoService.get(`features.${PLUGIN.ID}.isAvailable`)); - } - - get isReadOnly() { - return Boolean(this.xpackInfoService.get(`features.${PLUGIN.ID}.isReadOnly`)); - } - - get message() { - return this.xpackInfoService.get(`features.${PLUGIN.ID}.message`); - } - - notifyAndRedirect() { - toastNotifications.addDanger({ - title: ( - - {this.xpackInfoService.get(`features.${PLUGIN.ID}.message`)} - - ), - }); - this.kbnUrlService.redirect('/management'); - } - - /** - * Checks if the license is valid or the license can perform downgraded UI tasks. - * Otherwise, notifies and redirects. - */ - checkValidity() { - return new Promise((resolve, reject) => { - this.$timeout(() => { - if (this.isAvailable) { - return resolve(); - } - - this.notifyAndRedirect(); - return reject(); - }, 10); // To allow latest XHR call to update license info - }); - } -} diff --git a/x-pack/legacy/plugins/logstash/public/services/monitoring/index.js b/x-pack/legacy/plugins/logstash/public/services/monitoring/index.js deleted file mode 100755 index 83b2105beb5ef8..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/monitoring/index.js +++ /dev/null @@ -1,7 +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 './monitoring_service.factory'; diff --git a/x-pack/legacy/plugins/logstash/public/services/monitoring/monitoring_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/monitoring/monitoring_service.factory.js deleted file mode 100755 index 271c776dd6f69c..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/monitoring/monitoring_service.factory.js +++ /dev/null @@ -1,18 +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 { uiModules } from 'ui/modules'; -import { MonitoringService } from './monitoring_service'; -import '../cluster'; - -uiModules.get('xpack/logstash').factory('xpackLogstashMonitoringService', $injector => { - const $http = $injector.get('$http'); - const Promise = $injector.get('Promise'); - const monitoringUiEnabled = - $injector.has('monitoringUiEnabled') && $injector.get('monitoringUiEnabled'); - const clusterService = $injector.get('xpackLogstashClusterService'); - return new MonitoringService($http, Promise, monitoringUiEnabled, clusterService); -}); diff --git a/x-pack/legacy/plugins/logstash/public/services/pipeline/index.js b/x-pack/legacy/plugins/logstash/public/services/pipeline/index.js deleted file mode 100755 index 3b0e28bd555e6f..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/pipeline/index.js +++ /dev/null @@ -1,7 +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 './pipeline_service.factory'; diff --git a/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.factory.js deleted file mode 100755 index cf93915425213b..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.factory.js +++ /dev/null @@ -1,14 +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 { uiModules } from 'ui/modules'; -import { PipelineService } from './pipeline_service'; - -uiModules.get('xpack/logstash').factory('pipelineService', $injector => { - const $http = $injector.get('$http'); - const pipelinesService = $injector.get('pipelinesService'); - return new PipelineService($http, pipelinesService); -}); diff --git a/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.js b/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.js deleted file mode 100755 index b5d0dbeb852d52..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.js +++ /dev/null @@ -1,40 +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 chrome from 'ui/chrome'; -import { ROUTES } from '../../../../../../plugins/logstash/common/constants'; -import { Pipeline } from 'plugins/logstash/models/pipeline'; - -export class PipelineService { - constructor($http, pipelinesService) { - this.$http = $http; - this.pipelinesService = pipelinesService; - this.basePath = chrome.addBasePath(ROUTES.API_ROOT); - } - - loadPipeline(id) { - return this.$http.get(`${this.basePath}/pipeline/${id}`).then(response => { - return Pipeline.fromUpstreamJSON(response.data); - }); - } - - savePipeline(pipelineModel) { - return this.$http - .put(`${this.basePath}/pipeline/${pipelineModel.id}`, pipelineModel.upstreamJSON) - .catch(e => { - throw e.data.message; - }); - } - - deletePipeline(id) { - return this.$http - .delete(`${this.basePath}/pipeline/${id}`) - .then(() => this.pipelinesService.addToRecentlyDeleted(id)) - .catch(e => { - throw e.data.message; - }); - } -} diff --git a/x-pack/legacy/plugins/logstash/public/services/pipelines/index.js b/x-pack/legacy/plugins/logstash/public/services/pipelines/index.js deleted file mode 100755 index e273e12d46c6d0..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/pipelines/index.js +++ /dev/null @@ -1,7 +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 './pipelines_service.factory'; diff --git a/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.factory.js deleted file mode 100755 index 9295949e001eb3..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.factory.js +++ /dev/null @@ -1,17 +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 { uiModules } from 'ui/modules'; -import { PipelinesService } from './pipelines_service'; -import '../monitoring'; - -uiModules.get('xpack/logstash').factory('pipelinesService', $injector => { - const $http = $injector.get('$http'); - const $window = $injector.get('$window'); - const Promise = $injector.get('Promise'); - const monitoringService = $injector.get('xpackLogstashMonitoringService'); - return new PipelinesService($http, $window, Promise, monitoringService); -}); diff --git a/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.js b/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.js deleted file mode 100755 index d70c8be06fde4d..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.js +++ /dev/null @@ -1,135 +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 chrome from 'ui/chrome'; -import { ROUTES, MONITORING } from '../../../../../../plugins/logstash/common/constants'; -import { PipelineListItem } from 'plugins/logstash/models/pipeline_list_item'; - -const RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY = 'xpack.logstash.recentlyDeletedPipelines'; - -export class PipelinesService { - constructor($http, $window, Promise, monitoringService) { - this.$http = $http; - this.$window = $window; - this.Promise = Promise; - this.monitoringService = monitoringService; - this.basePath = chrome.addBasePath(ROUTES.API_ROOT); - } - - getPipelineList() { - return this.Promise.all([ - this.getManagementPipelineList(), - this.getMonitoringPipelineList(), - ]).then(([managementPipelines, monitoringPipelines]) => { - const now = Date.now(); - - // Monitoring will report centrally-managed pipelines as well, including recently-deleted centrally-managed ones. - // If there's a recently-deleted pipeline we're keeping track of BUT monitoring doesn't report it, that means - // it's not running in Logstash any more. So we can stop tracking it as a recently-deleted pipeline. - const monitoringPipelineIds = monitoringPipelines.map(pipeline => pipeline.id); - this.getRecentlyDeleted().forEach(recentlyDeletedPipeline => { - // We don't want to stop tracking the recently-deleted pipeline until Monitoring has had some - // time to report on it. Otherwise, if we stop tracking first, *then* Monitoring reports it, we'll - // still end up showing it in the list until Monitoring stops reporting it. - if (now - recentlyDeletedPipeline.deletedOn < MONITORING.ACTIVE_PIPELINE_RANGE_S * 1000) { - return; - } - - // If Monitoring is still reporting the pipeline, don't stop tracking it yet - if (monitoringPipelineIds.includes(recentlyDeletedPipeline.id)) { - return; - } - - this.removeFromRecentlyDeleted(recentlyDeletedPipeline.id); - }); - - // Merge centrally-managed pipelines with pipelines reported by monitoring. Take care to dedupe - // while merging because monitoring will (rightly) report centrally-managed pipelines as well, - // including recently-deleted ones! - const managementPipelineIds = managementPipelines.map(pipeline => pipeline.id); - return managementPipelines.concat( - monitoringPipelines.filter( - monitoringPipeline => - !managementPipelineIds.includes(monitoringPipeline.id) && - !this.isRecentlyDeleted(monitoringPipeline.id) - ) - ); - }); - } - - getManagementPipelineList() { - return this.$http - .get(`${this.basePath}/pipelines`) - .then(response => - response.data.pipelines.map(pipeline => PipelineListItem.fromUpstreamJSON(pipeline)) - ); - } - - getMonitoringPipelineList() { - return this.monitoringService.getPipelineList(); - } - - /** - * Delete a collection of pipelines - * - * @param pipelineIds Array of pipeline IDs - * @return Promise { numSuccesses, numErrors } - */ - deletePipelines(pipelineIds) { - const body = { - pipelineIds, - }; - return this.$http.post(`${this.basePath}/pipelines/delete`, body).then(response => { - this.addToRecentlyDeleted(...pipelineIds); - return response.data.results; - }); - } - - addToRecentlyDeleted(...pipelineIds) { - const recentlyDeletedPipelines = this.getRecentlyDeleted(); - const recentlyDeletedPipelineIds = recentlyDeletedPipelines.map(pipeline => pipeline.id); - pipelineIds.forEach(pipelineId => { - if (!recentlyDeletedPipelineIds.includes(pipelineId)) { - recentlyDeletedPipelines.push({ - id: pipelineId, - deletedOn: Date.now(), - }); - } - }); - this.setRecentlyDeleted(recentlyDeletedPipelines); - } - - removeFromRecentlyDeleted(...pipelineIds) { - const recentlyDeletedPipelinesToKeep = this.getRecentlyDeleted().filter( - recentlyDeletedPipeline => !pipelineIds.includes(recentlyDeletedPipeline.id) - ); - this.setRecentlyDeleted(recentlyDeletedPipelinesToKeep); - } - - isRecentlyDeleted(pipelineId) { - return this.getRecentlyDeleted() - .map(pipeline => pipeline.id) - .includes(pipelineId); - } - - getRecentlyDeleted() { - const recentlyDeletedPipelines = this.$window.localStorage.getItem( - RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY - ); - if (!recentlyDeletedPipelines) { - return []; - } - - return JSON.parse(recentlyDeletedPipelines); - } - - setRecentlyDeleted(recentlyDeletedPipelineIds) { - this.$window.localStorage.setItem( - RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY, - JSON.stringify(recentlyDeletedPipelineIds) - ); - } -} diff --git a/x-pack/legacy/plugins/logstash/public/services/security/index.js b/x-pack/legacy/plugins/logstash/public/services/security/index.js deleted file mode 100755 index c9ff911723156f..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/security/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { logstashSecurity } from './logstash_security'; diff --git a/x-pack/legacy/plugins/logstash/public/services/security/logstash_security.js b/x-pack/legacy/plugins/logstash/public/services/security/logstash_security.js deleted file mode 100755 index 0949038c9b6c73..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/security/logstash_security.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; - -export const logstashSecurity = { - isSecurityEnabled() { - return Boolean(xpackInfo.get(`features.security`)); - }, -}; diff --git a/x-pack/legacy/plugins/logstash/public/services/upgrade/index.js b/x-pack/legacy/plugins/logstash/public/services/upgrade/index.js deleted file mode 100755 index 345d0d0ff68c6d..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/upgrade/index.js +++ /dev/null @@ -1,7 +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 './upgrade_service.factory'; diff --git a/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.factory.js deleted file mode 100755 index 925c6ae677bdf7..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.factory.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { UpgradeService } from './upgrade_service'; - -uiModules.get('xpack/logstash').factory('upgradeService', $injector => { - const $http = $injector.get('$http'); - return new UpgradeService($http); -}); diff --git a/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.js b/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.js deleted file mode 100755 index 2019bdc1bf1aaa..00000000000000 --- a/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.js +++ /dev/null @@ -1,24 +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 chrome from 'ui/chrome'; -import { ROUTES } from '../../../../../../plugins/logstash/common/constants'; - -export class UpgradeService { - constructor($http) { - this.$http = $http; - this.basePath = chrome.addBasePath(ROUTES.API_ROOT); - } - - executeUpgrade() { - return this.$http - .post(`${this.basePath}/upgrade`) - .then(response => response.data.is_upgraded) - .catch(e => { - throw e.data.message; - }); - } -} diff --git a/x-pack/legacy/plugins/logstash/server/lib/check_license/__tests__/check_license.js b/x-pack/legacy/plugins/logstash/server/lib/check_license/__tests__/check_license.js deleted file mode 100755 index 5fcce0aaa12190..00000000000000 --- a/x-pack/legacy/plugins/logstash/server/lib/check_license/__tests__/check_license.js +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { set } from 'lodash'; -import { checkLicense } from '../check_license'; - -describe('check_license', function() { - let mockLicenseInfo; - beforeEach(() => (mockLicenseInfo = {})); - - describe('license information is undefined', () => { - beforeEach(() => (mockLicenseInfo = undefined)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is not available', () => { - beforeEach(() => (mockLicenseInfo.isAvailable = () => false)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is available', () => { - beforeEach(() => { - mockLicenseInfo.isAvailable = () => true; - set(mockLicenseInfo, 'license.getType', () => 'basic'); - }); - - describe('& license is > basic', () => { - beforeEach(() => { - set(mockLicenseInfo, 'license.isOneOf', () => true); - mockLicenseInfo.feature = () => ({ isEnabled: () => true }); // Security feature is enabled - }); - - describe('& license is active', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - - it('should set isAvailable to true', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); - }); - - it('should set enableLinks to true', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); - }); - - it('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should not set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.be(undefined); - }); - }); - - describe('& license is expired', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - - it('should set isAvailable to true', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); - }); - - it('should set enableLinks to true', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); - }); - - it('should set isReadOnly to true', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(true); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); - - describe('& license is basic', () => { - beforeEach(() => { - set(mockLicenseInfo, 'license.isOneOf', () => false); - mockLicenseInfo.feature = () => ({ isEnabled: () => true }); // Security feature is enabled - }); - - describe('& license is active', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('& license is expired', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); - - describe('& security is disabled', () => { - beforeEach(() => { - mockLicenseInfo.feature = () => ({ isEnabled: () => false }); // Security feature is disabled - set(mockLicenseInfo, 'license.isOneOf', () => true); - set(mockLicenseInfo, 'license.isActive', () => true); - }); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set isReadOnly to false', () => { - expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/logstash/server/lib/check_license/check_license.js b/x-pack/legacy/plugins/logstash/server/lib/check_license/check_license.js deleted file mode 100755 index 31136ae1c72a5c..00000000000000 --- a/x-pack/legacy/plugins/logstash/server/lib/check_license/check_license.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export function checkLicense(xpackLicenseInfo) { - // If, for some reason, we cannot get the license information - // from Elasticsearch, assume worst case and disable the Logstash pipeline UI - if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { - return { - isAvailable: false, - enableLinks: false, - isReadOnly: false, - message: i18n.translate( - 'xpack.logstash.managementSection.notPossibleToManagePipelinesMessage', - { - defaultMessage: - 'You cannot manage Logstash pipelines because license information is not available at this time.', - } - ), - }; - } - - const VALID_LICENSE_MODES = ['trial', 'standard', 'gold', 'platinum', 'enterprise']; - - const isLicenseModeValid = xpackLicenseInfo.license.isOneOf(VALID_LICENSE_MODES); - const isLicenseActive = xpackLicenseInfo.license.isActive(); - const licenseType = xpackLicenseInfo.license.getType(); - const isSecurityEnabled = xpackLicenseInfo.feature('security').isEnabled(); - - // Security is not enabled in ES - if (!isSecurityEnabled) { - const message = i18n.translate('xpack.logstash.managementSection.enableSecurityDescription', { - defaultMessage: - 'Security must be enabled in order to use Logstash pipeline management features.' + - ' Please set xpack.security.enabled: true in your elasticsearch.yml.', - }); - return { - isAvailable: false, - enableLinks: false, - isReadOnly: false, - message, - }; - } - - // License is not valid - if (!isLicenseModeValid) { - return { - isAvailable: false, - enableLinks: false, - isReadOnly: false, - message: i18n.translate('xpack.logstash.managementSection.licenseDoesNotSupportDescription', { - defaultMessage: - 'Your {licenseType} license does not support Logstash pipeline management features. Please upgrade your license.', - values: { licenseType }, - }), - }; - } - - // License is valid but not active, we go into a read-only mode. - if (!isLicenseActive) { - return { - isAvailable: true, - enableLinks: true, - isReadOnly: true, - message: i18n.translate( - 'xpack.logstash.managementSection.pipelineCrudOperationsNotAllowedDescription', - { - defaultMessage: - 'You cannot edit, create, or delete your Logstash pipelines because your {licenseType} license has expired.', - values: { licenseType }, - } - ), - }; - } - - // License is valid and active - return { - isAvailable: true, - enableLinks: true, - isReadOnly: false, - }; -} diff --git a/x-pack/legacy/plugins/logstash/server/lib/check_license/index.js b/x-pack/legacy/plugins/logstash/server/lib/check_license/index.js deleted file mode 100755 index f2c070fd44b6e6..00000000000000 --- a/x-pack/legacy/plugins/logstash/server/lib/check_license/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { checkLicense } from './check_license'; diff --git a/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/index.js b/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/index.js deleted file mode 100755 index 7b0f97c38d1292..00000000000000 --- a/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { registerLicenseChecker } from './register_license_checker'; diff --git a/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/register_license_checker.js b/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/register_license_checker.js deleted file mode 100755 index a0d06e77b410d2..00000000000000 --- a/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/register_license_checker.js +++ /dev/null @@ -1,21 +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 { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status'; -import { checkLicense } from '../check_license'; -import { PLUGIN } from '../../../../../../plugins/logstash/common/constants'; - -export function registerLicenseChecker(server) { - const xpackMainPlugin = server.plugins.xpack_main; - const logstashPlugin = server.plugins.logstash; - - mirrorPluginStatus(xpackMainPlugin, logstashPlugin); - xpackMainPlugin.status.once('green', () => { - // Register a function that is called whenever the xpack info changes, - // to re-compute the license check results for this plugin - xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense); - }); -} diff --git a/x-pack/legacy/plugins/maps/common/constants.ts b/x-pack/legacy/plugins/maps/common/constants.ts deleted file mode 100644 index 98945653c25dc8..00000000000000 --- a/x-pack/legacy/plugins/maps/common/constants.ts +++ /dev/null @@ -1,8 +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. - */ - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export * from '../../../../plugins/maps/common/constants'; diff --git a/x-pack/legacy/plugins/maps/common/get_join_key.ts b/x-pack/legacy/plugins/maps/common/get_join_key.ts deleted file mode 100644 index 004f12ca08d2ea..00000000000000 --- a/x-pack/legacy/plugins/maps/common/get_join_key.ts +++ /dev/null @@ -1,8 +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. - */ - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export * from '../../../../plugins/maps/common/get_join_key'; diff --git a/x-pack/legacy/plugins/maps/common/i18n_getters.ts b/x-pack/legacy/plugins/maps/common/i18n_getters.ts deleted file mode 100644 index f9d186dea2e2b9..00000000000000 --- a/x-pack/legacy/plugins/maps/common/i18n_getters.ts +++ /dev/null @@ -1,8 +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. - */ - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export * from '../../../../plugins/maps/common/i18n_getters'; diff --git a/x-pack/legacy/plugins/maps/common/parse_xml_string.js b/x-pack/legacy/plugins/maps/common/parse_xml_string.js deleted file mode 100644 index 34ec1444728281..00000000000000 --- a/x-pack/legacy/plugins/maps/common/parse_xml_string.js +++ /dev/null @@ -1,8 +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. - */ - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export * from '../../../../plugins/maps/common/parse_xml_string'; diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js index f4e01efc05f45f..8546e3712c7630 100644 --- a/x-pack/legacy/plugins/maps/index.js +++ b/x-pack/legacy/plugins/maps/index.js @@ -9,9 +9,14 @@ import mappings from './mappings.json'; import { i18n } from '@kbn/i18n'; import { resolve } from 'path'; import { migrations } from './migrations'; -import { getAppTitle } from './common/i18n_getters'; +import { getAppTitle } from '../../../plugins/maps/common/i18n_getters'; import { MapPlugin } from './server/plugin'; -import { APP_ID, APP_ICON, createMapPath, MAP_SAVED_OBJECT_TYPE } from './common/constants'; +import { + APP_ID, + APP_ICON, + createMapPath, + MAP_SAVED_OBJECT_TYPE, +} from '../../../plugins/maps/common/constants'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; export function maps(kibana) { @@ -53,7 +58,6 @@ export function maps(kibana) { }; }, embeddableFactories: ['plugins/maps/embeddable/map_embeddable_factory'], - home: ['plugins/maps/legacy_register_feature'], styleSheetPaths: `${__dirname}/public/index.scss`, savedObjectSchemas: { 'maps-telemetry': { @@ -78,7 +82,6 @@ export function maps(kibana) { }, mappings, migrations, - hacks: ['plugins/maps/register_vis_type_alias'], }, config(Joi) { return Joi.object({ diff --git a/x-pack/legacy/plugins/maps/migrations.js b/x-pack/legacy/plugins/maps/migrations.js index a8e69eef7a02fd..d3666025082b78 100644 --- a/x-pack/legacy/plugins/maps/migrations.js +++ b/x-pack/legacy/plugins/maps/migrations.js @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { extractReferences } from './common/migrations/references'; -import { emsRasterTileToEmsVectorTile } from './common/migrations/ems_raster_tile_to_ems_vector_tile'; -import { topHitsTimeToSort } from './common/migrations/top_hits_time_to_sort'; -import { moveApplyGlobalQueryToSources } from './common/migrations/move_apply_global_query'; -import { addFieldMetaOptions } from './common/migrations/add_field_meta_options'; -import { migrateSymbolStyleDescriptor } from './common/migrations/migrate_symbol_style_descriptor'; -import { migrateUseTopHitsToScalingType } from './common/migrations/scaling_type'; -import { migrateJoinAggKey } from './common/migrations/join_agg_key'; +import { extractReferences } from '../../../plugins/maps/common/migrations/references'; +import { emsRasterTileToEmsVectorTile } from '../../../plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile'; +import { topHitsTimeToSort } from '../../../plugins/maps/common/migrations/top_hits_time_to_sort'; +import { moveApplyGlobalQueryToSources } from '../../../plugins/maps/common/migrations/move_apply_global_query'; +import { addFieldMetaOptions } from '../../../plugins/maps/common/migrations/add_field_meta_options'; +import { migrateSymbolStyleDescriptor } from '../../../plugins/maps/common/migrations/migrate_symbol_style_descriptor'; +import { migrateUseTopHitsToScalingType } from '../../../plugins/maps/common/migrations/scaling_type'; +import { migrateJoinAggKey } from '../../../plugins/maps/common/migrations/join_agg_key'; export const migrations = { map: { diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts deleted file mode 100644 index 34f8c30b51874b..00000000000000 --- a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts +++ /dev/null @@ -1,8 +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. - */ -/* eslint-disable @typescript-eslint/consistent-type-definitions */ - -export * from '../../../../../plugins/maps/public/actions/map_actions'; diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.js b/x-pack/legacy/plugins/maps/public/actions/map_actions.js deleted file mode 100644 index 7bfbf5761c5b8c..00000000000000 --- a/x-pack/legacy/plugins/maps/public/actions/map_actions.js +++ /dev/null @@ -1,978 +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 _ from 'lodash'; -import turf from 'turf'; -import turfBooleanContains from '@turf/boolean-contains'; -import uuid from 'uuid/v4'; -import { - getLayerList, - getLayerListRaw, - getDataFilters, - getSelectedLayerId, - getMapReady, - getWaitingForMapReadyLayerListRaw, - getTransientLayerId, - getOpenTooltips, - getQuery, - getDataRequestDescriptor, -} from '../selectors/map_selectors'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FLYOUT_STATE } from '../../../../../plugins/maps/public/reducers/ui'; -import { - cancelRequest, - registerCancelCallback, - unregisterCancelCallback, - getEventHandlers, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; -import { updateFlyout } from '../actions/ui_actions'; -import { - FEATURE_ID_PROPERTY_NAME, - LAYER_TYPE, - SOURCE_DATA_ID_ORIGIN, -} from '../../common/constants'; - -import { - SET_SELECTED_LAYER, - SET_TRANSIENT_LAYER, - UPDATE_LAYER_ORDER, - ADD_LAYER, - SET_LAYER_ERROR_STATUS, - ADD_WAITING_FOR_MAP_READY_LAYER, - CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST, - REMOVE_LAYER, - SET_LAYER_VISIBILITY, - MAP_EXTENT_CHANGED, - MAP_READY, - MAP_DESTROYED, - LAYER_DATA_LOAD_STARTED, - LAYER_DATA_LOAD_ENDED, - LAYER_DATA_LOAD_ERROR, - UPDATE_SOURCE_DATA_REQUEST, - SET_JOINS, - SET_QUERY, - TRIGGER_REFRESH_TIMER, - UPDATE_LAYER_PROP, - UPDATE_LAYER_STYLE, - SET_LAYER_STYLE_META, - UPDATE_SOURCE_PROP, - SET_REFRESH_CONFIG, - SET_MOUSE_COORDINATES, - CLEAR_MOUSE_COORDINATES, - SET_GOTO, - CLEAR_GOTO, - TRACK_CURRENT_LAYER_STATE, - ROLLBACK_TO_TRACKED_LAYER_STATE, - REMOVE_TRACKED_LAYER_STATE, - SET_OPEN_TOOLTIPS, - UPDATE_DRAW_STATE, - SET_SCROLL_ZOOM, - SET_MAP_INIT_ERROR, - SET_INTERACTIVE, - DISABLE_TOOLTIP_CONTROL, - HIDE_TOOLBAR_OVERLAY, - HIDE_LAYER_CONTROL, - HIDE_VIEW_CONTROL, - SET_WAITING_FOR_READY_HIDDEN_LAYERS, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../plugins/maps/public/actions/map_actions'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export * from '../../../../../plugins/maps/public/actions/map_actions'; - -function getLayerLoadingCallbacks(dispatch, getState, layerId) { - return { - startLoading: (dataId, requestToken, meta) => - dispatch(startDataLoad(layerId, dataId, requestToken, meta)), - stopLoading: (dataId, requestToken, data, meta) => - dispatch(endDataLoad(layerId, dataId, requestToken, data, meta)), - onLoadError: (dataId, requestToken, errorMessage) => - dispatch(onDataLoadError(layerId, dataId, requestToken, errorMessage)), - updateSourceData: newData => { - dispatch(updateSourceDataRequest(layerId, newData)); - }, - isRequestStillActive: (dataId, requestToken) => { - const dataRequest = getDataRequestDescriptor(getState(), layerId, dataId); - if (!dataRequest) { - return false; - } - return dataRequest.dataRequestToken === requestToken; - }, - registerCancelCallback: (requestToken, callback) => - dispatch(registerCancelCallback(requestToken, callback)), - }; -} - -function getLayerById(layerId, state) { - return getLayerList(state).find(layer => { - return layerId === layer.getId(); - }); -} - -async function syncDataForAllLayers(dispatch, getState, dataFilters) { - const state = getState(); - const layerList = getLayerList(state); - const syncs = layerList.map(layer => { - const loadingFunctions = getLayerLoadingCallbacks(dispatch, getState, layer.getId()); - return layer.syncData({ ...loadingFunctions, dataFilters }); - }); - await Promise.all(syncs); -} - -export function cancelAllInFlightRequests() { - return (dispatch, getState) => { - getLayerList(getState()).forEach(layer => { - dispatch(clearDataRequests(layer)); - }); - }; -} - -function clearDataRequests(layer) { - return dispatch => { - layer.getInFlightRequestTokens().forEach(requestToken => { - dispatch(cancelRequest(requestToken)); - }); - dispatch({ - type: UPDATE_LAYER_PROP, - id: layer.getId(), - propName: '__dataRequests', - newValue: [], - }); - }; -} - -export function setMapInitError(errorMessage) { - return { - type: SET_MAP_INIT_ERROR, - errorMessage, - }; -} - -export function trackCurrentLayerState(layerId) { - return { - type: TRACK_CURRENT_LAYER_STATE, - layerId: layerId, - }; -} - -export function rollbackToTrackedLayerStateForSelectedLayer() { - return async (dispatch, getState) => { - const layerId = getSelectedLayerId(getState()); - await dispatch({ - type: ROLLBACK_TO_TRACKED_LAYER_STATE, - layerId: layerId, - }); - - // Ensure updateStyleMeta is triggered - // syncDataForLayer may not trigger endDataLoad if no re-fetch is required - dispatch(updateStyleMeta(layerId)); - - dispatch(syncDataForLayer(layerId)); - }; -} - -export function removeTrackedLayerStateForSelectedLayer() { - return (dispatch, getState) => { - const layerId = getSelectedLayerId(getState()); - dispatch({ - type: REMOVE_TRACKED_LAYER_STATE, - layerId: layerId, - }); - }; -} - -export function replaceLayerList(newLayerList) { - return (dispatch, getState) => { - const isMapReady = getMapReady(getState()); - if (!isMapReady) { - dispatch({ - type: CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST, - }); - } else { - getLayerListRaw(getState()).forEach(({ id }) => { - dispatch(removeLayerFromLayerList(id)); - }); - } - - newLayerList.forEach(layerDescriptor => { - dispatch(addLayer(layerDescriptor)); - }); - }; -} - -export function cloneLayer(layerId) { - return async (dispatch, getState) => { - const layer = getLayerById(layerId, getState()); - if (!layer) { - return; - } - - const clonedDescriptor = await layer.cloneDescriptor(); - dispatch(addLayer(clonedDescriptor)); - }; -} - -export function addLayer(layerDescriptor) { - return (dispatch, getState) => { - const isMapReady = getMapReady(getState()); - if (!isMapReady) { - dispatch({ - type: ADD_WAITING_FOR_MAP_READY_LAYER, - layer: layerDescriptor, - }); - return; - } - - dispatch({ - type: ADD_LAYER, - layer: layerDescriptor, - }); - dispatch(syncDataForLayer(layerDescriptor.id)); - }; -} - -// Do not use when rendering a map. Method exists to enable selectors for getLayerList when -// rendering is not needed. -export function addLayerWithoutDataSync(layerDescriptor) { - return { - type: ADD_LAYER, - layer: layerDescriptor, - }; -} - -function setLayerDataLoadErrorStatus(layerId, errorMessage) { - return dispatch => { - dispatch({ - type: SET_LAYER_ERROR_STATUS, - isInErrorState: errorMessage !== null, - layerId, - errorMessage, - }); - }; -} - -export function cleanTooltipStateForLayer(layerId, layerFeatures = []) { - return (dispatch, getState) => { - let featuresRemoved = false; - const openTooltips = getOpenTooltips(getState()) - .map(tooltipState => { - const nextFeatures = tooltipState.features.filter(tooltipFeature => { - if (tooltipFeature.layerId !== layerId) { - // feature from another layer, keep it - return true; - } - - // Keep feature if it is still in layer - return layerFeatures.some(layerFeature => { - return layerFeature.properties[FEATURE_ID_PROPERTY_NAME] === tooltipFeature.id; - }); - }); - - if (tooltipState.features.length !== nextFeatures.length) { - featuresRemoved = true; - } - - return { ...tooltipState, features: nextFeatures }; - }) - .filter(tooltipState => { - return tooltipState.features.length > 0; - }); - - if (featuresRemoved) { - dispatch({ - type: SET_OPEN_TOOLTIPS, - openTooltips, - }); - } - }; -} - -export function setLayerVisibility(layerId, makeVisible) { - return async (dispatch, getState) => { - //if the current-state is invisible, we also want to sync data - //e.g. if a layer was invisible at start-up, it won't have any data loaded - const layer = getLayerById(layerId, getState()); - - // If the layer visibility is already what we want it to be, do nothing - if (!layer || layer.isVisible() === makeVisible) { - return; - } - - if (!makeVisible) { - dispatch(cleanTooltipStateForLayer(layerId)); - } - - await dispatch({ - type: SET_LAYER_VISIBILITY, - layerId, - visibility: makeVisible, - }); - if (makeVisible) { - dispatch(syncDataForLayer(layerId)); - } - }; -} - -export function toggleLayerVisible(layerId) { - return async (dispatch, getState) => { - const layer = getLayerById(layerId, getState()); - if (!layer) { - return; - } - const makeVisible = !layer.isVisible(); - - dispatch(setLayerVisibility(layerId, makeVisible)); - }; -} - -export function setSelectedLayer(layerId) { - return async (dispatch, getState) => { - const oldSelectedLayer = getSelectedLayerId(getState()); - if (oldSelectedLayer) { - await dispatch(rollbackToTrackedLayerStateForSelectedLayer()); - } - if (layerId) { - dispatch(trackCurrentLayerState(layerId)); - } - dispatch({ - type: SET_SELECTED_LAYER, - selectedLayerId: layerId, - }); - }; -} - -export function removeTransientLayer() { - return async (dispatch, getState) => { - const transientLayerId = getTransientLayerId(getState()); - if (transientLayerId) { - await dispatch(removeLayerFromLayerList(transientLayerId)); - await dispatch(setTransientLayer(null)); - } - }; -} - -export function setTransientLayer(layerId) { - return { - type: SET_TRANSIENT_LAYER, - transientLayerId: layerId, - }; -} - -export function clearTransientLayerStateAndCloseFlyout() { - return async dispatch => { - await dispatch(updateFlyout(FLYOUT_STATE.NONE)); - await dispatch(setSelectedLayer(null)); - await dispatch(removeTransientLayer()); - }; -} - -export function updateLayerOrder(newLayerOrder) { - return { - type: UPDATE_LAYER_ORDER, - newLayerOrder, - }; -} - -export function mapReady() { - return (dispatch, getState) => { - dispatch({ - type: MAP_READY, - }); - - getWaitingForMapReadyLayerListRaw(getState()).forEach(layerDescriptor => { - dispatch(addLayer(layerDescriptor)); - }); - - dispatch({ - type: CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST, - }); - }; -} - -export function mapDestroyed() { - return { - type: MAP_DESTROYED, - }; -} - -export function mapExtentChanged(newMapConstants) { - return async (dispatch, getState) => { - const state = getState(); - const dataFilters = getDataFilters(state); - const { extent, zoom: newZoom } = newMapConstants; - const { buffer, zoom: currentZoom } = dataFilters; - - if (extent) { - let doesBufferContainExtent = false; - if (buffer) { - const bufferGeometry = turf.bboxPolygon([ - buffer.minLon, - buffer.minLat, - buffer.maxLon, - buffer.maxLat, - ]); - const extentGeometry = turf.bboxPolygon([ - extent.minLon, - extent.minLat, - extent.maxLon, - extent.maxLat, - ]); - - doesBufferContainExtent = turfBooleanContains(bufferGeometry, extentGeometry); - } - - if (!doesBufferContainExtent || currentZoom !== newZoom) { - const scaleFactor = 0.5; // TODO put scale factor in store and fetch with selector - const width = extent.maxLon - extent.minLon; - const height = extent.maxLat - extent.minLat; - dataFilters.buffer = { - minLon: extent.minLon - width * scaleFactor, - minLat: extent.minLat - height * scaleFactor, - maxLon: extent.maxLon + width * scaleFactor, - maxLat: extent.maxLat + height * scaleFactor, - }; - } - } - - dispatch({ - type: MAP_EXTENT_CHANGED, - mapState: { - ...dataFilters, - ...newMapConstants, - }, - }); - const newDataFilters = { ...dataFilters, ...newMapConstants }; - await syncDataForAllLayers(dispatch, getState, newDataFilters); - }; -} - -export function closeOnClickTooltip(tooltipId) { - return (dispatch, getState) => { - dispatch({ - type: SET_OPEN_TOOLTIPS, - openTooltips: getOpenTooltips(getState()).filter(({ id }) => { - return tooltipId !== id; - }), - }); - }; -} - -export function openOnClickTooltip(tooltipState) { - return (dispatch, getState) => { - const openTooltips = getOpenTooltips(getState()).filter(({ features, location, isLocked }) => { - return ( - isLocked && - !_.isEqual(location, tooltipState.location) && - !_.isEqual(features, tooltipState.features) - ); - }); - - openTooltips.push({ - ...tooltipState, - isLocked: true, - id: uuid(), - }); - - dispatch({ - type: SET_OPEN_TOOLTIPS, - openTooltips, - }); - }; -} - -export function closeOnHoverTooltip() { - return (dispatch, getState) => { - if (getOpenTooltips(getState()).length) { - dispatch({ - type: SET_OPEN_TOOLTIPS, - openTooltips: [], - }); - } - }; -} - -export function openOnHoverTooltip(tooltipState) { - return { - type: SET_OPEN_TOOLTIPS, - openTooltips: [ - { - ...tooltipState, - isLocked: false, - id: uuid(), - }, - ], - }; -} - -export function setMouseCoordinates({ lat, lon }) { - let safeLon = lon; - if (lon > 180) { - const overlapWestOfDateLine = lon - 180; - safeLon = -180 + overlapWestOfDateLine; - } else if (lon < -180) { - const overlapEastOfDateLine = Math.abs(lon) - 180; - safeLon = 180 - overlapEastOfDateLine; - } - - return { - type: SET_MOUSE_COORDINATES, - lat, - lon: safeLon, - }; -} - -export function clearMouseCoordinates() { - return { type: CLEAR_MOUSE_COORDINATES }; -} - -export function disableScrollZoom() { - return { type: SET_SCROLL_ZOOM, scrollZoom: false }; -} - -export function fitToLayerExtent(layerId) { - return async function(dispatch, getState) { - const targetLayer = getLayerById(layerId, getState()); - - if (targetLayer) { - const dataFilters = getDataFilters(getState()); - const bounds = await targetLayer.getBounds(dataFilters); - if (bounds) { - await dispatch(setGotoWithBounds(bounds)); - } - } - }; -} - -export function setGotoWithBounds(bounds) { - return { - type: SET_GOTO, - bounds: bounds, - }; -} - -export function setGotoWithCenter({ lat, lon, zoom }) { - return { - type: SET_GOTO, - center: { lat, lon, zoom }, - }; -} - -export function clearGoto() { - return { type: CLEAR_GOTO }; -} - -export function startDataLoad(layerId, dataId, requestToken, meta = {}) { - return (dispatch, getState) => { - const layer = getLayerById(layerId, getState()); - if (layer) { - dispatch(cancelRequest(layer.getPrevRequestToken(dataId))); - } - - const eventHandlers = getEventHandlers(getState()); - if (eventHandlers && eventHandlers.onDataLoad) { - eventHandlers.onDataLoad({ - layerId, - dataId, - }); - } - - dispatch({ - meta, - type: LAYER_DATA_LOAD_STARTED, - layerId, - dataId, - requestToken, - }); - }; -} - -export function updateSourceDataRequest(layerId, newData) { - return dispatch => { - dispatch({ - type: UPDATE_SOURCE_DATA_REQUEST, - dataId: SOURCE_DATA_ID_ORIGIN, - layerId, - newData, - }); - - dispatch(updateStyleMeta(layerId)); - }; -} - -export function endDataLoad(layerId, dataId, requestToken, data, meta) { - return async (dispatch, getState) => { - dispatch(unregisterCancelCallback(requestToken)); - - const features = data && data.features ? data.features : []; - - const eventHandlers = getEventHandlers(getState()); - if (eventHandlers && eventHandlers.onDataLoadEnd) { - const layer = getLayerById(layerId, getState()); - const resultMeta = {}; - if (layer && layer.getType() === LAYER_TYPE.VECTOR) { - resultMeta.featuresCount = features.length; - } - - eventHandlers.onDataLoadEnd({ - layerId, - dataId, - resultMeta, - }); - } - - dispatch(cleanTooltipStateForLayer(layerId, features)); - dispatch({ - type: LAYER_DATA_LOAD_ENDED, - layerId, - dataId, - data, - meta, - requestToken, - }); - - //Clear any data-load errors when there is a succesful data return. - //Co this on end-data-load iso at start-data-load to avoid blipping the error status between true/false. - //This avoids jitter in the warning icon of the TOC when the requests continues to return errors. - dispatch(setLayerDataLoadErrorStatus(layerId, null)); - - dispatch(updateStyleMeta(layerId)); - }; -} - -export function onDataLoadError(layerId, dataId, requestToken, errorMessage) { - return async (dispatch, getState) => { - dispatch(unregisterCancelCallback(requestToken)); - - const eventHandlers = getEventHandlers(getState()); - if (eventHandlers && eventHandlers.onDataLoadError) { - eventHandlers.onDataLoadError({ - layerId, - dataId, - errorMessage, - }); - } - - dispatch(cleanTooltipStateForLayer(layerId)); - dispatch({ - type: LAYER_DATA_LOAD_ERROR, - data: null, - layerId, - dataId, - requestToken, - }); - - dispatch(setLayerDataLoadErrorStatus(layerId, errorMessage)); - }; -} - -export function updateSourceProp(layerId, propName, value, newLayerType) { - return async dispatch => { - dispatch({ - type: UPDATE_SOURCE_PROP, - layerId, - propName, - value, - }); - if (newLayerType) { - dispatch(updateLayerType(layerId, newLayerType)); - } - await dispatch(clearMissingStyleProperties(layerId)); - dispatch(syncDataForLayer(layerId)); - }; -} - -function updateLayerType(layerId, newLayerType) { - return (dispatch, getState) => { - const layer = getLayerById(layerId, getState()); - if (!layer || layer.getType() === newLayerType) { - return; - } - dispatch(clearDataRequests(layer)); - dispatch({ - type: UPDATE_LAYER_PROP, - id: layerId, - propName: 'type', - newValue: newLayerType, - }); - }; -} - -export function syncDataForLayer(layerId) { - return async (dispatch, getState) => { - const targetLayer = getLayerById(layerId, getState()); - if (targetLayer) { - const dataFilters = getDataFilters(getState()); - const loadingFunctions = getLayerLoadingCallbacks(dispatch, getState, layerId); - await targetLayer.syncData({ - ...loadingFunctions, - dataFilters, - }); - } - }; -} - -export function updateLayerLabel(id, newLabel) { - return { - type: UPDATE_LAYER_PROP, - id, - propName: 'label', - newValue: newLabel, - }; -} - -export function updateLayerMinZoom(id, minZoom) { - return { - type: UPDATE_LAYER_PROP, - id, - propName: 'minZoom', - newValue: minZoom, - }; -} - -export function updateLayerMaxZoom(id, maxZoom) { - return { - type: UPDATE_LAYER_PROP, - id, - propName: 'maxZoom', - newValue: maxZoom, - }; -} - -export function updateLayerAlpha(id, alpha) { - return { - type: UPDATE_LAYER_PROP, - id, - propName: 'alpha', - newValue: alpha, - }; -} - -export function setLayerQuery(id, query) { - return dispatch => { - dispatch({ - type: UPDATE_LAYER_PROP, - id, - propName: 'query', - newValue: query, - }); - - dispatch(syncDataForLayer(id)); - }; -} - -export function removeSelectedLayer() { - return (dispatch, getState) => { - const state = getState(); - const layerId = getSelectedLayerId(state); - dispatch(removeLayer(layerId)); - }; -} - -export function removeLayer(layerId) { - return async (dispatch, getState) => { - const state = getState(); - const selectedLayerId = getSelectedLayerId(state); - if (layerId === selectedLayerId) { - dispatch(updateFlyout(FLYOUT_STATE.NONE)); - await dispatch(setSelectedLayer(null)); - } - dispatch(removeLayerFromLayerList(layerId)); - }; -} - -function removeLayerFromLayerList(layerId) { - return (dispatch, getState) => { - const layerGettingRemoved = getLayerById(layerId, getState()); - if (!layerGettingRemoved) { - return; - } - - layerGettingRemoved.getInFlightRequestTokens().forEach(requestToken => { - dispatch(cancelRequest(requestToken)); - }); - dispatch(cleanTooltipStateForLayer(layerId)); - layerGettingRemoved.destroy(); - dispatch({ - type: REMOVE_LAYER, - id: layerId, - }); - }; -} - -export function setQuery({ query, timeFilters, filters = [], refresh = false }) { - function generateQueryTimestamp() { - return new Date().toISOString(); - } - return async (dispatch, getState) => { - const prevQuery = getQuery(getState()); - const prevTriggeredAt = - prevQuery && prevQuery.queryLastTriggeredAt - ? prevQuery.queryLastTriggeredAt - : generateQueryTimestamp(); - - dispatch({ - type: SET_QUERY, - timeFilters, - query: { - ...query, - // ensure query changes to trigger re-fetch when "Refresh" clicked - queryLastTriggeredAt: refresh ? generateQueryTimestamp() : prevTriggeredAt, - }, - filters, - }); - - const dataFilters = getDataFilters(getState()); - await syncDataForAllLayers(dispatch, getState, dataFilters); - }; -} - -export function setRefreshConfig({ isPaused, interval }) { - return { - type: SET_REFRESH_CONFIG, - isPaused, - interval, - }; -} - -export function triggerRefreshTimer() { - return async (dispatch, getState) => { - dispatch({ - type: TRIGGER_REFRESH_TIMER, - }); - - const dataFilters = getDataFilters(getState()); - await syncDataForAllLayers(dispatch, getState, dataFilters); - }; -} - -export function clearMissingStyleProperties(layerId) { - return async (dispatch, getState) => { - const targetLayer = getLayerById(layerId, getState()); - if (!targetLayer) { - return; - } - - const style = targetLayer.getCurrentStyle(); - if (!style) { - return; - } - - const nextFields = await targetLayer.getFields(); //take into account all fields, since labels can be driven by any field (source or join) - const { hasChanges, nextStyleDescriptor } = style.getDescriptorWithMissingStylePropsRemoved( - nextFields - ); - if (hasChanges) { - dispatch(updateLayerStyle(layerId, nextStyleDescriptor)); - } - }; -} - -export function updateLayerStyle(layerId, styleDescriptor) { - return dispatch => { - dispatch({ - type: UPDATE_LAYER_STYLE, - layerId, - style: { - ...styleDescriptor, - }, - }); - - // Ensure updateStyleMeta is triggered - // syncDataForLayer may not trigger endDataLoad if no re-fetch is required - dispatch(updateStyleMeta(layerId)); - - // Style update may require re-fetch, for example ES search may need to retrieve field used for dynamic styling - dispatch(syncDataForLayer(layerId)); - }; -} - -export function updateStyleMeta(layerId) { - return async (dispatch, getState) => { - const layer = getLayerById(layerId, getState()); - if (!layer) { - return; - } - const sourceDataRequest = layer.getSourceDataRequest(); - const style = layer.getCurrentStyle(); - if (!style || !sourceDataRequest) { - return; - } - const styleMeta = await style.pluckStyleMetaFromSourceDataRequest(sourceDataRequest); - dispatch({ - type: SET_LAYER_STYLE_META, - layerId, - styleMeta, - }); - }; -} - -export function updateLayerStyleForSelectedLayer(styleDescriptor) { - return (dispatch, getState) => { - const selectedLayerId = getSelectedLayerId(getState()); - if (!selectedLayerId) { - return; - } - dispatch(updateLayerStyle(selectedLayerId, styleDescriptor)); - }; -} - -export function setJoinsForLayer(layer, joins) { - return async dispatch => { - await dispatch({ - type: SET_JOINS, - layer: layer, - joins: joins, - }); - - await dispatch(clearMissingStyleProperties(layer.getId())); - dispatch(syncDataForLayer(layer.getId())); - }; -} - -export function updateDrawState(drawState) { - return dispatch => { - if (drawState !== null) { - dispatch({ type: SET_OPEN_TOOLTIPS, openTooltips: [] }); // tooltips just get in the way - } - dispatch({ - type: UPDATE_DRAW_STATE, - drawState: drawState, - }); - }; -} - -export function disableInteractive() { - return { type: SET_INTERACTIVE, disableInteractive: true }; -} - -export function disableTooltipControl() { - return { type: DISABLE_TOOLTIP_CONTROL, disableTooltipControl: true }; -} - -export function hideToolbarOverlay() { - return { type: HIDE_TOOLBAR_OVERLAY, hideToolbarOverlay: true }; -} - -export function hideLayerControl() { - return { type: HIDE_LAYER_CONTROL, hideLayerControl: true }; -} -export function hideViewControl() { - return { type: HIDE_VIEW_CONTROL, hideViewControl: true }; -} - -export function setHiddenLayers(hiddenLayerIds) { - return (dispatch, getState) => { - const isMapReady = getMapReady(getState()); - - if (!isMapReady) { - dispatch({ type: SET_WAITING_FOR_READY_HIDDEN_LAYERS, hiddenLayerIds }); - } else { - getLayerListRaw(getState()).forEach(layer => - dispatch(setLayerVisibility(layer.id, !hiddenLayerIds.includes(layer.id))) - ); - } - }; -} diff --git a/x-pack/legacy/plugins/maps/public/actions/ui_actions.js b/x-pack/legacy/plugins/maps/public/actions/ui_actions.js deleted file mode 100644 index 33ab2fd74122a6..00000000000000 --- a/x-pack/legacy/plugins/maps/public/actions/ui_actions.js +++ /dev/null @@ -1,91 +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 { - UPDATE_FLYOUT, - CLOSE_SET_VIEW, - OPEN_SET_VIEW, - SET_IS_LAYER_TOC_OPEN, - SET_FULL_SCREEN, - SET_READ_ONLY, - SET_OPEN_TOC_DETAILS, - SHOW_TOC_DETAILS, - HIDE_TOC_DETAILS, - UPDATE_INDEXING_STAGE, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../plugins/maps/public/actions/ui_actions'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export * from '../../../../../plugins/maps/public/actions/ui_actions'; - -export function exitFullScreen() { - return { - type: SET_FULL_SCREEN, - isFullScreen: false, - }; -} - -export function updateFlyout(display) { - return { - type: UPDATE_FLYOUT, - display, - }; -} -export function closeSetView() { - return { - type: CLOSE_SET_VIEW, - }; -} -export function openSetView() { - return { - type: OPEN_SET_VIEW, - }; -} -export function setIsLayerTOCOpen(isLayerTOCOpen) { - return { - type: SET_IS_LAYER_TOC_OPEN, - isLayerTOCOpen, - }; -} -export function enableFullScreen() { - return { - type: SET_FULL_SCREEN, - isFullScreen: true, - }; -} -export function setReadOnly(isReadOnly) { - return { - type: SET_READ_ONLY, - isReadOnly, - }; -} - -export function setOpenTOCDetails(layerIds) { - return { - type: SET_OPEN_TOC_DETAILS, - layerIds, - }; -} - -export function showTOCDetails(layerId) { - return { - type: SHOW_TOC_DETAILS, - layerId, - }; -} - -export function hideTOCDetails(layerId) { - return { - type: HIDE_TOC_DETAILS, - layerId, - }; -} - -export function updateIndexingStage(stage) { - return { - type: UPDATE_INDEXING_STAGE, - stage, - }; -} diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js deleted file mode 100644 index 686259aeaaba44..00000000000000 --- a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js +++ /dev/null @@ -1,54 +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 _ from 'lodash'; -// Import each layer type, even those not used, to init in registry -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import '../../../../../plugins/maps/public/layers/sources/wms_source'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import '../../../../../plugins/maps/public/layers/sources/ems_file_source'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import '../../../../../plugins/maps/public/layers/sources/es_search_source'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import '../../../../../plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import '../../../../../plugins/maps/public/layers/sources/kibana_regionmap_source'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import '../../../../../plugins/maps/public/layers/sources/es_geo_grid_source'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import '../../../../../plugins/maps/public/layers/sources/xyz_tms_source'; - -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { KibanaTilemapSource } from '../../../../../plugins/maps/public/layers/sources/kibana_tilemap_source'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { EMSTMSSource } from '../../../../../plugins/maps/public/layers/sources/ems_tms_source'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getInjectedVarFunc } from '../../../../../plugins/maps/public/kibana_services'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getKibanaTileMap } from '../../../../../plugins/maps/public/meta'; - -export function getInitialLayers(layerListJSON, initialLayers = []) { - if (layerListJSON) { - return JSON.parse(layerListJSON); - } - - const tilemapSourceFromKibana = getKibanaTileMap(); - if (_.get(tilemapSourceFromKibana, 'url')) { - const sourceDescriptor = KibanaTilemapSource.createDescriptor(); - const source = new KibanaTilemapSource(sourceDescriptor); - const layer = source.createDefaultLayer(); - return [layer.toLayerDescriptor(), ...initialLayers]; - } - - const isEmsEnabled = getInjectedVarFunc()('isEmsEnabled', true); - if (isEmsEnabled) { - const descriptor = EMSTMSSource.createDescriptor({ isAutoSelect: true }); - const source = new EMSTMSSource(descriptor); - const layer = source.createDefaultLayer(); - return [layer.toLayerDescriptor(), ...initialLayers]; - } - - return initialLayers; -} diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js index 6bc8a4d0be5aca..9522fd12ad37d1 100644 --- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js +++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js @@ -28,8 +28,10 @@ import { // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { createMapStore } from '../../../../../plugins/maps/public/reducers/store'; import { Provider } from 'react-redux'; -import { GisMap } from '../connected_components/gis_map'; -import { addHelpMenuToAppChrome } from '../help_menu_util'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { GisMap } from '../../../../../plugins/maps/public/connected_components/gis_map'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { addHelpMenuToAppChrome } from '../../../../../plugins/maps/public/help_menu_util'; import { setSelectedLayer, setRefreshConfig, @@ -37,7 +39,8 @@ import { replaceLayerList, setQuery, clearTransientLayerStateAndCloseFlyout, -} from '../actions/map_actions'; + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../plugins/maps/public/actions/map_actions'; import { DEFAULT_IS_LAYER_TOC_OPEN, FLYOUT_STATE, @@ -49,22 +52,29 @@ import { setReadOnly, setIsLayerTOCOpen, setOpenTOCDetails, -} from '../actions/ui_actions'; -import { getIsFullScreen } from '../selectors/ui_selectors'; + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../plugins/maps/public/actions/ui_actions'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getIsFullScreen } from '../../../../../plugins/maps/public/selectors/ui_selectors'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { copyPersistentState } from '../../../../../plugins/maps/public/reducers/util'; import { getQueryableUniqueIndexPatternIds, hasDirtyState, getLayerListRaw, -} from '../selectors/map_selectors'; + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../plugins/maps/public/selectors/map_selectors'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { getInspectorAdapters } from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; -import { getInitialLayers } from './get_initial_layers'; -import { getInitialQuery } from './get_initial_query'; -import { getInitialTimeFilters } from './get_initial_time_filters'; -import { getInitialRefreshConfig } from './get_initial_refresh_config'; -import { MAP_SAVED_OBJECT_TYPE, MAP_APP_PATH } from '../../common/constants'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getInitialLayers } from '../../../../../plugins/maps/public/angular/get_initial_layers'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getInitialQuery } from '../../../../../plugins/maps/public/angular/get_initial_query'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getInitialTimeFilters } from '../../../../../plugins/maps/public/angular/get_initial_time_filters'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getInitialRefreshConfig } from '../../../../../plugins/maps/public/angular/get_initial_refresh_config'; +import { MAP_SAVED_OBJECT_TYPE, MAP_APP_PATH } from '../../../../../plugins/maps/common/constants'; import { npSetup, npStart } from 'ui/new_platform'; import { esFilters } from '../../../../../../src/plugins/data/public'; import { diff --git a/x-pack/legacy/plugins/maps/public/components/_index.scss b/x-pack/legacy/plugins/maps/public/components/_index.scss deleted file mode 100644 index 0b327194424244..00000000000000 --- a/x-pack/legacy/plugins/maps/public/components/_index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import './metric_editors'; -@import './geometry_filter'; -@import './tooltip_selector'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/_index.scss b/x-pack/legacy/plugins/maps/public/connected_components/_index.scss deleted file mode 100644 index 99a2e222ea6c14..00000000000000 --- a/x-pack/legacy/plugins/maps/public/connected_components/_index.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import './gis_map/gis_map'; -@import './layer_addpanel/source_select/index'; -@import './layer_panel/index'; -@import './widget_overlay/index'; -@import './toolbar_overlay/index'; -@import './map/features_tooltip/index'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_index.scss b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_index.scss deleted file mode 100644 index 7fe1396fcca16e..00000000000000 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './source_select'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/_index.scss b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/_index.scss deleted file mode 100644 index fd074edf032fa0..00000000000000 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/_index.scss +++ /dev/null @@ -1,4 +0,0 @@ -@import './layer_panel'; -@import './filter_editor/filter_editor'; -@import './join_editor/resources/join'; -@import './style_settings/style_settings'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.d.ts b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.d.ts deleted file mode 100644 index cf4fdc7be70c68..00000000000000 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.d.ts +++ /dev/null @@ -1,8 +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. - */ -/* eslint-disable @typescript-eslint/consistent-type-definitions */ - -export * from '../../../../../../plugins/maps/public/connected_components/layer_panel/view'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/utils.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/utils.js deleted file mode 100644 index a1d1341b7c4f7e..00000000000000 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/utils.js +++ /dev/null @@ -1,107 +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 _ from 'lodash'; -import { - loadSpriteSheetImageData, - addSpriteSheetToMapFromImageData, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../../plugins/maps/public/connected_components/map/mb/utils'; - -export { loadSpriteSheetImageData, addSpriteSheetToMapFromImageData }; - -export function removeOrphanedSourcesAndLayers(mbMap, layerList) { - const mbStyle = mbMap.getStyle(); - - const mbLayerIdsToRemove = []; - mbStyle.layers.forEach(mbLayer => { - const layer = layerList.find(layer => { - return layer.ownsMbLayerId(mbLayer.id); - }); - if (!layer) { - mbLayerIdsToRemove.push(mbLayer.id); - } - }); - mbLayerIdsToRemove.forEach(mbLayerId => mbMap.removeLayer(mbLayerId)); - - const mbSourcesToRemove = []; - for (const mbSourceId in mbStyle.sources) { - if (mbStyle.sources.hasOwnProperty(mbSourceId)) { - const layer = layerList.find(layer => { - return layer.ownsMbSourceId(mbSourceId); - }); - if (!layer) { - mbSourcesToRemove.push(mbSourceId); - } - } - } - mbSourcesToRemove.forEach(mbSourceId => mbMap.removeSource(mbSourceId)); -} - -/** - * This is function assumes only a single layer moved in the layerList, compared to mbMap - * It is optimized to minimize the amount of mbMap.moveLayer calls. - * @param mbMap - * @param layerList - */ -export function syncLayerOrderForSingleLayer(mbMap, layerList) { - if (!layerList || layerList.length === 0) { - return; - } - - const mbLayers = mbMap.getStyle().layers.slice(); - const layerIds = mbLayers.map(mbLayer => { - const layer = layerList.find(layer => layer.ownsMbLayerId(mbLayer.id)); - return layer.getId(); - }); - - const currentLayerOrderLayerIds = _.uniq(layerIds); - - const newLayerOrderLayerIdsUnfiltered = layerList.map(l => l.getId()); - const newLayerOrderLayerIds = newLayerOrderLayerIdsUnfiltered.filter(layerId => - currentLayerOrderLayerIds.includes(layerId) - ); - - let netPos = 0; - let netNeg = 0; - const movementArr = currentLayerOrderLayerIds.reduce((accu, id, idx) => { - const movement = newLayerOrderLayerIds.findIndex(newOId => newOId === id) - idx; - movement > 0 ? netPos++ : movement < 0 && netNeg++; - accu.push({ id, movement }); - return accu; - }, []); - if (netPos === 0 && netNeg === 0) { - return; - } - const movedLayerId = - (netPos >= netNeg && movementArr.find(l => l.movement < 0).id) || - (netPos < netNeg && movementArr.find(l => l.movement > 0).id); - const nextLayerIdx = newLayerOrderLayerIds.findIndex(layerId => layerId === movedLayerId) + 1; - - let nextMbLayerId; - if (nextLayerIdx === newLayerOrderLayerIds.length) { - nextMbLayerId = null; - } else { - const foundLayer = mbLayers.find(({ id: mbLayerId }) => { - const layerId = newLayerOrderLayerIds[nextLayerIdx]; - const layer = layerList.find(layer => layer.getId() === layerId); - return layer.ownsMbLayerId(mbLayerId); - }); - nextMbLayerId = foundLayer.id; - } - - const movedLayer = layerList.find(layer => layer.getId() === movedLayerId); - mbLayers.forEach(({ id: mbLayerId }) => { - if (movedLayer.ownsMbLayerId(mbLayerId)) { - mbMap.moveLayer(mbLayerId, nextMbLayerId); - } - }); -} - -export async function addSpritesheetToMap(json, imgUrl, mbMap) { - const imgData = await loadSpriteSheetImageData(imgUrl); - addSpriteSheetToMapFromImageData(json, imgData, mbMap); -} diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/_index.scss b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/_index.scss deleted file mode 100644 index cc1ab35039dac2..00000000000000 --- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/_index.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import './mixins'; - -@import './widget_overlay'; -@import './attribution_control/attribution_control'; -@import './layer_control/index'; -@import './view_control/view_control'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss deleted file mode 100644 index 761ef9d17b4c2d..00000000000000 --- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import './layer_control'; -@import './layer_toc/toc_entry/toc_entry'; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts index 96c3baf634a834..90b17412377f52 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -4,153 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; -import { i18n } from '@kbn/i18n'; +/* + Maintain legacy embeddable legacy present while apps switch over + */ + import { npSetup, npStart } from 'ui/new_platform'; -import { IIndexPattern } from 'src/plugins/data/public'; -// @ts-ignore -import { getMapsSavedObjectLoader } from '../angular/services/gis_map_saved_object_loader'; -import { MapEmbeddable, MapEmbeddableInput } from './map_embeddable'; import { - getIndexPatternService, - getHttp, - getMapsCapabilities, + bindSetupCoreAndPlugins, + bindStartCoreAndPlugins, // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../plugins/maps/public/kibana_services'; -import { - EmbeddableFactoryDefinition, - IContainer, -} from '../../../../../../src/plugins/embeddable/public'; - -import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants'; +} from '../../../../../plugins/maps/public/plugin'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { createMapStore } from '../../../../../plugins/maps/public/reducers/store'; -import { addLayerWithoutDataSync } from '../actions/map_actions'; -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'; -// @ts-ignore -import { - bindSetupCoreAndPlugins as bindNpSetupCoreAndPlugins, - bindStartCoreAndPlugins as bindNpStartCoreAndPlugins, -} from '../../../../../plugins/maps/public/plugin'; // eslint-disable-line @kbn/eslint/no-restricted-paths - -export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { - type = MAP_SAVED_OBJECT_TYPE; - savedObjectMetaData = { - name: i18n.translate('xpack.maps.mapSavedObjectLabel', { - defaultMessage: 'Map', - }), - type: MAP_SAVED_OBJECT_TYPE, - getIconForSavedObject: () => APP_ICON, - }; - constructor() { - // Init required services. Necessary while in legacy - bindNpSetupCoreAndPlugins(npSetup.core, npSetup.plugins); - bindNpStartCoreAndPlugins(npStart.core, npStart.plugins); - } - - async isEditable() { - return getMapsCapabilities().save as boolean; - } - - // Not supported yet for maps types. - canCreateNew() { - return false; - } - - getDisplayName() { - return i18n.translate('xpack.maps.embeddableDisplayName', { - defaultMessage: 'map', - }); - } - - async _getIndexPatterns(layerList: unknown[]): Promise { - // Need to extract layerList from store to get queryable index pattern ids - const store = createMapStore(); - let queryableIndexPatternIds; - try { - layerList.forEach((layerDescriptor: unknown) => { - store.dispatch(addLayerWithoutDataSync(layerDescriptor)); - }); - queryableIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState()); - } catch (error) { - throw new Error( - i18n.translate('xpack.maps.mapEmbeddableFactory.invalidLayerList', { - defaultMessage: 'Unable to load map, malformed layer list', - }) - ); - } - - const promises = queryableIndexPatternIds.map(async indexPatternId => { - try { - return await getIndexPatternService().get(indexPatternId); - } catch (error) { - // Unable to load index pattern, better to not throw error so map embeddable can render - // Error will be surfaced by map embeddable since it too will be unable to locate the index pattern - return null; - } - }); - const indexPatterns = await Promise.all(promises); - return _.compact(indexPatterns) as IIndexPattern[]; - } - - async _fetchSavedMap(savedObjectId: string) { - const savedObjectLoader = getMapsSavedObjectLoader(); - return await savedObjectLoader.get(savedObjectId); - } - - createFromSavedObject = async ( - savedObjectId: string, - input: MapEmbeddableInput, - parent?: IContainer - ) => { - const savedMap = await this._fetchSavedMap(savedObjectId); - const layerList = getInitialLayers(savedMap.layerListJSON); - const indexPatterns = await this._getIndexPatterns(layerList); - - const embeddable = new MapEmbeddable( - { - layerList, - title: savedMap.title, - editUrl: getHttp().basePath.prepend(createMapPath(savedObjectId)), - indexPatterns, - editable: await this.isEditable(), - }, - input, - parent - ); - - try { - embeddable.updateInput(mergeInputWithSavedMap(input, savedMap)); - } catch (error) { - throw new Error( - i18n.translate('xpack.maps.mapEmbeddableFactory.invalidSavedObject', { - defaultMessage: 'Unable to load map, malformed saved object', - }) - ); - } - - return embeddable; - }; +import { MAP_SAVED_OBJECT_TYPE } from '../../../../../plugins/maps/common/constants'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { MapEmbeddableFactory } from '../../../../../plugins/maps/public/embeddable'; - create = async (input: MapEmbeddableInput, parent?: IContainer) => { - const layerList = getInitialLayers(); - const indexPatterns = await this._getIndexPatterns(layerList); +bindSetupCoreAndPlugins(npSetup.core, npSetup.plugins); +bindStartCoreAndPlugins(npStart.core, npStart.plugins); - return new MapEmbeddable( - { - layerList, - title: input.title ?? '', - indexPatterns, - editable: false, - }, - input, - parent - ); - }; -} +export * from '../../../../../plugins/maps/public/embeddable/map_embeddable_factory'; npSetup.plugins.embeddable.registerEmbeddableFactory( MAP_SAVED_OBJECT_TYPE, diff --git a/x-pack/legacy/plugins/maps/public/index.scss b/x-pack/legacy/plugins/maps/public/index.scss index b2ac514299d80c..b2a228f01b9210 100644 --- a/x-pack/legacy/plugins/maps/public/index.scss +++ b/x-pack/legacy/plugins/maps/public/index.scss @@ -1,17 +1,3 @@ -// Import the EUI global scope so we can use EUI constants -@import 'src/legacy/ui/public/styles/_styling_constants'; - /* GIS plugin styles */ -// Prefix all styles with "map" to avoid conflicts. -// Examples -// mapChart -// mapChart__legend -// mapChart__legend--small -// mapChart__legend-isLoading - -@import './main'; -@import './mapbox_hacks'; -@import './connected_components/index'; -@import './components/index'; -@import '../../../../plugins/maps/public/layers/index'; +@import '../../../../plugins/maps/public/index'; diff --git a/x-pack/legacy/plugins/maps/public/index.ts b/x-pack/legacy/plugins/maps/public/index.ts index 8555594e909d38..98db26859297b9 100644 --- a/x-pack/legacy/plugins/maps/public/index.ts +++ b/x-pack/legacy/plugins/maps/public/index.ts @@ -15,15 +15,14 @@ import 'uiExports/embeddableActions'; import 'ui/autoload/all'; import 'react-vis/dist/style.css'; - -import './angular/services/gis_map_saved_object_loader'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import '../../../../plugins/maps/public/angular/services/gis_map_saved_object_loader'; import './angular/map_controller'; import './routes'; // @ts-ignore -import { PluginInitializerContext } from 'kibana/public'; import { MapsPlugin } from './plugin'; -export const plugin = (initializerContext: PluginInitializerContext) => { +export const plugin = () => { return new MapsPlugin(); }; @@ -32,4 +31,4 @@ export { RenderTooltipContentParams, ITooltipProperty, } from '../../../../plugins/maps/public/layers/tooltips/tooltip_property'; -export { MapEmbeddable, MapEmbeddableInput } from './embeddable'; +export { MapEmbeddable, MapEmbeddableInput } from '../../../../plugins/maps/public/embeddable'; diff --git a/x-pack/legacy/plugins/maps/public/legacy.ts b/x-pack/legacy/plugins/maps/public/legacy.ts index 96d9e09c1d09a1..bcbfca17755fb4 100644 --- a/x-pack/legacy/plugins/maps/public/legacy.ts +++ b/x-pack/legacy/plugins/maps/public/legacy.ts @@ -7,10 +7,9 @@ import { npSetup, npStart } from 'ui/new_platform'; // @ts-ignore Untyped Module import { uiModules } from 'ui/modules'; -import { PluginInitializerContext } from 'kibana/public'; // eslint-disable-line import/order import { plugin } from '.'; -const pluginInstance = plugin({} as PluginInitializerContext); +const pluginInstance = plugin(); const setupPlugins = { __LEGACY: { diff --git a/x-pack/legacy/plugins/maps/public/legacy_register_feature.ts b/x-pack/legacy/plugins/maps/public/legacy_register_feature.ts deleted file mode 100644 index 00f788f267d4b1..00000000000000 --- a/x-pack/legacy/plugins/maps/public/legacy_register_feature.ts +++ /dev/null @@ -1,14 +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 { npSetup } from 'ui/new_platform'; -import { featureCatalogueEntry } from './feature_catalogue_entry'; - -const { - plugins: { home }, -} = npSetup; - -home.featureCatalogue.register(featureCatalogueEntry); diff --git a/x-pack/legacy/plugins/maps/public/plugin.ts b/x-pack/legacy/plugins/maps/public/plugin.ts index 71f1a30c1fbef9..0123e32b6d3b93 100644 --- a/x-pack/legacy/plugins/maps/public/plugin.ts +++ b/x-pack/legacy/plugins/maps/public/plugin.ts @@ -10,7 +10,7 @@ import { Start as InspectorStartContract } from 'src/plugins/inspector/public'; // @ts-ignore import { wrapInI18nContext } from 'ui/i18n'; // @ts-ignore -import { MapListing } from './components/map_listing'; +import { MapListing } from '../../../../plugins/maps/public/components/map_listing'; // eslint-disable-line @kbn/eslint/no-restricted-paths // @ts-ignore import { bindSetupCoreAndPlugins as bindNpSetupCoreAndPlugins, @@ -18,7 +18,6 @@ import { } from '../../../../plugins/maps/public/plugin'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public'; import { LicensingPluginSetup } from '../../../../plugins/licensing/public'; -import { featureCatalogueEntry } from './feature_catalogue_entry'; import { DataPublicPluginSetup, DataPublicPluginStart, @@ -57,8 +56,6 @@ export class MapsPlugin implements Plugin { }); bindNpSetupCoreAndPlugins(core, np); - - np.home.featureCatalogue.register(featureCatalogueEntry); } public start(core: CoreStart, plugins: MapsPluginStartDependencies) { diff --git a/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js b/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js deleted file mode 100644 index 9dc07bcb5dc0e9..00000000000000 --- a/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; -import { APP_ID, APP_ICON, MAP_BASE_URL } from '../common/constants'; -import { - getInjectedVarFunc, - getVisualizations, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../plugins/maps/public/kibana_services'; -import { npSetup } from 'ui/new_platform'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { bindSetupCoreAndPlugins } from '../../../../plugins/maps/public/plugin'; - -bindSetupCoreAndPlugins(npSetup.core, npSetup.plugins); - -const showMapVisualizationTypes = getInjectedVarFunc()('showMapVisualizationTypes', false); - -const description = i18n.translate('xpack.maps.visTypeAlias.description', { - defaultMessage: 'Create and style maps with multiple layers and indices.', -}); - -const legacyMapVisualizationWarning = i18n.translate( - 'xpack.maps.visTypeAlias.legacyMapVizWarning', - { - defaultMessage: `Use the Maps app instead of Coordinate Map and Region Map. -The Maps app offers more functionality and is easier to use.`, - } -); - -getVisualizations().registerAlias({ - aliasUrl: MAP_BASE_URL, - name: APP_ID, - title: i18n.translate('xpack.maps.visTypeAlias.title', { - defaultMessage: 'Maps', - }), - description: showMapVisualizationTypes - ? `${description} ${legacyMapVisualizationWarning}` - : description, - icon: APP_ICON, - stage: 'production', -}); - -if (!showMapVisualizationTypes) { - getVisualizations().hideTypes(['region_map', 'tile_map']); -} diff --git a/x-pack/legacy/plugins/maps/public/routes.js b/x-pack/legacy/plugins/maps/public/routes.js index c082e0e1352c0e..70c1c4a50efd45 100644 --- a/x-pack/legacy/plugins/maps/public/routes.js +++ b/x-pack/legacy/plugins/maps/public/routes.js @@ -6,15 +6,18 @@ import { i18n } from '@kbn/i18n'; import routes from 'ui/routes'; -import listingTemplate from './angular/listing_ng_wrapper.html'; -import mapTemplate from './angular/map.html'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import listingTemplate from '../../../../plugins/maps/public/angular/listing_ng_wrapper.html'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import mapTemplate from '../../../../plugins/maps/public/angular/map.html'; import { getSavedObjectsClient, getCoreChrome, getMapsCapabilities, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../plugins/maps/public/kibana_services'; -import { getMapsSavedObjectLoader } from './angular/services/gis_map_saved_object_loader'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getMapsSavedObjectLoader } from '../../../../plugins/maps/public/angular/services/gis_map_saved_object_loader'; routes.enable(); diff --git a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js index c5522b7ba21c58..e2a758075155a5 100644 --- a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js +++ b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js @@ -5,7 +5,10 @@ */ import _ from 'lodash'; -import { DEFAULT_MAX_RESULT_WINDOW, DEFAULT_MAX_INNER_RESULT_WINDOW } from '../../common/constants'; +import { + DEFAULT_MAX_RESULT_WINDOW, + DEFAULT_MAX_INNER_RESULT_WINDOW, +} from '../../../../../plugins/maps/common/constants'; export function getIndexPatternSettings(indicesSettingsResp) { let maxResultWindow = Infinity; diff --git a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js index 01a1ba2703cba3..c152f5bfffc31c 100644 --- a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js +++ b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js @@ -5,7 +5,10 @@ */ import { getIndexPatternSettings } from './get_index_pattern_settings'; -import { DEFAULT_MAX_RESULT_WINDOW, DEFAULT_MAX_INNER_RESULT_WINDOW } from '../../common/constants'; +import { + DEFAULT_MAX_RESULT_WINDOW, + DEFAULT_MAX_INNER_RESULT_WINDOW, +} from '../../../../../plugins/maps/common/constants'; describe('max_result_window and max_inner_result_window are not set', () => { test('Should provide default values when values not set', () => { diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts b/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts index 652bb83a0d781c..d34e306d1fff90 100644 --- a/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts +++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts @@ -9,7 +9,7 @@ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { SavedObjectsClientContract } from 'src/core/server'; import { getMapsTelemetry } from '../maps_telemetry'; // @ts-ignore -import { TELEMETRY_TYPE } from '../../../common/constants'; +import { TELEMETRY_TYPE } from '../../../../../../plugins/maps/common/constants'; export function registerMapsUsageCollector( usageCollection: UsageCollectionSetup, diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts index 27c0211446e852..4610baabad3fe5 100644 --- a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts +++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts @@ -16,8 +16,8 @@ import { ES_GEO_FIELD_TYPE, MAP_SAVED_OBJECT_TYPE, TELEMETRY_TYPE, -} from '../../common/constants'; -import { LayerDescriptor } from '../../common/descriptor_types'; +} from '../../../../../plugins/maps/common/constants'; +import { LayerDescriptor } from '../../../../../plugins/maps/common/descriptor_types'; import { MapSavedObject } from '../../../../../plugins/maps/common/map_saved_object_type'; interface IStats { diff --git a/x-pack/legacy/plugins/maps/server/plugin.js b/x-pack/legacy/plugins/maps/server/plugin.js index 25c552433e9f82..79f3dcf76b82eb 100644 --- a/x-pack/legacy/plugins/maps/server/plugin.js +++ b/x-pack/legacy/plugins/maps/server/plugin.js @@ -4,7 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { APP_ID, APP_ICON, createMapPath, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; +import { + APP_ID, + APP_ICON, + createMapPath, + MAP_SAVED_OBJECT_TYPE, +} from '../../../../plugins/maps/common/constants'; import { getEcommerceSavedObjects } from './sample_data/ecommerce_saved_objects'; import { getFlightsSavedObjects } from './sample_data/flights_saved_objects.js'; import { getWebLogsSavedObjects } from './sample_data/web_logs_saved_objects.js'; diff --git a/x-pack/legacy/plugins/maps/server/routes.js b/x-pack/legacy/plugins/maps/server/routes.js index 20e022001577a8..d49f9827e3ea04 100644 --- a/x-pack/legacy/plugins/maps/server/routes.js +++ b/x-pack/legacy/plugins/maps/server/routes.js @@ -21,7 +21,7 @@ import { GIS_API_PATH, EMS_SPRITES_PATH, INDEX_SETTINGS_API_PATH, -} from '../common/constants'; +} from '../../../../plugins/maps/common/constants'; import { EMSClient } from '@elastic/ems-client'; import fetch from 'node-fetch'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/reporting/common/constants.ts b/x-pack/legacy/plugins/reporting/common/constants.ts index e3d6a4274e7df7..f30a7cc87f3187 100644 --- a/x-pack/legacy/plugins/reporting/common/constants.ts +++ b/x-pack/legacy/plugins/reporting/common/constants.ts @@ -20,6 +20,7 @@ export const API_GENERATE_IMMEDIATE = `${API_BASE_URL_V1}/generate/immediate/csv export const CONTENT_TYPE_CSV = 'text/csv'; export const CSV_REPORTING_ACTION = 'downloadCsvReport'; export const CSV_BOM_CHARS = '\ufeff'; +export const CSV_FORMULA_CHARS = ['=', '+', '-', '@']; export const WHITELISTED_JOB_CONTENT_TYPES = [ 'application/json', diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.ts index f0afade8629ab4..ad35aaf0030943 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.ts @@ -300,7 +300,7 @@ describe('CSV Execute Job', function() { }); }); - describe('Cells with formula values', () => { + describe('Warning when cells have formulas', () => { it('returns `csv_contains_formulas` when cells contain formulas', async function() { configGetStub.withArgs('csv', 'checkForFormulas').returns(true); callAsCurrentUserStub.onFirstCall().returns({ @@ -353,6 +353,7 @@ describe('CSV Execute Job', function() { it('returns no warnings when cells have no formulas', async function() { configGetStub.withArgs('csv', 'checkForFormulas').returns(true); + configGetStub.withArgs('csv', 'escapeFormulaValues').returns(false); callAsCurrentUserStub.onFirstCall().returns({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -376,6 +377,33 @@ describe('CSV Execute Job', function() { expect(csvContainsFormulas).toEqual(false); }); + it('returns no warnings when cells have formulas but are escaped', async function() { + configGetStub.withArgs('csv', 'checkForFormulas').returns(true); + configGetStub.withArgs('csv', 'escapeFormulaValues').returns(true); + callAsCurrentUserStub.onFirstCall().returns({ + hits: { + hits: [{ _source: { '=SUM(A1:A2)': 'foo', two: 'bar' } }], + }, + _scroll_id: 'scrollId', + }); + + const executeJob = await executeJobFactory(mockReportingPlugin, mockLogger); + const jobParams = getJobDocPayload({ + headers: encryptedHeaders, + fields: ['=SUM(A1:A2)', 'two'], + conflictedTypesFields: [], + searchRequest: { index: null, body: null }, + }); + + const { csv_contains_formulas: csvContainsFormulas } = await executeJob( + 'job123', + jobParams, + cancellationToken + ); + + expect(csvContainsFormulas).toEqual(false); + }); + it('returns no warnings when configured not to', async () => { configGetStub.withArgs('csv', 'checkForFormulas').returns(false); callAsCurrentUserStub.onFirstCall().returns({ @@ -446,6 +474,50 @@ describe('CSV Execute Job', function() { }); }); + describe('Escaping cells with formulas', () => { + it('escapes values with formulas', async () => { + configGetStub.withArgs('csv', 'escapeFormulaValues').returns(true); + callAsCurrentUserStub.onFirstCall().returns({ + hits: { + hits: [{ _source: { one: `=cmd|' /C calc'!A0`, two: 'bar' } }], + }, + _scroll_id: 'scrollId', + }); + + const executeJob = await executeJobFactory(mockReportingPlugin, mockLogger); + const jobParams = getJobDocPayload({ + headers: encryptedHeaders, + fields: ['one', 'two'], + conflictedTypesFields: [], + searchRequest: { index: null, body: null }, + }); + const { content } = await executeJob('job123', jobParams, cancellationToken); + + expect(content).toEqual("one,two\n\"'=cmd|' /C calc'!A0\",bar\n"); + }); + + it('does not escapes values with formulas', async () => { + configGetStub.withArgs('csv', 'escapeFormulaValues').returns(false); + callAsCurrentUserStub.onFirstCall().returns({ + hits: { + hits: [{ _source: { one: `=cmd|' /C calc'!A0`, two: 'bar' } }], + }, + _scroll_id: 'scrollId', + }); + + const executeJob = await executeJobFactory(mockReportingPlugin, mockLogger); + const jobParams = getJobDocPayload({ + headers: encryptedHeaders, + fields: ['one', 'two'], + conflictedTypesFields: [], + searchRequest: { index: null, body: null }, + }); + const { content } = await executeJob('job123', jobParams, cancellationToken); + + expect(content).toEqual('one,two\n"=cmd|\' /C calc\'!A0",bar\n'); + }); + }); + describe('Elasticsearch call errors', function() { it('should reject Promise if search call errors out', async function() { callAsCurrentUserStub.rejects(new Error()); 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 376a398da274f9..dbe305bc452dbf 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 @@ -123,7 +123,7 @@ export const executeJobFactory: ExecuteJobFactory + CSV_FORMULA_CHARS.some(formulaChar => startsWith(val, formulaChar)); diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/check_cells_for_formulas.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/check_cells_for_formulas.ts index 09f7cd2061ffbb..0ec39c527d6567 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/check_cells_for_formulas.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/check_cells_for_formulas.ts @@ -5,8 +5,7 @@ */ import * as _ from 'lodash'; - -const formulaValues = ['=', '+', '-', '@']; +import { cellHasFormulas } from './cell_has_formula'; interface IFlattened { [header: string]: string; @@ -14,7 +13,7 @@ interface IFlattened { export const checkIfRowsHaveFormulas = (flattened: IFlattened, fields: string[]) => { const pruned = _.pick(flattened, fields); - const csvValues = [..._.keys(pruned), ...(_.values(pruned) as string[])]; + const cells = [..._.keys(pruned), ...(_.values(pruned) as string[])]; - return _.some(csvValues, cell => _.some(formulaValues, char => _.startsWith(cell, char))); + return _.some(cells, cell => cellHasFormulas(cell)); }; diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.test.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.test.ts index 64b021a2aeea84..dd0f9d08b864b9 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.test.ts @@ -11,7 +11,7 @@ describe('escapeValue', function() { describe('quoteValues is true', function() { let escapeValue: (val: string) => string; beforeEach(function() { - escapeValue = createEscapeValue(true); + escapeValue = createEscapeValue(true, false); }); it('should escape value with spaces', function() { @@ -46,7 +46,7 @@ describe('escapeValue', function() { describe('quoteValues is false', function() { let escapeValue: (val: string) => string; beforeEach(function() { - escapeValue = createEscapeValue(false); + escapeValue = createEscapeValue(false, false); }); it('should return the value unescaped', function() { @@ -54,4 +54,34 @@ describe('escapeValue', function() { expect(escapeValue(value)).to.be(value); }); }); + + describe('escapeValues', () => { + describe('when true', () => { + let escapeValue: (val: string) => string; + beforeEach(function() { + escapeValue = createEscapeValue(true, true); + }); + + ['@', '+', '-', '='].forEach(badChar => { + it(`should escape ${badChar} injection values`, function() { + expect(escapeValue(`${badChar}cmd|' /C calc'!A0`)).to.be( + `"'${badChar}cmd|' /C calc'!A0"` + ); + }); + }); + }); + + describe('when false', () => { + let escapeValue: (val: string) => string; + beforeEach(function() { + escapeValue = createEscapeValue(true, false); + }); + + ['@', '+', '-', '='].forEach(badChar => { + it(`should not escape ${badChar} injection values`, function() { + expect(escapeValue(`${badChar}cmd|' /C calc'!A0`)).to.be(`"${badChar}cmd|' /C calc'!A0"`); + }); + }); + }); + }); }); diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.ts index 563de563350e9e..60e75d74b2f98e 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.ts @@ -5,15 +5,20 @@ */ import { RawValue } from './types'; +import { cellHasFormulas } from './cell_has_formula'; const nonAlphaNumRE = /[^a-zA-Z0-9]/; const allDoubleQuoteRE = /"/g; -export function createEscapeValue(quoteValues: boolean): (val: RawValue) => string { +export function createEscapeValue( + quoteValues: boolean, + escapeFormulas: boolean +): (val: RawValue) => string { return function escapeValue(val: RawValue) { if (val && typeof val === 'string') { - if (quoteValues && nonAlphaNumRE.test(val)) { - return `"${val.replace(allDoubleQuoteRE, '""')}"`; + const formulasEscaped = escapeFormulas && cellHasFormulas(val) ? "'" + val : val; + if (quoteValues && nonAlphaNumRE.test(formulasEscaped)) { + return `"${formulasEscaped.replace(allDoubleQuoteRE, '""')}"`; } } diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/generate_csv.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/generate_csv.ts index 1986e68917ba86..c7996ebf832a19 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/generate_csv.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/generate_csv.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { i18n } from '@kbn/i18n'; import { Logger } from '../../../../types'; import { GenerateCsvParams, SavedSearchGeneratorResult } from '../../types'; import { createFlattenHit } from './flatten_hit'; @@ -26,14 +27,17 @@ export function createGenerateCsv(logger: Logger) { cancellationToken, settings, }: GenerateCsvParams): Promise { - const escapeValue = createEscapeValue(settings.quoteValues); + const escapeValue = createEscapeValue(settings.quoteValues, settings.escapeFormulaValues); const builder = new MaxSizeStringBuilder(settings.maxSizeBytes); const header = `${fields.map(escapeValue).join(settings.separator)}\n`; + const warnings: string[] = []; + if (!builder.tryAppend(header)) { return { size: 0, content: '', maxSizeReached: true, + warnings: [], }; } @@ -82,11 +86,20 @@ export function createGenerateCsv(logger: Logger) { const size = builder.getSizeInBytes(); logger.debug(`finished generating, total size in bytes: ${size}`); + if (csvContainsFormulas && settings.escapeFormulaValues) { + warnings.push( + i18n.translate('xpack.reporting.exportTypes.csv.generateCsv.escapedFormulaValues', { + defaultMessage: 'CSV may contain formulas whose values have been escaped', + }) + ); + } + return { content: builder.getString(), - csvContainsFormulas, + csvContainsFormulas: csvContainsFormulas && !settings.escapeFormulaValues, maxSizeReached, size, + warnings, }; }; } diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/types.d.ts b/x-pack/legacy/plugins/reporting/export_types/csv/types.d.ts index 529c195486bc6d..40a42db3526352 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/types.d.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/types.d.ts @@ -87,6 +87,7 @@ export interface SavedSearchGeneratorResult { size: number; maxSizeReached: boolean; csvContainsFormulas?: boolean; + warnings: string[]; } export interface CsvResultFromSearch { @@ -109,5 +110,6 @@ export interface GenerateCsvParams { maxSizeBytes: number; scroll: ScrollConfig; checkForFormulas?: boolean; + escapeFormulaValues: boolean; }; } 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 9757c71c19cf41..2611b74c83de92 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 @@ -173,6 +173,7 @@ export async function generateCsvSearch( ...uiSettings, maxSizeBytes: config.get('csv', 'maxSizeBytes'), scroll: config.get('csv', 'scroll'), + escapeFormulaValues: config.get('csv', 'escapeFormulaValues'), timezone, }, }; diff --git a/x-pack/legacy/plugins/siem/public/containers/case/api.test.tsx b/x-pack/legacy/plugins/siem/public/containers/case/api.test.tsx index 4f5655cc9f2219..693a7175ebc3eb 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/api.test.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/api.test.tsx @@ -5,6 +5,9 @@ */ import { KibanaServices } from '../../lib/kibana'; + +import { CASES_URL } from '../../../../../../plugins/case/common/constants'; + import { deleteCases, getActionLicense, @@ -22,6 +25,7 @@ import { pushCase, pushToService, } from './api'; + import { actionLicenses, allCases, @@ -44,7 +48,7 @@ import { caseUserActionsSnake, casesStatusSnake, } from './mock'; -import { CASES_URL } from './constants'; + import { DEFAULT_FILTER_OPTIONS, DEFAULT_QUERY_PARAMS } from './use_get_cases'; import * as i18n from './translations'; diff --git a/x-pack/legacy/plugins/siem/public/containers/case/api.ts b/x-pack/legacy/plugins/siem/public/containers/case/api.ts index 12b4c80a2dd899..b7453616324199 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/api.ts +++ b/x-pack/legacy/plugins/siem/public/containers/case/api.ts @@ -20,6 +20,21 @@ import { ActionTypeExecutorResult, } from '../../../../../../plugins/case/common/api'; +import { + CASE_STATUS_URL, + CASES_URL, + CASE_TAGS_URL, + CASE_REPORTERS_URL, + ACTION_TYPES_URL, + ACTION_URL, +} from '../../../../../../plugins/case/common/constants'; + +import { + getCaseDetailsUrl, + getCaseUserActionUrl, + getCaseCommentsUrl, +} from '../../../../../../plugins/case/common/api/helpers'; + import { KibanaServices } from '../../lib/kibana'; import { @@ -33,8 +48,6 @@ import { CaseUserActions, } from './types'; -import { CASES_URL } from './constants'; - import { convertToCamelCase, convertAllCasesToCamel, @@ -54,7 +67,7 @@ export const getCase = async ( includeComments: boolean = true, signal: AbortSignal ): Promise => { - const response = await KibanaServices.get().http.fetch(`${CASES_URL}/${caseId}`, { + const response = await KibanaServices.get().http.fetch(getCaseDetailsUrl(caseId), { method: 'GET', query: { includeComments, @@ -65,18 +78,15 @@ export const getCase = async ( }; export const getCasesStatus = async (signal: AbortSignal): Promise => { - const response = await KibanaServices.get().http.fetch( - `${CASES_URL}/status`, - { - method: 'GET', - signal, - } - ); + const response = await KibanaServices.get().http.fetch(CASE_STATUS_URL, { + method: 'GET', + signal, + }); return convertToCamelCase(decodeCasesStatusResponse(response)); }; export const getTags = async (signal: AbortSignal): Promise => { - const response = await KibanaServices.get().http.fetch(`${CASES_URL}/tags`, { + const response = await KibanaServices.get().http.fetch(CASE_TAGS_URL, { method: 'GET', signal, }); @@ -84,7 +94,7 @@ export const getTags = async (signal: AbortSignal): Promise => { }; export const getReporters = async (signal: AbortSignal): Promise => { - const response = await KibanaServices.get().http.fetch(`${CASES_URL}/reporters`, { + const response = await KibanaServices.get().http.fetch(CASE_REPORTERS_URL, { method: 'GET', signal, }); @@ -96,7 +106,7 @@ export const getCaseUserActions = async ( signal: AbortSignal ): Promise => { const response = await KibanaServices.get().http.fetch( - `${CASES_URL}/${caseId}/user_actions`, + getCaseUserActionUrl(caseId), { method: 'GET', signal, @@ -193,14 +203,11 @@ export const patchComment = async ( version: string, signal: AbortSignal ): Promise => { - const response = await KibanaServices.get().http.fetch( - `${CASES_URL}/${caseId}/comments`, - { - method: 'PATCH', - body: JSON.stringify({ comment: commentUpdate, id: commentId, version }), - signal, - } - ); + const response = await KibanaServices.get().http.fetch(getCaseCommentsUrl(caseId), { + method: 'PATCH', + body: JSON.stringify({ comment: commentUpdate, id: commentId, version }), + signal, + }); return convertToCamelCase(decodeCaseResponse(response)); }; @@ -219,7 +226,7 @@ export const pushCase = async ( signal: AbortSignal ): Promise => { const response = await KibanaServices.get().http.fetch( - `${CASES_URL}/${caseId}/_push`, + `${getCaseDetailsUrl(caseId)}/_push`, { method: 'POST', body: JSON.stringify(push), @@ -235,7 +242,7 @@ export const pushToService = async ( signal: AbortSignal ): Promise => { const response = await KibanaServices.get().http.fetch( - `/api/action/${connectorId}/_execute`, + `${ACTION_URL}/${connectorId}/_execute`, { method: 'POST', body: JSON.stringify({ params: casePushParams }), @@ -251,7 +258,7 @@ export const pushToService = async ( }; export const getActionLicense = async (signal: AbortSignal): Promise => { - const response = await KibanaServices.get().http.fetch(`/api/action/types`, { + const response = await KibanaServices.get().http.fetch(ACTION_TYPES_URL, { method: 'GET', signal, }); diff --git a/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts b/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts index c24081c777a968..85e472811c93bf 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts +++ b/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts @@ -13,26 +13,27 @@ import { } from '../../../../../../../plugins/case/common/api'; import { KibanaServices } from '../../../lib/kibana'; -import { CASES_CONFIGURE_URL } from '../constants'; +import { + CASE_CONFIGURE_CONNECTORS_URL, + CASE_CONFIGURE_URL, +} from '../../../../../../../plugins/case/common/constants'; + import { ApiProps } from '../types'; import { convertToCamelCase, decodeCaseConfigureResponse } from '../utils'; import { CaseConfigure } from './types'; export const fetchConnectors = async ({ signal }: ApiProps): Promise => { - const response = await KibanaServices.get().http.fetch( - `${CASES_CONFIGURE_URL}/connectors/_find`, - { - method: 'GET', - signal, - } - ); + const response = await KibanaServices.get().http.fetch(`${CASE_CONFIGURE_CONNECTORS_URL}/_find`, { + method: 'GET', + signal, + }); return response; }; export const getCaseConfigure = async ({ signal }: ApiProps): Promise => { const response = await KibanaServices.get().http.fetch( - CASES_CONFIGURE_URL, + CASE_CONFIGURE_URL, { method: 'GET', signal, @@ -51,7 +52,7 @@ export const postCaseConfigure = async ( signal: AbortSignal ): Promise => { const response = await KibanaServices.get().http.fetch( - CASES_CONFIGURE_URL, + CASE_CONFIGURE_URL, { method: 'POST', body: JSON.stringify(caseConfiguration), @@ -68,7 +69,7 @@ export const patchCaseConfigure = async ( signal: AbortSignal ): Promise => { const response = await KibanaServices.get().http.fetch( - CASES_CONFIGURE_URL, + CASE_CONFIGURE_URL, { method: 'PATCH', body: JSON.stringify(caseConfiguration), diff --git a/x-pack/legacy/plugins/siem/public/containers/case/constants.ts b/x-pack/legacy/plugins/siem/public/containers/case/constants.ts index ab8dc98db4f64b..d8bb499ed7922a 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/constants.ts +++ b/x-pack/legacy/plugins/siem/public/containers/case/constants.ts @@ -4,7 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export const CASES_URL = `/api/cases`; -export const CASES_CONFIGURE_URL = `/api/cases/configure`; export const DEFAULT_TABLE_ACTIVE_PAGE = 1; export const DEFAULT_TABLE_LIMIT = 5; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx index 0a30329baf68d1..f74c2bad1019e8 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/use_rule_status.test.tsx @@ -72,104 +72,108 @@ describe('useRuleStatus', () => { cleanup(); }); - test('init', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useRuleStatus('myOwnRuleID') - ); - await waitForNextUpdate(); - expect(result.current).toEqual([true, null, null]); - }); - }); - - test('fetch rule status', async () => { - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useRuleStatus('myOwnRuleID') - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual([ - false, - { - current_status: { - alert_id: 'alertId', - last_failure_at: null, - last_failure_message: null, - last_success_at: 'mm/dd/yyyyTHH:MM:sssz', - last_success_message: 'it is a success', - status: 'succeeded', - status_date: 'mm/dd/yyyyTHH:MM:sssz', - gap: null, - bulk_create_time_durations: ['2235.01'], - search_after_time_durations: ['616.97'], - last_look_back_date: '2020-03-19T00:32:07.996Z', - }, - failures: [], - }, - result.current[2], - ]); - }); - }); - - test('re-fetch rule status', async () => { - const spyOngetRuleStatusById = jest.spyOn(api, 'getRuleStatusById'); - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useRuleStatus('myOwnRuleID') - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - if (result.current[2]) { - result.current[2]('myOwnRuleID'); - } - await waitForNextUpdate(); - expect(spyOngetRuleStatusById).toHaveBeenCalledTimes(2); - }); - }); - - test('init rules statuses', async () => { - const payload = [testRule]; - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useRulesStatuses(payload) - ); - await waitForNextUpdate(); - expect(result.current).toEqual({ loading: false, rulesStatuses: [] }); + describe('useRuleStatus', () => { + test('init', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useRuleStatus('myOwnRuleID') + ); + await waitForNextUpdate(); + expect(result.current).toEqual([true, null, null]); + }); }); - }); - test('fetch rules statuses', async () => { - const payload = [testRule]; - await act(async () => { - const { result, waitForNextUpdate } = renderHook(() => - useRulesStatuses(payload) - ); - await waitForNextUpdate(); - await waitForNextUpdate(); - expect(result.current).toEqual({ - loading: false, - rulesStatuses: [ + test('fetch rule status', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useRuleStatus('myOwnRuleID') + ); + await waitForNextUpdate(); + await waitForNextUpdate(); + expect(result.current).toEqual([ + false, { current_status: { alert_id: 'alertId', - bulk_create_time_durations: ['2235.01'], - gap: null, last_failure_at: null, last_failure_message: null, - last_look_back_date: '2020-03-19T00:32:07.996Z', last_success_at: 'mm/dd/yyyyTHH:MM:sssz', last_success_message: 'it is a success', - search_after_time_durations: ['616.97'], status: 'succeeded', status_date: 'mm/dd/yyyyTHH:MM:sssz', + gap: null, + bulk_create_time_durations: ['2235.01'], + search_after_time_durations: ['616.97'], + last_look_back_date: '2020-03-19T00:32:07.996Z', }, failures: [], - id: '12345678987654321', - activate: true, - name: 'Test rule', }, - ], + result.current[2], + ]); + }); + }); + + test('re-fetch rule status', async () => { + const spyOngetRuleStatusById = jest.spyOn(api, 'getRuleStatusById'); + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useRuleStatus('myOwnRuleID') + ); + await waitForNextUpdate(); + await waitForNextUpdate(); + if (result.current[2]) { + result.current[2]('myOwnRuleID'); + } + await waitForNextUpdate(); + expect(spyOngetRuleStatusById).toHaveBeenCalledTimes(2); + }); + }); + }); + + describe('useRulesStatuses', () => { + test('init rules statuses', async () => { + const payload = [testRule]; + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useRulesStatuses(payload) + ); + await waitForNextUpdate(); + expect(result.current).toEqual({ loading: false, rulesStatuses: [] }); + }); + }); + + test('fetch rules statuses', async () => { + const payload = [testRule]; + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => + useRulesStatuses(payload) + ); + await waitForNextUpdate(); + await waitForNextUpdate(); + expect(result.current).toEqual({ + loading: false, + rulesStatuses: [ + { + current_status: { + alert_id: 'alertId', + bulk_create_time_durations: ['2235.01'], + gap: null, + last_failure_at: null, + last_failure_message: null, + last_look_back_date: '2020-03-19T00:32:07.996Z', + last_success_at: 'mm/dd/yyyyTHH:MM:sssz', + last_success_message: 'it is a success', + search_after_time_durations: ['616.97'], + status: 'succeeded', + status_date: 'mm/dd/yyyyTHH:MM:sssz', + }, + failures: [], + id: '12345678987654321', + activate: true, + name: 'Test rule', + }, + ], + }); }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/__mock__/form.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/__mock__/form.ts index 9d2ac29bc47d7d..cc01edcfaab112 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/__mock__/form.ts +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/__mock__/form.ts @@ -3,6 +3,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { useForm } from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks'; +jest.mock( + '../../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form' +); export const mockFormHook = { isSubmitted: false, isSubmitting: false, @@ -35,3 +39,5 @@ export const getFormMock = (sampleData: any) => ({ }), getFormData: () => sampleData, }); + +export const useFormMock = useForm as jest.Mock; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.test.tsx index d480744fc932a7..0897be6310fa2e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.test.tsx @@ -14,6 +14,8 @@ import { Router, routeData, mockHistory, mockLocation } from '../__mock__/router import { useInsertTimeline } from '../../../../components/timeline/insert_timeline_popover/use_insert_timeline'; import { usePostCase } from '../../../../containers/case/use_post_case'; +import { useGetTags } from '../../../../containers/case/use_get_tags'; + jest.mock('../../../../components/timeline/insert_timeline_popover/use_insert_timeline'); jest.mock('../../../../containers/case/use_post_case'); import { useForm } from '../../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks'; @@ -22,6 +24,14 @@ import { SiemPageName } from '../../../home/types'; jest.mock( '../../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form' ); +jest.mock('../../../../containers/case/use_get_tags'); +jest.mock( + '../../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider', + () => ({ + FormDataProvider: ({ children }: { children: ({ tags }: { tags: string[] }) => void }) => + children({ tags: ['rad', 'dude'] }), + }) +); export const useFormMock = useForm as jest.Mock; @@ -40,9 +50,11 @@ const defaultInsertTimeline = { handleCursorChange, handleOnTimelineChange, }; + +const sampleTags = ['coke', 'pepsi']; const sampleData = { description: 'what a great description', - tags: ['coke', 'pepsi'], + tags: sampleTags, title: 'what a cool title', }; const defaultPostCase = { @@ -52,14 +64,28 @@ const defaultPostCase = { postCase, }; describe('Create case', () => { + // Suppress warnings about "noSuggestions" prop + /* eslint-disable no-console */ + const originalError = console.error; + beforeAll(() => { + console.error = jest.fn(); + }); + afterAll(() => { + console.error = originalError; + }); + /* eslint-enable no-console */ + const fetchTags = jest.fn(); const formHookMock = getFormMock(sampleData); - beforeEach(() => { jest.resetAllMocks(); useInsertTimelineMock.mockImplementation(() => defaultInsertTimeline); usePostCaseMock.mockImplementation(() => defaultPostCase); useFormMock.mockImplementation(() => ({ form: formHookMock })); jest.spyOn(routeData, 'useLocation').mockReturnValue(mockLocation); + (useGetTags as jest.Mock).mockImplementation(() => ({ + tags: sampleTags, + fetchTags, + })); }); it('should post case on submit click', async () => { @@ -118,4 +144,19 @@ describe('Create case', () => { ); expect(wrapper.find(`[data-test-subj="create-case-loading-spinner"]`).exists()).toBeTruthy(); }); + it('Tag options render with new tags added', () => { + const wrapper = mount( + + + + + + ); + expect( + wrapper + .find(`[data-test-subj="caseTags"] [data-test-subj="input"]`) + .first() + .prop('options') + ).toEqual([{ label: 'coke' }, { label: 'pepsi' }, { label: 'rad' }, { label: 'dude' }]); + }); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx index 53b792bb9b5ebb..0f819f961b3963 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/create/index.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { EuiButton, EuiButtonEmpty, @@ -15,8 +15,16 @@ import { import styled, { css } from 'styled-components'; import { Redirect } from 'react-router-dom'; +import { isEqual } from 'lodash/fp'; import { CasePostRequest } from '../../../../../../../../plugins/case/common/api'; -import { Field, Form, getUseField, useForm, UseField } from '../../../../shared_imports'; +import { + Field, + Form, + getUseField, + useForm, + UseField, + FormDataProvider, +} from '../../../../shared_imports'; import { usePostCase } from '../../../../containers/case/use_post_case'; import { schema } from './schema'; import { InsertTimelinePopover } from '../../../../components/timeline/insert_timeline_popover'; @@ -24,6 +32,7 @@ import { useInsertTimeline } from '../../../../components/timeline/insert_timeli import * as i18n from '../../translations'; import { SiemPageName } from '../../../home/types'; import { MarkdownEditorForm } from '../../../../components/markdown_editor/form'; +import { useGetTags } from '../../../../containers/case/use_get_tags'; export const CommonUseField = getUseField({ component: Field }); @@ -59,6 +68,21 @@ export const Create = React.memo(() => { options: { stripEmptyFields: false }, schema, }); + const { tags: tagOptions } = useGetTags(); + const [options, setOptions] = useState( + tagOptions.map(label => ({ + label, + })) + ); + useEffect( + () => + setOptions( + tagOptions.map(label => ({ + label, + })) + ), + [tagOptions] + ); const { handleCursorChange, handleOnTimelineChange } = useInsertTimeline( form, 'description' @@ -108,6 +132,8 @@ export const Create = React.memo(() => { fullWidth: true, placeholder: '', disabled: isLoading, + options, + noSuggestions: false, }, }} /> @@ -131,6 +157,25 @@ export const Create = React.memo(() => { }} /> + + {({ tags: anotherTags }) => { + const current: string[] = options.map(opt => opt.label); + const newOptions = anotherTags.reduce((acc: string[], item: string) => { + if (!acc.includes(item)) { + return [...acc, item]; + } + return acc; + }, current); + if (!isEqual(current, newOptions)) { + setOptions( + newOptions.map((label: string) => ({ + label, + })) + ); + } + return null; + }} + ({ + FormDataProvider: ({ children }: { children: ({ tags }: { tags: string[] }) => void }) => + children({ tags: ['rad', 'dude'] }), + }) +); const onSubmit = jest.fn(); const defaultProps = { disabled: false, @@ -26,11 +35,27 @@ const defaultProps = { }; describe('TagList ', () => { + // Suppress warnings about "noSuggestions" prop + /* eslint-disable no-console */ + const originalError = console.error; + beforeAll(() => { + console.error = jest.fn(); + }); + afterAll(() => { + console.error = originalError; + }); + /* eslint-enable no-console */ const sampleTags = ['coke', 'pepsi']; + const fetchTags = jest.fn(); const formHookMock = getFormMock({ tags: sampleTags }); beforeEach(() => { jest.resetAllMocks(); (useForm as jest.Mock).mockImplementation(() => ({ form: formHookMock })); + + (useGetTags as jest.Mock).mockImplementation(() => ({ + tags: sampleTags, + fetchTags, + })); }); it('Renders no tags, and then edit', () => { const wrapper = mount( @@ -80,6 +105,23 @@ describe('TagList ', () => { expect(onSubmit).toBeCalledWith(sampleTags); }); }); + it('Tag options render with new tags added', () => { + const wrapper = mount( + + + + ); + wrapper + .find(`[data-test-subj="tag-list-edit-button"]`) + .last() + .simulate('click'); + expect( + wrapper + .find(`[data-test-subj="caseTags"] [data-test-subj="input"]`) + .first() + .prop('options') + ).toEqual([{ label: 'coke' }, { label: 'pepsi' }, { label: 'rad' }, { label: 'dude' }]); + }); it('Cancels on cancel', async () => { const props = { ...defaultProps, diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/index.tsx index 9bac000b93235d..c96ae09706426c 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/tag_list/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import { EuiText, EuiHorizontalRule, @@ -17,10 +17,12 @@ import { EuiLoadingSpinner, } from '@elastic/eui'; import styled, { css } from 'styled-components'; +import { isEqual } from 'lodash/fp'; import * as i18n from './translations'; -import { Form, useForm } from '../../../../shared_imports'; +import { Form, FormDataProvider, useForm } from '../../../../shared_imports'; import { schema } from './schema'; import { CommonUseField } from '../create'; +import { useGetTags } from '../../../../containers/case/use_get_tags'; interface TagListProps { disabled?: boolean; @@ -54,6 +56,22 @@ export const TagList = React.memo( setIsEditTags(false); } }, [form, onSubmit]); + const { tags: tagOptions } = useGetTags(); + const [options, setOptions] = useState( + tagOptions.map(label => ({ + label, + })) + ); + + useEffect( + () => + setOptions( + tagOptions.map(label => ({ + label, + })) + ), + [tagOptions] + ); return ( @@ -75,7 +93,7 @@ export const TagList = React.memo( )} - + {tags.length === 0 && !isEditTags &&

{i18n.NO_TAGS}

} {tags.length > 0 && !isEditTags && @@ -98,9 +116,30 @@ export const TagList = React.memo( euiFieldProps: { fullWidth: true, placeholder: '', + options, + noSuggestions: false, }, }} /> + + {({ tags: anotherTags }) => { + const current: string[] = options.map(opt => opt.label); + const newOptions = anotherTags.reduce((acc: string[], item: string) => { + if (!acc.includes(item)) { + return [...acc, item]; + } + return acc; + }, current); + if (!isEqual(current, newOptions)) { + setOptions( + newOptions.map((label: string) => ({ + label, + })) + ); + } + return null; + }} + diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/index.test.tsx index 1c71260422d4b1..ff402e8ea1c8b4 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/index.test.tsx @@ -8,17 +8,13 @@ import React from 'react'; import { mount } from 'enzyme'; import { Router, routeData, mockHistory, mockLocation } from '../__mock__/router'; -import { getFormMock } from '../__mock__/form'; +import { getFormMock, useFormMock } from '../__mock__/form'; import { useUpdateComment } from '../../../../containers/case/use_update_comment'; import { basicCase, getUserAction } from '../../../../containers/case/mock'; import { UserActionTree } from './'; import { TestProviders } from '../../../../mock'; -import { useFormMock } from '../create/index.test'; import { wait } from '../../../../lib/helpers'; import { act } from 'react-dom/test-utils'; -jest.mock( - '../../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form' -); const fetchUserActions = jest.fn(); const onUpdateField = jest.fn(); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx index 8bea504f842065..97c89f91c12bdf 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx @@ -243,7 +243,7 @@ export const getMonitoringColumns = (): RulesStatusesColumns[] => { {value != null && value.length > 0 ? Math.max(...value?.map(item => Number.parseFloat(item))) - : null} + : getEmptyTagValue()} ), truncateText: true, @@ -256,7 +256,7 @@ export const getMonitoringColumns = (): RulesStatusesColumns[] => { {value != null && value.length > 0 ? Math.max(...value?.map(item => Number.parseFloat(item))) - : null} + : getEmptyTagValue()} ), truncateText: true, @@ -267,7 +267,7 @@ export const getMonitoringColumns = (): RulesStatusesColumns[] => { name: i18n.COLUMN_GAP, render: (value: RuleStatus['current_status']['gap']) => ( - {value} + {value ?? getEmptyTagValue()} ), truncateText: true, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.test.tsx index f4955c2a93b8dd..59b3b02ff3587a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.test.tsx @@ -5,10 +5,138 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { shallow, mount } from 'enzyme'; +import { act } from 'react-dom/test-utils'; +import { createKibanaContextProviderMock } from '../../../../mock/kibana_react'; +import { TestProviders } from '../../../../mock'; +import { wait } from '../../../../lib/helpers'; import { AllRules } from './index'; +jest.mock('./reducer', () => { + return { + allRulesReducer: jest.fn().mockReturnValue(() => ({ + exportRuleIds: [], + filterOptions: { + filter: 'some filter', + sortField: 'some sort field', + sortOrder: 'desc', + }, + loadingRuleIds: [], + loadingRulesAction: null, + pagination: { + page: 1, + perPage: 20, + total: 1, + }, + rules: [ + { + actions: [], + created_at: '2020-02-14T19:49:28.178Z', + created_by: 'elastic', + description: 'jibber jabber', + enabled: false, + false_positives: [], + filters: [], + from: 'now-660s', + id: 'rule-id-1', + immutable: true, + index: ['endgame-*'], + interval: '10m', + language: 'kuery', + max_signals: 100, + name: 'Credential Dumping - Detected - Elastic Endpoint', + output_index: '.siem-signals-default', + query: 'host.name:*', + references: [], + risk_score: 73, + rule_id: '571afc56-5ed9-465d-a2a9-045f099f6e7e', + severity: 'high', + tags: ['Elastic', 'Endpoint'], + threat: [], + throttle: null, + to: 'now', + type: 'query', + updated_at: '2020-02-14T19:49:28.320Z', + updated_by: 'elastic', + version: 1, + }, + ], + selectedRuleIds: [], + })), + }; +}); + +jest.mock('../../../../containers/detection_engine/rules', () => { + return { + useRules: jest.fn().mockReturnValue([ + false, + { + page: 1, + perPage: 20, + total: 1, + data: [ + { + actions: [], + created_at: '2020-02-14T19:49:28.178Z', + created_by: 'elastic', + description: 'jibber jabber', + enabled: false, + false_positives: [], + filters: [], + from: 'now-660s', + id: 'rule-id-1', + immutable: true, + index: ['endgame-*'], + interval: '10m', + language: 'kuery', + max_signals: 100, + name: 'Credential Dumping - Detected - Elastic Endpoint', + output_index: '.siem-signals-default', + query: 'host.name:*', + references: [], + risk_score: 73, + rule_id: '571afc56-5ed9-465d-a2a9-045f099f6e7e', + severity: 'high', + tags: ['Elastic', 'Endpoint'], + threat: [], + throttle: null, + to: 'now', + type: 'query', + updated_at: '2020-02-14T19:49:28.320Z', + updated_by: 'elastic', + version: 1, + }, + ], + }, + ]), + useRulesStatuses: jest.fn().mockReturnValue({ + loading: false, + rulesStatuses: [ + { + current_status: { + alert_id: 'alertId', + bulk_create_time_durations: ['2235.01'], + gap: null, + last_failure_at: null, + last_failure_message: null, + last_look_back_date: new Date().toISOString(), + last_success_at: new Date().toISOString(), + last_success_message: 'it is a success', + search_after_time_durations: ['616.97'], + status: 'succeeded', + status_date: new Date().toISOString(), + }, + failures: [], + id: '12345678987654321', + activate: true, + name: 'Test rule', + }, + ], + }), + }; +}); + jest.mock('react-router-dom', () => { const originalModule = jest.requireActual('react-router-dom'); @@ -37,4 +165,66 @@ describe('AllRules', () => { expect(wrapper.find('[title="All rules"]')).toHaveLength(1); }); + + it('renders rules tab', async () => { + const KibanaContext = createKibanaContextProviderMock(); + const wrapper = mount( + + + + + + ); + + await act(async () => { + await wait(); + + expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeFalsy(); + expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeTruthy(); + }); + }); + + it('renders monitoring tab when monitoring tab clicked', async () => { + const KibanaContext = createKibanaContextProviderMock(); + + const wrapper = mount( + + + + + + ); + const monitoringTab = wrapper.find('[data-test-subj="allRulesTableTab-monitoring"] button'); + monitoringTab.simulate('click'); + + await act(async () => { + wrapper.update(); + await wait(); + + expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeTruthy(); + expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeFalsy(); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx index e96ed856208bde..18ca4d42bd018d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx @@ -4,7 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiBasicTable, EuiContextMenuPanel, EuiLoadingContent, EuiSpacer } from '@elastic/eui'; +import { + EuiBasicTable, + EuiContextMenuPanel, + EuiLoadingContent, + EuiSpacer, + EuiTab, + EuiTabs, +} from '@elastic/eui'; import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'; import { useHistory } from 'react-router-dom'; import uuid from 'uuid'; @@ -75,6 +82,24 @@ interface AllRulesProps { setRefreshRulesData: (refreshRule: (refreshPrePackagedRule?: boolean) => void) => void; } +export enum AllRulesTabs { + rules = 'rules', + monitoring = 'monitoring', +} + +const allRulesTabs = [ + { + id: AllRulesTabs.rules, + name: i18n.RULES_TAB, + disabled: false, + }, + { + id: AllRulesTabs.monitoring, + name: i18n.MONITORING_TAB, + disabled: false, + }, +]; + /** * Table Component for displaying all Rules for a given cluster. Provides the ability to filter * by name, sort by enabled, and perform the following actions: @@ -114,6 +139,7 @@ export const AllRules = React.memo( const history = useHistory(); const [, dispatchToaster] = useStateToaster(); const mlCapabilities = useMlCapabilities(); + const [allRulesTab, setAllRulesTab] = useState(AllRulesTabs.rules); // TODO: Refactor license check + hasMlAdminPermissions to common check const hasMlPermissions = @@ -271,6 +297,25 @@ export const AllRules = React.memo( return false; }, [loadingRuleIds, loadingRulesAction]); + const tabs = useMemo( + () => ( + + {allRulesTabs.map(tab => ( + setAllRulesTab(tab.id)} + isSelected={tab.id === allRulesTab} + disabled={tab.disabled} + key={tab.id} + > + {tab.name} + + ))} + + ), + [allRulesTabs, allRulesTab, setAllRulesTab] + ); + return ( <> ( exportSelectedData={exportRules} /> + {tabs} + <> @@ -321,7 +368,7 @@ export const AllRules = React.memo( )} {showRulesTable({ rulesCustomInstalled, rulesInstalled }) && !initLoading && ( <> - + @@ -352,6 +399,7 @@ export const AllRules = React.memo( { it('renders correctly', () => { @@ -16,6 +17,7 @@ describe('AllRulesTables', () => { return ( { expect(wrapper.dive().find('[data-test-subj="rules-table"]')).toHaveLength(1); }); + + it('renders rules tab when "selectedTab" is "rules"', () => { + const Component = () => { + const ref = useRef(); + + return ( + + ); + }; + const wrapper = shallow(); + + expect(wrapper.dive().find('[data-test-subj="rules-table"]')).toHaveLength(1); + expect(wrapper.dive().find('[data-test-subj="monitoring-table"]')).toHaveLength(0); + }); + + it('renders monitoring tab when "selectedTab" is "monitoring"', () => { + const Component = () => { + const ref = useRef(); + + return ( + + ); + }; + const wrapper = shallow(); + + expect(wrapper.dive().find('[data-test-subj="rules-table"]')).toHaveLength(0); + expect(wrapper.dive().find('[data-test-subj="monitoring-table"]')).toHaveLength(1); + }); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/all_rules_tables/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/all_rules_tables/index.tsx index 31aaa426e4f3b8..8ea5606d0082c0 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/all_rules_tables/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/all_rules_tables/index.tsx @@ -7,13 +7,11 @@ import { EuiBasicTable, EuiBasicTableColumn, - EuiTab, - EuiTabs, EuiEmptyPrompt, Direction, EuiTableSelectionType, } from '@elastic/eui'; -import React, { useMemo, memo, useState } from 'react'; +import React, { useMemo, memo } from 'react'; import styled from 'styled-components'; import { EuiBasicTableOnChange } from '../../types'; @@ -23,6 +21,7 @@ import { RuleStatusRowItemType, } from '../../../../../pages/detection_engine/rules/all/columns'; import { Rule, Rules } from '../../../../../containers/detection_engine/rules'; +import { AllRulesTabs } from '../../all'; // EuiBasicTable give me a hardtime with adding the ref attributes so I went the easy way // after few hours of fight with typescript !!!! I lost :( @@ -57,27 +56,10 @@ interface AllRulesTablesProps { }; tableOnChangeCallback: ({ page, sort }: EuiBasicTableOnChange) => void; tableRef?: React.MutableRefObject; + selectedTab: AllRulesTabs; } -enum AllRulesTabs { - rules = 'rules', - monitoring = 'monitoring', -} - -const allRulesTabs = [ - { - id: AllRulesTabs.rules, - name: i18n.RULES_TAB, - disabled: false, - }, - { - id: AllRulesTabs.monitoring, - name: i18n.MONITORING_TAB, - disabled: false, - }, -]; - -const AllRulesTablesComponent: React.FC = ({ +export const AllRulesTablesComponent: React.FC = ({ euiBasicTableSelectionProps, hasNoPermissions, monitoringColumns, @@ -88,34 +70,17 @@ const AllRulesTablesComponent: React.FC = ({ sorting, tableOnChangeCallback, tableRef, + selectedTab, }) => { - const [allRulesTab, setAllRulesTab] = useState(AllRulesTabs.rules); const emptyPrompt = useMemo(() => { return ( {i18n.NO_RULES}} titleSize="xs" body={i18n.NO_RULES_BODY} /> ); }, []); - const tabs = useMemo( - () => ( - - {allRulesTabs.map(tab => ( - setAllRulesTab(tab.id)} - isSelected={tab.id === allRulesTab} - disabled={tab.disabled} - key={tab.id} - > - {tab.name} - - ))} - - ), - [allRulesTabs, allRulesTab, setAllRulesTab] - ); + return ( <> - {tabs} - {allRulesTab === AllRulesTabs.rules && ( + {selectedTab === AllRulesTabs.rules && ( = ({ selection={hasNoPermissions ? undefined : euiBasicTableSelectionProps} /> )} - {allRulesTab === AllRulesTabs.monitoring && ( + {selectedTab === AllRulesTabs.monitoring && ( { - [eventAction.2]: expected value to equal [acknowledge]" `); }); + + test('should validate and throw error when timestamp has spaces', () => { + const randoDate = new Date('1963-09-23T01:23:45Z').toISOString(); + const timestamp = ` ${randoDate}`; + expect(() => { + validateParams(actionType, { + timestamp, + }); + }).toThrowError(`error validating action params: error parsing timestamp "${timestamp}"`); + }); + + test('should validate and throw error when timestamp is invalid', () => { + const timestamp = `1963-09-55 90:23:45`; + expect(() => { + validateParams(actionType, { + timestamp, + }); + }).toThrowError(`error validating action params: error parsing timestamp "${timestamp}"`); + }); }); describe('execute()', () => { diff --git a/x-pack/plugins/actions/server/builtin_action_types/pagerduty.ts b/x-pack/plugins/actions/server/builtin_action_types/pagerduty.ts index 2b607d0dd41bac..f4d69a4a39e40e 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/pagerduty.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/pagerduty.ts @@ -70,18 +70,26 @@ const ParamsSchema = schema.object( function validateParams(paramsObject: any): string | void { const params: ActionParamsType = paramsObject; - const { timestamp } = params; if (timestamp != null) { - let date; try { - date = Date.parse(timestamp); + const date = Date.parse(timestamp); + if (isNaN(date)) { + return i18n.translate('xpack.actions.builtin.pagerduty.invalidTimestampErrorMessage', { + defaultMessage: `error parsing timestamp "{timestamp}"`, + values: { + timestamp, + }, + }); + } } catch (err) { - return 'error parsing timestamp: ${err.message}'; - } - - if (isNaN(date)) { - return 'error parsing timestamp'; + return i18n.translate('xpack.actions.builtin.pagerduty.timestampParsingFailedErrorMessage', { + defaultMessage: `error parsing timestamp "{timestamp}": {message}`, + values: { + timestamp, + message: err.message, + }, + }); } } } diff --git a/x-pack/plugins/case/common/api/helpers.ts b/x-pack/plugins/case/common/api/helpers.ts new file mode 100644 index 00000000000000..0efdcd38196597 --- /dev/null +++ b/x-pack/plugins/case/common/api/helpers.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + CASE_DETAILS_URL, + CASE_COMMENTS_URL, + CASE_USER_ACTIONS_URL, + CASE_COMMENT_DETAILS_URL, +} from '../constants'; + +export const getCaseDetailsUrl = (id: string): string => { + return CASE_DETAILS_URL.replace('{case_id}', id); +}; + +export const getCaseCommentsUrl = (id: string): string => { + return CASE_COMMENTS_URL.replace('{case_id}', id); +}; + +export const getCaseCommentDetailsUrl = (caseId: string, commentId: string): string => { + return CASE_COMMENT_DETAILS_URL.replace('{case_id}', caseId).replace('{comment_id}', commentId); +}; + +export const getCaseUserActionUrl = (id: string): string => { + return CASE_USER_ACTIONS_URL.replace('{case_id}', id); +}; diff --git a/x-pack/plugins/case/common/constants.ts b/x-pack/plugins/case/common/constants.ts new file mode 100644 index 00000000000000..dcfa46bfa60191 --- /dev/null +++ b/x-pack/plugins/case/common/constants.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. + */ + +export const APP_ID = 'case'; + +/** + * Case routes + */ + +export const CASES_URL = '/api/cases'; +export const CASE_DETAILS_URL = `${CASES_URL}/{case_id}`; +export const CASE_CONFIGURE_URL = `${CASES_URL}/configure`; +export const CASE_CONFIGURE_CONNECTORS_URL = `${CASE_CONFIGURE_URL}/connectors`; +export const CASE_COMMENTS_URL = `${CASE_DETAILS_URL}/comments`; +export const CASE_COMMENT_DETAILS_URL = `${CASE_DETAILS_URL}/comments/{comment_id}`; +export const CASE_REPORTERS_URL = `${CASES_URL}/reporters`; +export const CASE_STATUS_URL = `${CASES_URL}/status`; +export const CASE_TAGS_URL = `${CASES_URL}/tags`; +export const CASE_USER_ACTIONS_URL = `${CASE_DETAILS_URL}/user_actions`; + +/** + * Action routes + */ + +export const ACTION_URL = '/api/action'; +export const ACTION_TYPES_URL = '/api/action/types'; diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/delete_all_comments.ts b/x-pack/plugins/case/server/routes/api/cases/comments/delete_all_comments.ts index 1dfab165eccd72..e9bcb9690ebd86 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/delete_all_comments.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/delete_all_comments.ts @@ -9,11 +9,12 @@ import { schema } from '@kbn/config-schema'; import { buildCommentUserActionItem } from '../../../../services/user_actions/helpers'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; export function initDeleteAllCommentsApi({ caseService, router, userActionService }: RouteDeps) { router.delete( { - path: '/api/cases/{case_id}/comments', + path: CASE_COMMENTS_URL, validate: { params: schema.object({ case_id: schema.string(), diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.test.ts b/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.test.ts index b2022e6dec26d6..67cb9984095709 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.test.ts @@ -15,6 +15,7 @@ import { mockCaseComments, } from '../../__fixtures__'; import { initDeleteCommentApi } from './delete_comment'; +import { CASE_COMMENT_DETAILS_URL } from '../../../../../common/constants'; describe('DELETE comment', () => { let routeHandler: RequestHandler; @@ -23,7 +24,7 @@ describe('DELETE comment', () => { }); it(`deletes the comment. responds with 204`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}/comments/{comment_id}', + path: CASE_COMMENT_DETAILS_URL, method: 'delete', params: { case_id: 'mock-id-1', @@ -43,7 +44,7 @@ describe('DELETE comment', () => { }); it(`returns an error when thrown from deleteComment service`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}/comments/{comment_id}', + path: CASE_COMMENT_DETAILS_URL, method: 'delete', params: { case_id: 'mock-id-1', diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.ts b/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.ts index ff0729afed96a2..72ef400415d0fb 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/delete_comment.ts @@ -11,11 +11,12 @@ import { CASE_SAVED_OBJECT } from '../../../../saved_object_types'; import { buildCommentUserActionItem } from '../../../../services/user_actions/helpers'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; +import { CASE_COMMENT_DETAILS_URL } from '../../../../../common/constants'; export function initDeleteCommentApi({ caseService, router, userActionService }: RouteDeps) { router.delete( { - path: '/api/cases/{case_id}/comments/{comment_id}', + path: CASE_COMMENT_DETAILS_URL, validate: { params: schema.object({ case_id: schema.string(), diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/find_comments.ts b/x-pack/plugins/case/server/routes/api/cases/comments/find_comments.ts index 92da64cebee741..3df9fdb80ba8a1 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/find_comments.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/find_comments.ts @@ -18,11 +18,12 @@ import { } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { escapeHatch, transformComments, wrapError } from '../../utils'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; export function initFindCaseCommentsApi({ caseService, router }: RouteDeps) { router.get( { - path: '/api/cases/{case_id}/comments/_find', + path: `${CASE_COMMENTS_URL}/_find`, validate: { params: schema.object({ case_id: schema.string(), diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/get_all_comment.ts b/x-pack/plugins/case/server/routes/api/cases/comments/get_all_comment.ts index 1500039eb2cc28..8d7820d4e8fece 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/get_all_comment.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/get_all_comment.ts @@ -9,11 +9,12 @@ import { schema } from '@kbn/config-schema'; import { AllCommentsResponseRt } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { flattenCommentSavedObjects, wrapError } from '../../utils'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; export function initGetAllCommentsApi({ caseService, router }: RouteDeps) { router.get( { - path: '/api/cases/{case_id}/comments', + path: CASE_COMMENTS_URL, validate: { params: schema.object({ case_id: schema.string(), diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/get_comment.test.ts b/x-pack/plugins/case/server/routes/api/cases/comments/get_comment.test.ts index 9c8d0e5254df0a..b5a7d6367ea4bc 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/get_comment.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/get_comment.test.ts @@ -15,6 +15,7 @@ import { } from '../../__fixtures__'; import { flattenCommentSavedObject } from '../../utils'; import { initGetCommentApi } from './get_comment'; +import { CASE_COMMENT_DETAILS_URL } from '../../../../../common/constants'; describe('GET comment', () => { let routeHandler: RequestHandler; @@ -23,7 +24,7 @@ describe('GET comment', () => { }); it(`returns the comment`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}/comments/{comment_id}', + path: CASE_COMMENT_DETAILS_URL, method: 'get', params: { case_id: 'mock-id-1', @@ -48,7 +49,7 @@ describe('GET comment', () => { }); it(`returns an error when getComment throws`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}/comments/{comment_id}', + path: CASE_COMMENT_DETAILS_URL, method: 'get', params: { case_id: 'mock-id-1', diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/get_comment.ts b/x-pack/plugins/case/server/routes/api/cases/comments/get_comment.ts index 24f44a5f5129b7..5fa668f6ae5deb 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/get_comment.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/get_comment.ts @@ -9,11 +9,12 @@ import { schema } from '@kbn/config-schema'; import { CommentResponseRt } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { flattenCommentSavedObject, wrapError } from '../../utils'; +import { CASE_COMMENT_DETAILS_URL } from '../../../../../common/constants'; export function initGetCommentApi({ caseService, router }: RouteDeps) { router.get( { - path: '/api/cases/{case_id}/comments/{comment_id}', + path: CASE_COMMENT_DETAILS_URL, validate: { params: schema.object({ case_id: schema.string(), diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.test.ts b/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.test.ts index 8d9906c2abe7fa..04473e302e4680 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.test.ts @@ -14,6 +14,7 @@ import { mockCases, } from '../../__fixtures__'; import { initPatchCommentApi } from './patch_comment'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; describe('PATCH comment', () => { let routeHandler: RequestHandler; @@ -22,7 +23,7 @@ describe('PATCH comment', () => { }); it(`Patch a comment`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}/comments', + path: CASE_COMMENTS_URL, method: 'patch', params: { case_id: 'mock-id-1', @@ -50,7 +51,7 @@ describe('PATCH comment', () => { it(`Fails with 409 if version does not match`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}/comments', + path: CASE_COMMENTS_URL, method: 'patch', params: { case_id: 'mock-id-1', @@ -74,7 +75,7 @@ describe('PATCH comment', () => { }); it(`Returns an error if updateComment throws`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}/comments', + path: CASE_COMMENTS_URL, method: 'patch', params: { case_id: 'mock-id-1', diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.ts b/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.ts index 3b38afc02ed81b..dd9b124ff1b79f 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/patch_comment.ts @@ -15,11 +15,12 @@ import { CASE_SAVED_OBJECT } from '../../../../saved_object_types'; import { buildCommentUserActionItem } from '../../../../services/user_actions/helpers'; import { RouteDeps } from '../../types'; import { escapeHatch, wrapError, flattenCaseSavedObject } from '../../utils'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; export function initPatchCommentApi({ caseService, router, userActionService }: RouteDeps) { router.patch( { - path: '/api/cases/{case_id}/comments', + path: CASE_COMMENTS_URL, validate: { params: schema.object({ case_id: schema.string(), diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.test.ts b/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.test.ts index 23039da681ec65..9006470f36f368 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.test.ts @@ -15,6 +15,7 @@ import { mockCaseComments, } from '../../__fixtures__'; import { initPostCommentApi } from './post_comment'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; describe('POST comment', () => { let routeHandler: RequestHandler; @@ -27,7 +28,7 @@ describe('POST comment', () => { }); it(`Posts a new comment`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}/comments', + path: CASE_COMMENTS_URL, method: 'post', params: { case_id: 'mock-id-1', @@ -52,7 +53,7 @@ describe('POST comment', () => { }); it(`Returns an error if the case does not exist`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}/comments', + path: CASE_COMMENTS_URL, method: 'post', params: { case_id: 'this-is-not-real', @@ -75,7 +76,7 @@ describe('POST comment', () => { }); it(`Returns an error if postNewCase throws`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}/comments', + path: CASE_COMMENTS_URL, method: 'post', params: { case_id: 'mock-id-1', @@ -100,7 +101,7 @@ describe('POST comment', () => { routeHandler = await createRoute(initPostCommentApi, 'post', true); const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}/comments', + path: CASE_COMMENTS_URL, method: 'post', params: { case_id: 'mock-id-1', diff --git a/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.ts b/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.ts index 70405af26f5762..a296d9815f251b 100644 --- a/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.ts +++ b/x-pack/plugins/case/server/routes/api/cases/comments/post_comment.ts @@ -15,11 +15,12 @@ import { CASE_SAVED_OBJECT } from '../../../../saved_object_types'; import { buildCommentUserActionItem } from '../../../../services/user_actions/helpers'; import { escapeHatch, transformNewComment, wrapError, flattenCaseSavedObject } from '../../utils'; import { RouteDeps } from '../../types'; +import { CASE_COMMENTS_URL } from '../../../../../common/constants'; export function initPostCommentApi({ caseService, router, userActionService }: RouteDeps) { router.post( { - path: '/api/cases/{case_id}/comments', + path: CASE_COMMENTS_URL, validate: { params: schema.object({ case_id: schema.string(), diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.test.ts b/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.test.ts index 66d39c3f11d28e..5b3b6e77b94037 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.test.ts @@ -15,6 +15,7 @@ import { import { mockCaseConfigure } from '../../__fixtures__/mock_saved_objects'; import { initGetCaseConfigure } from './get_configure'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; describe('GET configuration', () => { let routeHandler: RequestHandler; @@ -24,7 +25,7 @@ describe('GET configuration', () => { it('returns the configuration', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'get', }); @@ -44,7 +45,7 @@ describe('GET configuration', () => { it('handles undefined version correctly', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'get', }); @@ -78,7 +79,7 @@ describe('GET configuration', () => { it('returns an empty object when there is no configuration', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'get', }); @@ -95,7 +96,7 @@ describe('GET configuration', () => { it('returns an error if find throws an error', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'get', }); diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.ts b/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.ts index 2832edaa892d5d..03bec1fe72d39a 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/get_configure.ts @@ -7,11 +7,12 @@ import { CaseConfigureResponseRt } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; export function initGetCaseConfigure({ caseConfigureService, caseService, router }: RouteDeps) { router.get( { - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, validate: false, }, async (context, request, response) => { diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.test.ts b/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.test.ts index 62edaa0a4792aa..09692ff73b94bc 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.test.ts @@ -16,6 +16,7 @@ import { import { mockCaseConfigure } from '../../__fixtures__/mock_saved_objects'; import { initCaseConfigureGetActionConnector } from './get_connectors'; import { getActions } from '../../__mocks__/request_responses'; +import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../../common/constants'; describe('GET connectors', () => { let routeHandler: RequestHandler; @@ -25,7 +26,7 @@ describe('GET connectors', () => { it('returns the connectors', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure/connectors/_find', + path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, method: 'get', }); @@ -44,7 +45,7 @@ describe('GET connectors', () => { it('it throws an error when actions client is null', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure/connectors/_find', + path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, method: 'get', }); diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts b/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts index 3e9a1c96d55ed8..00575655d4c426 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts @@ -8,6 +8,8 @@ import Boom from 'boom'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; +import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../../common/constants'; + /* * Be aware that this api will only return 20 connectors */ @@ -17,7 +19,7 @@ const CASE_SERVICE_NOW_ACTION = '.servicenow'; export function initCaseConfigureGetActionConnector({ caseService, router }: RouteDeps) { router.get( { - path: '/api/cases/configure/connectors/_find', + path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, validate: false, }, async (context, request, response) => { diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.test.ts b/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.test.ts index 5b3d68a2586644..9b71f777b95ab0 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.test.ts @@ -15,6 +15,7 @@ import { import { mockCaseConfigure } from '../../__fixtures__/mock_saved_objects'; import { initPatchCaseConfigure } from './patch_configure'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; describe('PATCH configuration', () => { let routeHandler: RequestHandler; @@ -29,7 +30,7 @@ describe('PATCH configuration', () => { it('patch configuration', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'patch', body: { closure_type: 'close-by-pushing', @@ -61,7 +62,7 @@ describe('PATCH configuration', () => { routeHandler = await createRoute(initPatchCaseConfigure, 'patch', true); const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'patch', body: { closure_type: 'close-by-pushing', @@ -91,7 +92,7 @@ describe('PATCH configuration', () => { it('throw error when configuration have not being created', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'patch', body: { closure_type: 'close-by-pushing', @@ -113,7 +114,7 @@ describe('PATCH configuration', () => { it('throw error when the versions are different', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'patch', body: { closure_type: 'close-by-pushing', @@ -135,7 +136,7 @@ describe('PATCH configuration', () => { it('handles undefined version correctly', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'patch', body: { connector_id: 'no-version', version: mockCaseConfigure[0].version }, }); diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.ts b/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.ts index 3a1b9d5059cbc4..47f7d503e32b8d 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/patch_configure.ts @@ -16,11 +16,12 @@ import { } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { wrapError, escapeHatch } from '../../utils'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; export function initPatchCaseConfigure({ caseConfigureService, caseService, router }: RouteDeps) { router.patch( { - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, validate: { body: escapeHatch, }, diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/post_configure.test.ts b/x-pack/plugins/case/server/routes/api/cases/configure/post_configure.test.ts index 7e40cad5b12981..fb95cc53a17109 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/post_configure.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/post_configure.test.ts @@ -16,6 +16,7 @@ import { import { mockCaseConfigure } from '../../__fixtures__/mock_saved_objects'; import { initPostCaseConfigure } from './post_configure'; import { newConfiguration } from '../../__mocks__/request_responses'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; describe('POST configuration', () => { let routeHandler: RequestHandler; @@ -30,7 +31,7 @@ describe('POST configuration', () => { it('create configuration', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: newConfiguration, }); @@ -61,7 +62,7 @@ describe('POST configuration', () => { routeHandler = await createRoute(initPostCaseConfigure, 'post', true); const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: newConfiguration, }); @@ -90,7 +91,7 @@ describe('POST configuration', () => { it('throws when missing connector_id', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: { connector_name: 'My connector 2', @@ -111,7 +112,7 @@ describe('POST configuration', () => { it('throws when missing connector_name', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: { connector_id: '456', @@ -132,7 +133,7 @@ describe('POST configuration', () => { it('throws when missing closure_type', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: { connector_id: '456', @@ -153,7 +154,7 @@ describe('POST configuration', () => { it('it deletes the previous configuration', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: newConfiguration, }); @@ -172,7 +173,7 @@ describe('POST configuration', () => { it('it does NOT delete when not found', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: newConfiguration, }); @@ -191,7 +192,7 @@ describe('POST configuration', () => { it('it deletes all configuration', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: newConfiguration, }); @@ -214,7 +215,7 @@ describe('POST configuration', () => { it('returns an error if find throws an error', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: newConfiguration, }); @@ -232,7 +233,7 @@ describe('POST configuration', () => { it('returns an error if delete throws an error', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: newConfiguration, }); @@ -250,7 +251,7 @@ describe('POST configuration', () => { it('returns an error if post throws an error', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: { connector_id: 'throw-error-create', @@ -272,7 +273,7 @@ describe('POST configuration', () => { it('handles undefined version correctly', async () => { const req = httpServerMock.createKibanaRequest({ - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, method: 'post', body: { ...newConfiguration, connector_id: 'no-version' }, }); diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/post_configure.ts b/x-pack/plugins/case/server/routes/api/cases/configure/post_configure.ts index 2a23abf0cbf217..5c1693e728c377 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/post_configure.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/post_configure.ts @@ -16,11 +16,12 @@ import { } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { wrapError, escapeHatch } from '../../utils'; +import { CASE_CONFIGURE_URL } from '../../../../../common/constants'; export function initPostCaseConfigure({ caseConfigureService, caseService, router }: RouteDeps) { router.post( { - path: '/api/cases/configure', + path: CASE_CONFIGURE_URL, validate: { body: escapeHatch, }, diff --git a/x-pack/plugins/case/server/routes/api/cases/delete_cases.test.ts b/x-pack/plugins/case/server/routes/api/cases/delete_cases.test.ts index c5be6f78a15708..e655339e05eb12 100644 --- a/x-pack/plugins/case/server/routes/api/cases/delete_cases.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/delete_cases.test.ts @@ -16,6 +16,7 @@ import { mockCaseComments, } from '../__fixtures__'; import { initDeleteCasesApi } from './delete_cases'; +import { CASES_URL } from '../../../../common/constants'; describe('DELETE case', () => { let routeHandler: RequestHandler; @@ -24,7 +25,7 @@ describe('DELETE case', () => { }); it(`deletes the case. responds with 204`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', + path: CASES_URL, method: 'delete', query: { ids: ['mock-id-1'], @@ -43,7 +44,7 @@ describe('DELETE case', () => { }); it(`returns an error when thrown from deleteCase service`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', + path: CASES_URL, method: 'delete', query: { ids: ['not-real'], @@ -62,7 +63,7 @@ describe('DELETE case', () => { }); it(`returns an error when thrown from getAllCaseComments service`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', + path: CASES_URL, method: 'delete', query: { ids: ['bad-guy'], @@ -81,7 +82,7 @@ describe('DELETE case', () => { }); it(`returns an error when thrown from deleteComment service`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', + path: CASES_URL, method: 'delete', query: { ids: ['valid-id'], diff --git a/x-pack/plugins/case/server/routes/api/cases/delete_cases.ts b/x-pack/plugins/case/server/routes/api/cases/delete_cases.ts index 0214017ae5c290..20591637a6c230 100644 --- a/x-pack/plugins/case/server/routes/api/cases/delete_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/delete_cases.ts @@ -9,11 +9,12 @@ import { schema } from '@kbn/config-schema'; import { buildCaseUserActionItem } from '../../../services/user_actions/helpers'; import { RouteDeps } from '../types'; import { wrapError } from '../utils'; +import { CASES_URL } from '../../../../common/constants'; export function initDeleteCasesApi({ caseService, router, userActionService }: RouteDeps) { router.delete( { - path: '/api/cases', + path: CASES_URL, validate: { query: schema.object({ ids: schema.arrayOf(schema.string()), diff --git a/x-pack/plugins/case/server/routes/api/cases/find_cases.test.ts b/x-pack/plugins/case/server/routes/api/cases/find_cases.test.ts index 8fafb1af0eb826..7af1cee494457a 100644 --- a/x-pack/plugins/case/server/routes/api/cases/find_cases.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/find_cases.test.ts @@ -14,6 +14,7 @@ import { mockCases, } from '../__fixtures__'; import { initFindCasesApi } from './find_cases'; +import { CASES_URL } from '../../../../common/constants'; describe('GET all cases', () => { let routeHandler: RequestHandler; @@ -22,7 +23,7 @@ describe('GET all cases', () => { }); it(`gets all the cases`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', + path: `${CASES_URL}/_find`, method: 'get', }); diff --git a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts index b2716749e97491..40fc0301b058ac 100644 --- a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts @@ -15,6 +15,7 @@ import { CasesFindResponseRt, CasesFindRequestRt, throwErrors } from '../../../. import { transformCases, sortToSnake, wrapError, escapeHatch } from '../utils'; import { RouteDeps, TotalCommentByCase } from '../types'; import { CASE_SAVED_OBJECT } from '../../../saved_object_types'; +import { CASES_URL } from '../../../../common/constants'; const combineFilters = (filters: string[], operator: 'OR' | 'AND'): string => filters?.filter(i => i !== '').join(` ${operator} `); @@ -41,7 +42,7 @@ const buildFilter = ( export function initFindCasesApi({ caseService, router }: RouteDeps) { router.get( { - path: '/api/cases/_find', + path: `${CASES_URL}/_find`, validate: { query: escapeHatch, }, diff --git a/x-pack/plugins/case/server/routes/api/cases/get_case.test.ts b/x-pack/plugins/case/server/routes/api/cases/get_case.test.ts index 5912df2c40aa3f..a8c12d4734b53a 100644 --- a/x-pack/plugins/case/server/routes/api/cases/get_case.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/get_case.test.ts @@ -18,6 +18,7 @@ import { } from '../__fixtures__'; import { flattenCaseSavedObject } from '../utils'; import { initGetCaseApi } from './get_case'; +import { CASE_DETAILS_URL } from '../../../../common/constants'; describe('GET case', () => { let routeHandler: RequestHandler; @@ -26,7 +27,7 @@ describe('GET case', () => { }); it(`returns the case with empty case comments when includeComments is false`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}', + path: CASE_DETAILS_URL, method: 'get', params: { case_id: 'mock-id-1', @@ -55,7 +56,7 @@ describe('GET case', () => { }); it(`returns an error when thrown from getCase`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}', + path: CASE_DETAILS_URL, method: 'get', params: { case_id: 'abcdefg', @@ -78,7 +79,7 @@ describe('GET case', () => { }); it(`returns the case with case comments when includeComments is true`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}', + path: CASE_DETAILS_URL, method: 'get', params: { case_id: 'mock-id-1', @@ -102,7 +103,7 @@ describe('GET case', () => { }); it(`returns an error when thrown from getAllCaseComments`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases/{case_id}', + path: CASE_DETAILS_URL, method: 'get', params: { case_id: 'bad-guy', diff --git a/x-pack/plugins/case/server/routes/api/cases/get_case.ts b/x-pack/plugins/case/server/routes/api/cases/get_case.ts index ac32b20541a9c9..1e836d38c285c3 100644 --- a/x-pack/plugins/case/server/routes/api/cases/get_case.ts +++ b/x-pack/plugins/case/server/routes/api/cases/get_case.ts @@ -9,11 +9,12 @@ import { schema } from '@kbn/config-schema'; import { CaseResponseRt } from '../../../../common/api'; import { RouteDeps } from '../types'; import { flattenCaseSavedObject, wrapError } from '../utils'; +import { CASE_DETAILS_URL } from '../../../../common/constants'; export function initGetCaseApi({ caseService, router }: RouteDeps) { router.get( { - path: '/api/cases/{case_id}', + path: CASE_DETAILS_URL, validate: { params: schema.object({ case_id: schema.string(), diff --git a/x-pack/plugins/case/server/routes/api/cases/patch_cases.ts b/x-pack/plugins/case/server/routes/api/cases/patch_cases.ts index c36ea8964dc807..57f9fc20dbf342 100644 --- a/x-pack/plugins/case/server/routes/api/cases/patch_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/patch_cases.ts @@ -20,11 +20,12 @@ import { escapeHatch, wrapError, flattenCaseSavedObject } from '../utils'; import { RouteDeps } from '../types'; import { getCaseToUpdate } from './helpers'; import { buildCaseUserActions } from '../../../services/user_actions/helpers'; +import { CASES_URL } from '../../../../common/constants'; export function initPatchCasesApi({ caseService, router, userActionService }: RouteDeps) { router.patch( { - path: '/api/cases', + path: CASES_URL, validate: { body: escapeHatch, }, diff --git a/x-pack/plugins/case/server/routes/api/cases/post_case.test.ts b/x-pack/plugins/case/server/routes/api/cases/post_case.test.ts index 5899102224774b..0bbceb52140464 100644 --- a/x-pack/plugins/case/server/routes/api/cases/post_case.test.ts +++ b/x-pack/plugins/case/server/routes/api/cases/post_case.test.ts @@ -14,6 +14,7 @@ import { mockCases, } from '../__fixtures__'; import { initPostCaseApi } from './post_case'; +import { CASES_URL } from '../../../../common/constants'; describe('POST cases', () => { let routeHandler: RequestHandler; @@ -26,7 +27,7 @@ describe('POST cases', () => { }); it(`Posts a new case`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', + path: CASES_URL, method: 'post', body: { description: 'This is a brand new case of a bad meanie defacing data', @@ -49,7 +50,7 @@ describe('POST cases', () => { it(`Error if you passing status for a new case`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', + path: CASES_URL, method: 'post', body: { description: 'This is a brand new case of a bad meanie defacing data', @@ -70,7 +71,7 @@ describe('POST cases', () => { }); it(`Returns an error if postNewCase throws`, async () => { const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', + path: CASES_URL, method: 'post', body: { description: 'Throw an error', @@ -93,7 +94,7 @@ describe('POST cases', () => { routeHandler = await createRoute(initPostCaseApi, 'post', true); const request = httpServerMock.createKibanaRequest({ - path: '/api/cases', + path: CASES_URL, method: 'post', body: { description: 'This is a brand new case of a bad meanie defacing data', diff --git a/x-pack/plugins/case/server/routes/api/cases/post_case.ts b/x-pack/plugins/case/server/routes/api/cases/post_case.ts index 239b8bfdf9b29a..059a8b1affd54a 100644 --- a/x-pack/plugins/case/server/routes/api/cases/post_case.ts +++ b/x-pack/plugins/case/server/routes/api/cases/post_case.ts @@ -14,11 +14,12 @@ import { flattenCaseSavedObject, transformNewCase, wrapError, escapeHatch } from import { CasePostRequestRt, throwErrors, excess, CaseResponseRt } from '../../../../common/api'; import { buildCaseUserActionItem } from '../../../services/user_actions/helpers'; import { RouteDeps } from '../types'; +import { CASES_URL } from '../../../../common/constants'; export function initPostCaseApi({ caseService, router, userActionService }: RouteDeps) { router.post( { - path: '/api/cases', + path: CASES_URL, validate: { body: escapeHatch, }, diff --git a/x-pack/plugins/case/server/routes/api/cases/push_case.ts b/x-pack/plugins/case/server/routes/api/cases/push_case.ts index aff057adea37f5..94ebe24c3d2aef 100644 --- a/x-pack/plugins/case/server/routes/api/cases/push_case.ts +++ b/x-pack/plugins/case/server/routes/api/cases/push_case.ts @@ -15,6 +15,7 @@ import { flattenCaseSavedObject, wrapError, escapeHatch } from '../utils'; import { CaseExternalServiceRequestRt, CaseResponseRt, throwErrors } from '../../../../common/api'; import { buildCaseUserActionItem } from '../../../services/user_actions/helpers'; import { RouteDeps } from '../types'; +import { CASE_DETAILS_URL } from '../../../../common/constants'; export function initPushCaseUserActionApi({ caseConfigureService, @@ -24,7 +25,7 @@ export function initPushCaseUserActionApi({ }: RouteDeps) { router.post( { - path: '/api/cases/{case_id}/_push', + path: `${CASE_DETAILS_URL}/_push`, validate: { params: schema.object({ case_id: schema.string(), diff --git a/x-pack/plugins/case/server/routes/api/cases/reporters/get_reporters.ts b/x-pack/plugins/case/server/routes/api/cases/reporters/get_reporters.ts index 56862a96e05632..3fc96f506d1757 100644 --- a/x-pack/plugins/case/server/routes/api/cases/reporters/get_reporters.ts +++ b/x-pack/plugins/case/server/routes/api/cases/reporters/get_reporters.ts @@ -7,11 +7,12 @@ import { UsersRt } from '../../../../../common/api'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; +import { CASE_REPORTERS_URL } from '../../../../../common/constants'; export function initGetReportersApi({ caseService, router }: RouteDeps) { router.get( { - path: '/api/cases/reporters', + path: CASE_REPORTERS_URL, validate: {}, }, async (context, request, response) => { diff --git a/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts b/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts index f7431729d398c2..8f86dbc91f315d 100644 --- a/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts +++ b/x-pack/plugins/case/server/routes/api/cases/status/get_status.ts @@ -9,11 +9,12 @@ import { wrapError } from '../../utils'; import { CasesStatusResponseRt } from '../../../../../common/api'; import { CASE_SAVED_OBJECT } from '../../../../saved_object_types'; +import { CASE_STATUS_URL } from '../../../../../common/constants'; export function initGetCasesStatusApi({ caseService, router }: RouteDeps) { router.get( { - path: '/api/cases/status', + path: CASE_STATUS_URL, validate: {}, }, async (context, request, response) => { diff --git a/x-pack/plugins/case/server/routes/api/cases/tags/get_tags.ts b/x-pack/plugins/case/server/routes/api/cases/tags/get_tags.ts index 55e8fe2af128ca..1a3da659c58c4e 100644 --- a/x-pack/plugins/case/server/routes/api/cases/tags/get_tags.ts +++ b/x-pack/plugins/case/server/routes/api/cases/tags/get_tags.ts @@ -6,11 +6,12 @@ import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; +import { CASE_TAGS_URL } from '../../../../../common/constants'; export function initGetTagsApi({ caseService, router }: RouteDeps) { router.get( { - path: '/api/cases/tags', + path: CASE_TAGS_URL, validate: {}, }, async (context, request, response) => { diff --git a/x-pack/plugins/case/server/routes/api/cases/user_actions/get_all_user_actions.ts b/x-pack/plugins/case/server/routes/api/cases/user_actions/get_all_user_actions.ts index 2d4f16e46d5616..c90979f60d23f7 100644 --- a/x-pack/plugins/case/server/routes/api/cases/user_actions/get_all_user_actions.ts +++ b/x-pack/plugins/case/server/routes/api/cases/user_actions/get_all_user_actions.ts @@ -10,11 +10,12 @@ import { CaseUserActionsResponseRt } from '../../../../../common/api'; import { CASE_SAVED_OBJECT, CASE_COMMENT_SAVED_OBJECT } from '../../../../saved_object_types'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; +import { CASE_USER_ACTIONS_URL } from '../../../../../common/constants'; export function initGetAllUserActionsApi({ userActionService, router }: RouteDeps) { router.get( { - path: '/api/cases/{case_id}/user_actions', + path: CASE_USER_ACTIONS_URL, validate: { params: schema.object({ case_id: schema.string(), diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/defs.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/defs.tsx index de9c3c7e8f8f3e..064645019ca34c 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/defs.tsx +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/defs.tsx @@ -54,7 +54,11 @@ type ResolverColorNames = | 'activeNoWarning' | 'activeWarning' | 'fullLabelBackground' - | 'inertDescription'; + | 'inertDescription' + | 'labelBackgroundTerminatedProcess' + | 'labelBackgroundTerminatedTrigger' + | 'labelBackgroundRunningProcess' + | 'labelBackgroundRunningTrigger'; export const NamedColors: Record = { ok: saturate(0.5, resolverPalette.temperatures[0]), @@ -70,6 +74,10 @@ export const NamedColors: Record = { activeNoWarning: '#0078FF', activeWarning: '#C61F38', fullLabelBackground: '#3B3C41', + labelBackgroundTerminatedProcess: '#8A96A8', + labelBackgroundTerminatedTrigger: '#8A96A8', + labelBackgroundRunningProcess: '#8A96A8', + labelBackgroundRunningTrigger: '#8A96A8', inertDescription: '#747474', }; diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx b/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx index 10e331ffff02d5..3201e83164dba6 100644 --- a/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/endpoint/public/embeddables/resolver/view/process_event_dot.tsx @@ -7,7 +7,13 @@ import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; -import { htmlIdGenerator, EuiKeyboardAccessible } from '@elastic/eui'; +import { + htmlIdGenerator, + EuiKeyboardAccessible, + EuiButton, + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; import { useSelector } from 'react-redux'; import { applyMatrix3 } from '../lib/vector2'; import { Vector2, Matrix3, AdjacentProcessMap, ResolverProcessType } from '../types'; @@ -21,7 +27,7 @@ import * as selectors from '../store/selectors'; const nodeAssets = { runningProcessCube: { cubeSymbol: `#${SymbolIds.runningProcessCube}`, - labelBackground: NamedColors.fullLabelBackground, + labelBackground: NamedColors.labelBackgroundRunningProcess, descriptionFill: NamedColors.empty, descriptionText: i18n.translate('xpack.endpoint.resolver.runningProcess', { defaultMessage: 'Running Process', @@ -29,7 +35,7 @@ const nodeAssets = { }, runningTriggerCube: { cubeSymbol: `#${SymbolIds.runningTriggerCube}`, - labelBackground: NamedColors.fullLabelBackground, + labelBackground: NamedColors.labelBackgroundRunningTrigger, descriptionFill: NamedColors.empty, descriptionText: i18n.translate('xpack.endpoint.resolver.runningTrigger', { defaultMessage: 'Running Trigger', @@ -37,7 +43,7 @@ const nodeAssets = { }, terminatedProcessCube: { cubeSymbol: `#${SymbolIds.terminatedProcessCube}`, - labelBackground: NamedColors.fullLabelBackground, + labelBackground: NamedColors.labelBackgroundTerminatedProcess, descriptionFill: NamedColors.empty, descriptionText: i18n.translate('xpack.endpoint.resolver.terminatedProcess', { defaultMessage: 'Terminated Process', @@ -45,7 +51,7 @@ const nodeAssets = { }, terminatedTriggerCube: { cubeSymbol: `#${SymbolIds.terminatedTriggerCube}`, - labelBackground: NamedColors.fullLabelBackground, + labelBackground: NamedColors.labelBackgroundTerminatedTrigger, descriptionFill: NamedColors.empty, descriptionText: i18n.translate('xpack.endpoint.resolver.terminatedTrigger', { defaultMessage: 'Terminated Trigger', @@ -53,8 +59,46 @@ const nodeAssets = { }, }; +const ChildEventsButton = React.memo(() => { + return ( + ) => { + clickEvent.preventDefault(); + clickEvent.stopPropagation(); + }, [])} + color="ghost" + size="s" + iconType="arrowDown" + iconSide="right" + tabIndex={-1} + > + {i18n.translate('xpack.endpoint.resolver.relatedEvents', { + defaultMessage: 'Events', + })} + + ); +}); + +const RelatedAlertsButton = React.memo(() => { + return ( + ) => { + clickEvent.preventDefault(); + clickEvent.stopPropagation(); + }, [])} + color="ghost" + size="s" + tabIndex={-1} + > + {i18n.translate('xpack.endpoint.resolver.relatedAlerts', { + defaultMessage: 'Related Alerts', + })} + + ); +}); + /** - * A placeholder view for a process node. + * An artefact that represents a process node. */ export const ProcessEventDot = styled( React.memo( @@ -184,6 +228,7 @@ export const ProcessEventDot = styled( }, [animationTarget, dispatch, nodeId] ); + /* eslint-disable jsx-a11y/click-events-have-key-events */ /** * Key event handling (e.g. 'Enter'/'Space') is provisioned by the `EuiKeyboardAccessible` component @@ -256,13 +301,17 @@ export const ProcessEventDot = styled(
{descriptionText}
= 2 ? 'euiButton' : 'euiButton euiButton--small'} data-test-subject="nodeLabel" id={labelId} style={{ backgroundColor: labelBackground, - padding: '.15em 0', + padding: '.15rem 0', textAlign: 'center', - maxWidth: '100%', + maxWidth: '20rem', + minWidth: '12rem', + width: '60%', overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis', contain: 'content', + margin: '.25rem 0 .35rem 0', }} > - {eventModel.eventName(event)} + + {eventModel.eventName(event)} +
+ {magFactorX >= 2 && ( + + + + + + + + + )}
@@ -317,6 +383,8 @@ export const ProcessEventDot = styled( white-space: nowrap; will-change: left, top, width, height; contain: strict; + min-width: 280px; + min-height: 90px; //dasharray & dashoffset should be equal to "pull" the stroke back //when it is transitioned. diff --git a/x-pack/plugins/endpoint/scripts/resolver_generator.ts b/x-pack/plugins/endpoint/scripts/resolver_generator.ts index dd9e591f4b034f..2129bef0624b87 100644 --- a/x-pack/plugins/endpoint/scripts/resolver_generator.ts +++ b/x-pack/plugins/endpoint/scripts/resolver_generator.ts @@ -29,7 +29,7 @@ async function main() { alertIndex: { alias: 'ai', describe: 'index to store alerts in', - default: '.alerts-endpoint-000001', + default: 'events-endpoint-1', type: 'string', }, eventIndex: { diff --git a/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx index 68bfe41fd538e6..9494e4aa396a50 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_ec2/layout.tsx @@ -6,19 +6,19 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types'; +import { LayoutPropsWithTheme } from '../../../public/pages/metrics/metric_detail/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Section } from '../../../public/pages/metrics/components/section'; +import { Section } from '../../../public/pages/metrics/metric_detail/components/section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SubSection } from '../../../public/pages/metrics/components/sub_section'; +import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutContent } from '../../../public/pages/metrics/components/layout_content'; +import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis'; +import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { withTheme } from '../../../../observability/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MetadataDetails } from '../../../public/pages/metrics/components/metadata_details'; +import { MetadataDetails } from '../../../public/pages/metrics/metric_detail/components/metadata_details'; export const Layout = withTheme(({ metrics, theme, onChangeRangeTime }: LayoutPropsWithTheme) => ( diff --git a/x-pack/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx index 683851fec4d772..b2da7dec3f2e08 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx @@ -6,9 +6,9 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar'; -import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items'; -import { CloudToolbarItems } from '../shared/compontents/cloud_toolbar_items'; +import { ToolbarProps } from '../../../public/pages/metrics/inventory_view/components/toolbars/toolbar'; +import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_groupby_toolbar_items'; +import { CloudToolbarItems } from '../shared/components/cloud_toolbar_items'; import { SnapshotMetricType } from '../types'; export const AwsEC2ToolbarItems = (props: ToolbarProps) => { diff --git a/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx index 220c6c67f4aeaa..08b865f01b06c6 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_rds/layout.tsx @@ -6,17 +6,17 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types'; +import { LayoutPropsWithTheme } from '../../../public/pages/metrics/metric_detail/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Section } from '../../../public/pages/metrics/components/section'; +import { Section } from '../../../public/pages/metrics/metric_detail/components/section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SubSection } from '../../../public/pages/metrics/components/sub_section'; +import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis'; +import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { withTheme } from '../../../../observability/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutContent } from '../../../public/pages/metrics/components/layout_content'; +import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; export const Layout = withTheme(({ metrics, onChangeRangeTime, theme }: LayoutPropsWithTheme) => ( diff --git a/x-pack/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx index 24f05fd91589c8..2a8394b9dd3a41 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx @@ -6,9 +6,9 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar'; -import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items'; -import { CloudToolbarItems } from '../shared/compontents/cloud_toolbar_items'; +import { ToolbarProps } from '../../../public/pages/metrics/inventory_view/components/toolbars/toolbar'; +import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_groupby_toolbar_items'; +import { CloudToolbarItems } from '../shared/components/cloud_toolbar_items'; import { SnapshotMetricType } from '../types'; export const AwsRDSToolbarItems = (props: ToolbarProps) => { diff --git a/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx index 805236cf470827..e16f8ef6addde6 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_s3/layout.tsx @@ -6,17 +6,17 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types'; +import { LayoutPropsWithTheme } from '../../../public/pages/metrics/metric_detail/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Section } from '../../../public/pages/metrics/components/section'; +import { Section } from '../../../public/pages/metrics/metric_detail/components/section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SubSection } from '../../../public/pages/metrics/components/sub_section'; +import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis'; +import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { withTheme } from '../../../../observability/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutContent } from '../../../public/pages/metrics/components/layout_content'; +import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; export const Layout = withTheme(({ metrics, onChangeRangeTime, theme }: LayoutPropsWithTheme) => ( diff --git a/x-pack/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx index 54c3196ae28335..324bdd05860290 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx @@ -6,9 +6,9 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar'; -import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items'; -import { CloudToolbarItems } from '../shared/compontents/cloud_toolbar_items'; +import { ToolbarProps } from '../../../public/pages/metrics/inventory_view/components/toolbars/toolbar'; +import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_groupby_toolbar_items'; +import { CloudToolbarItems } from '../shared/components/cloud_toolbar_items'; import { SnapshotMetricType } from '../types'; export const AwsS3ToolbarItems = (props: ToolbarProps) => { diff --git a/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx b/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx index d581ac751682d5..ff13f2db104de9 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_sqs/layout.tsx @@ -6,17 +6,17 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types'; +import { LayoutPropsWithTheme } from '../../../public/pages/metrics/metric_detail/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Section } from '../../../public/pages/metrics/components/section'; +import { Section } from '../../../public/pages/metrics/metric_detail/components/section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SubSection } from '../../../public/pages/metrics/components/sub_section'; +import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis'; +import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { withTheme } from '../../../../observability/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutContent } from '../../../public/pages/metrics/components/layout_content'; +import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; export const Layout = withTheme(({ metrics, onChangeRangeTime, theme }: LayoutPropsWithTheme) => ( diff --git a/x-pack/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx index 22cb2740a4a7f7..3229c07034772c 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx @@ -6,9 +6,9 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar'; -import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items'; -import { CloudToolbarItems } from '../shared/compontents/cloud_toolbar_items'; +import { ToolbarProps } from '../../../public/pages/metrics/inventory_view/components/toolbars/toolbar'; +import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_groupby_toolbar_items'; +import { CloudToolbarItems } from '../shared/components/cloud_toolbar_items'; import { SnapshotMetricType } from '../types'; export const AwsSQSToolbarItems = (props: ToolbarProps) => { diff --git a/x-pack/plugins/infra/common/inventory_models/container/layout.tsx b/x-pack/plugins/infra/common/inventory_models/container/layout.tsx index 9956b2c9a2ce46..b9366a43e40c6b 100644 --- a/x-pack/plugins/infra/common/inventory_models/container/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/container/layout.tsx @@ -6,21 +6,21 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types'; +import { LayoutPropsWithTheme } from '../../../public/pages/metrics/metric_detail/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Section } from '../../../public/pages/metrics/components/section'; +import { Section } from '../../../public/pages/metrics/metric_detail/components/section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SubSection } from '../../../public/pages/metrics/components/sub_section'; +import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { GaugesSectionVis } from '../../../public/pages/metrics/components/gauges_section_vis'; +import { GaugesSectionVis } from '../../../public/pages/metrics/metric_detail/components/gauges_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis'; +import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { withTheme } from '../../../../observability/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutContent } from '../../../public/pages/metrics/components/layout_content'; +import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MetadataDetails } from '../../../public/pages/metrics/components/metadata_details'; +import { MetadataDetails } from '../../../public/pages/metrics/metric_detail/components/metadata_details'; export const Layout = withTheme(({ metrics, onChangeRangeTime, theme }: LayoutPropsWithTheme) => ( diff --git a/x-pack/plugins/infra/common/inventory_models/container/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/container/toolbar_items.tsx index dbb77cb3b677ee..f6c707726d9ca1 100644 --- a/x-pack/plugins/infra/common/inventory_models/container/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/container/toolbar_items.tsx @@ -6,8 +6,8 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar'; -import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items'; +import { ToolbarProps } from '../../../public/pages/metrics/inventory_view/components/toolbars/toolbar'; +import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_groupby_toolbar_items'; import { SnapshotMetricType } from '../types'; export const ContainerToolbarItems = (props: ToolbarProps) => { diff --git a/x-pack/plugins/infra/common/inventory_models/host/layout.tsx b/x-pack/plugins/infra/common/inventory_models/host/layout.tsx index 6d7d3612542208..e23118c747a9ba 100644 --- a/x-pack/plugins/infra/common/inventory_models/host/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/host/layout.tsx @@ -8,21 +8,21 @@ import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { withTheme } from '../../../../observability/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types'; +import { LayoutPropsWithTheme } from '../../../public/pages/metrics/metric_detail/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Section } from '../../../public/pages/metrics/components/section'; +import { Section } from '../../../public/pages/metrics/metric_detail/components/section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SubSection } from '../../../public/pages/metrics/components/sub_section'; +import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { GaugesSectionVis } from '../../../public/pages/metrics/components/gauges_section_vis'; +import { GaugesSectionVis } from '../../../public/pages/metrics/metric_detail/components/gauges_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis'; +import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; import * as Aws from '../shared/layouts/aws'; import * as Ngnix from '../shared/layouts/nginx'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MetadataDetails } from '../../../public/pages/metrics/components/metadata_details'; +import { MetadataDetails } from '../../../public/pages/metrics/metric_detail/components/metadata_details'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutContent } from '../../../public/pages/metrics/components/layout_content'; +import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; export const Layout = withTheme(({ metrics, onChangeRangeTime, theme }: LayoutPropsWithTheme) => ( diff --git a/x-pack/plugins/infra/common/inventory_models/host/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/host/toolbar_items.tsx index fc7696ee53c9d6..136264c0e26f4d 100644 --- a/x-pack/plugins/infra/common/inventory_models/host/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/host/toolbar_items.tsx @@ -6,8 +6,8 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar'; -import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items'; +import { ToolbarProps } from '../../../public/pages/metrics/inventory_view/components/toolbars/toolbar'; +import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_groupby_toolbar_items'; import { SnapshotMetricType } from '../types'; export const HostToolbarItems = (props: ToolbarProps) => { diff --git a/x-pack/plugins/infra/common/inventory_models/layouts.ts b/x-pack/plugins/infra/common/inventory_models/layouts.ts index b59ce010361ecd..df82f154b47b3b 100644 --- a/x-pack/plugins/infra/common/inventory_models/layouts.ts +++ b/x-pack/plugins/infra/common/inventory_models/layouts.ts @@ -23,7 +23,7 @@ import { Layout as AwsRDSLayout } from './aws_rds/layout'; import { Layout as AwsSQSLayout } from './aws_sqs/layout'; import { InventoryItemType } from './types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutProps } from '../../public/pages/metrics/types'; +import { LayoutProps } from '../../public/pages/metrics/metric_detail/types'; interface Layouts { [type: string]: ReactNode; diff --git a/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx b/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx index 8bc2f3ee8b4b31..271e32556ae286 100644 --- a/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx +++ b/x-pack/plugins/infra/common/inventory_models/pod/layout.tsx @@ -6,22 +6,22 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutPropsWithTheme } from '../../../public/pages/metrics/types'; +import { LayoutPropsWithTheme } from '../../../public/pages/metrics/metric_detail/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Section } from '../../../public/pages/metrics/components/section'; +import { Section } from '../../../public/pages/metrics/metric_detail/components/section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SubSection } from '../../../public/pages/metrics/components/sub_section'; +import { SubSection } from '../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { GaugesSectionVis } from '../../../public/pages/metrics/components/gauges_section_vis'; +import { GaugesSectionVis } from '../../../public/pages/metrics/metric_detail/components/gauges_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ChartSectionVis } from '../../../public/pages/metrics/components/chart_section_vis'; +import { ChartSectionVis } from '../../../public/pages/metrics/metric_detail/components/chart_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { withTheme } from '../../../../observability/public'; import * as Nginx from '../shared/layouts/nginx'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MetadataDetails } from '../../../public/pages/metrics/components/metadata_details'; +import { MetadataDetails } from '../../../public/pages/metrics/metric_detail/components/metadata_details'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutContent } from '../../../public/pages/metrics/components/layout_content'; +import { LayoutContent } from '../../../public/pages/metrics/metric_detail/components/layout_content'; export const Layout = withTheme(({ metrics, onChangeRangeTime, theme }: LayoutPropsWithTheme) => ( diff --git a/x-pack/plugins/infra/common/inventory_models/pod/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/pod/toolbar_items.tsx index d48c27efd88fda..c1cd375ff47bf9 100644 --- a/x-pack/plugins/infra/common/inventory_models/pod/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/pod/toolbar_items.tsx @@ -6,8 +6,8 @@ import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ToolbarProps } from '../../../public/components/inventory/toolbars/toolbar'; -import { MetricsAndGroupByToolbarItems } from '../shared/compontents/metrics_and_groupby_toolbar_items'; +import { ToolbarProps } from '../../../public/pages/metrics/inventory_view/components/toolbars/toolbar'; +import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_groupby_toolbar_items'; import { SnapshotMetricType } from '../types'; export const PodToolbarItems = (props: ToolbarProps) => { diff --git a/x-pack/plugins/infra/common/inventory_models/shared/compontents/cloud_toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/shared/components/cloud_toolbar_items.tsx similarity index 75% rename from x-pack/plugins/infra/common/inventory_models/shared/compontents/cloud_toolbar_items.tsx rename to x-pack/plugins/infra/common/inventory_models/shared/components/cloud_toolbar_items.tsx index 766a8ae8142f57..da5017b0f3a36f 100644 --- a/x-pack/plugins/infra/common/inventory_models/shared/compontents/cloud_toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/shared/components/cloud_toolbar_items.tsx @@ -7,11 +7,11 @@ import React from 'react'; import { EuiFlexItem } from '@elastic/eui'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ToolbarProps } from '../../../../public/components/inventory/toolbars/toolbar'; +import { ToolbarProps } from '../../../../public/pages/metrics/inventory_view/components/toolbars/toolbar'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { WaffleAccountsControls } from '../../../../public/components/waffle/waffle_accounts_controls'; +import { WaffleAccountsControls } from '../../../../public/pages/metrics/inventory_view/components/waffle/waffle_accounts_controls'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { WaffleRegionControls } from '../../../../public/components/waffle/waffle_region_controls'; +import { WaffleRegionControls } from '../../../../public/pages/metrics/inventory_view/components/waffle/waffle_region_controls'; type Props = ToolbarProps; diff --git a/x-pack/plugins/infra/common/inventory_models/shared/compontents/metrics_and_groupby_toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/shared/components/metrics_and_groupby_toolbar_items.tsx similarity index 81% rename from x-pack/plugins/infra/common/inventory_models/shared/compontents/metrics_and_groupby_toolbar_items.tsx rename to x-pack/plugins/infra/common/inventory_models/shared/components/metrics_and_groupby_toolbar_items.tsx index 738fce45ee99f1..bf37828ed0856f 100644 --- a/x-pack/plugins/infra/common/inventory_models/shared/compontents/metrics_and_groupby_toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/shared/components/metrics_and_groupby_toolbar_items.tsx @@ -7,16 +7,16 @@ import React, { useMemo } from 'react'; import { EuiFlexItem } from '@elastic/eui'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ToolbarProps } from '../../../../public/components/inventory/toolbars/toolbar'; +import { ToolbarProps } from '../../../../public/pages/metrics/inventory_view/components/toolbars/toolbar'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { WaffleMetricControls } from '../../../../public/components/waffle/metric_control'; +import { WaffleMetricControls } from '../../../../public/pages/metrics/inventory_view/components/waffle/metric_control'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { WaffleGroupByControls } from '../../../../public/components/waffle/waffle_group_by_controls'; +import { WaffleGroupByControls } from '../../../../public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls'; import { toGroupByOpt, toMetricOpt, // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../public/components/inventory/toolbars/toolbar_wrapper'; +} from '../../../../public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper'; import { SnapshotMetricType } from '../../types'; interface Props extends ToolbarProps { diff --git a/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx b/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx index 7a0b898d406ce7..6f2791534c17e8 100644 --- a/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx +++ b/x-pack/plugins/infra/common/inventory_models/shared/layouts/aws.tsx @@ -6,15 +6,15 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutPropsWithTheme } from '../../../../public/pages/metrics/types'; +import { LayoutPropsWithTheme } from '../../../../public/pages/metrics/metric_detail/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Section } from '../../../../public/pages/metrics/components/section'; +import { Section } from '../../../../public/pages/metrics/metric_detail/components/section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SubSection } from '../../../../public/pages/metrics/components/sub_section'; +import { SubSection } from '../../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { GaugesSectionVis } from '../../../../public/pages/metrics/components/gauges_section_vis'; +import { GaugesSectionVis } from '../../../../public/pages/metrics/metric_detail/components/gauges_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ChartSectionVis } from '../../../../public/pages/metrics/components/chart_section_vis'; +import { ChartSectionVis } from '../../../../public/pages/metrics/metric_detail/components/chart_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { withTheme } from '../../../../../observability/public'; diff --git a/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx b/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx index 79cea5150d498d..cf3a06994cc96c 100644 --- a/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx +++ b/x-pack/plugins/infra/common/inventory_models/shared/layouts/nginx.tsx @@ -6,13 +6,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LayoutPropsWithTheme } from '../../../../public/pages/metrics/types'; +import { LayoutPropsWithTheme } from '../../../../public/pages/metrics/metric_detail/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Section } from '../../../../public/pages/metrics/components/section'; +import { Section } from '../../../../public/pages/metrics/metric_detail/components/section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SubSection } from '../../../../public/pages/metrics/components/sub_section'; +import { SubSection } from '../../../../public/pages/metrics/metric_detail/components/sub_section'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ChartSectionVis } from '../../../../public/pages/metrics/components/chart_section_vis'; +import { ChartSectionVis } from '../../../../public/pages/metrics/metric_detail/components/chart_section_vis'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { withTheme } from '../../../../../observability/public'; diff --git a/x-pack/plugins/infra/common/inventory_models/toolbars.ts b/x-pack/plugins/infra/common/inventory_models/toolbars.ts index 39e9f5a260f7a9..e00cb094d04550 100644 --- a/x-pack/plugins/infra/common/inventory_models/toolbars.ts +++ b/x-pack/plugins/infra/common/inventory_models/toolbars.ts @@ -11,7 +11,7 @@ import { HostToolbarItems } from './host/toolbar_items'; import { ContainerToolbarItems } from './container/toolbar_items'; import { PodToolbarItems } from './pod/toolbar_items'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ToolbarProps } from '../../public/components/inventory/toolbars/toolbar'; +import { ToolbarProps } from '../../public/pages/metrics/inventory_view/components/toolbars/toolbar'; import { AwsEC2ToolbarItems } from './aws_ec2/toolbar_items'; import { AwsS3ToolbarItems } from './aws_s3/toolbar_items'; import { AwsRDSToolbarItems } from './aws_rds/toolbar_items'; diff --git a/x-pack/plugins/infra/common/saved_objects/inventory_view.ts b/x-pack/plugins/infra/common/saved_objects/inventory_view.ts index 8ae765f379add1..8933de57b04485 100644 --- a/x-pack/plugins/infra/common/saved_objects/inventory_view.ts +++ b/x-pack/plugins/infra/common/saved_objects/inventory_view.ts @@ -7,7 +7,7 @@ // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ElasticsearchMappingOf } from '../../server/utils/typed_elasticsearch_mappings'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { WaffleViewState } from '../../public/pages/inventory_view/hooks/use_waffle_view_state'; +import { WaffleViewState } from '../../public/pages/metrics/inventory_view/hooks/use_waffle_view_state'; export const inventoryViewSavedObjectType = 'inventory-view'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths diff --git a/x-pack/plugins/infra/common/saved_objects/metrics_explorer_view.ts b/x-pack/plugins/infra/common/saved_objects/metrics_explorer_view.ts index a9f11948446408..6eb08eabc15b5d 100644 --- a/x-pack/plugins/infra/common/saved_objects/metrics_explorer_view.ts +++ b/x-pack/plugins/infra/common/saved_objects/metrics_explorer_view.ts @@ -11,7 +11,7 @@ import { MetricsExplorerChartOptions, MetricsExplorerTimeOptions, // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../public/containers/metrics_explorer/use_metrics_explorer_options'; +} from '../../public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { SavedViewSavedObject } from '../../public/hooks/use_saved_view'; diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/alert_flyout.tsx b/x-pack/plugins/infra/public/components/alerting/metrics/alert_flyout.tsx index 914054e1fd9b7e..38709c117c8176 100644 --- a/x-pack/plugins/infra/public/components/alerting/metrics/alert_flyout.tsx +++ b/x-pack/plugins/infra/public/components/alerting/metrics/alert_flyout.tsx @@ -10,8 +10,8 @@ import { TriggerActionsContext } from '../../../utils/triggers_actions_context'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../../server/lib/alerting/metric_threshold/types'; -import { MetricsExplorerOptions } from '../../../containers/metrics_explorer/use_metrics_explorer_options'; import { MetricsExplorerSeries } from '../../../../common/http_api/metrics_explorer'; +import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; interface Props { visible?: boolean; diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx b/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx index 0e9da32aaa5093..665da25dcbfffd 100644 --- a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx +++ b/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx @@ -35,10 +35,10 @@ import { IErrorObject } from '../../../../../triggers_actions_ui/public/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MetricsExplorerOptions } from '../../../containers/metrics_explorer/use_metrics_explorer_options'; -import { MetricsExplorerKueryBar } from '../../metrics_explorer/kuery_bar'; import { MetricsExplorerSeries } from '../../../../common/http_api/metrics_explorer'; -import { MetricsExplorerGroupBy } from '../../metrics_explorer/group_by'; +import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar'; +import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; +import { MetricsExplorerGroupBy } from '../../../pages/metrics/metrics_explorer/components/group_by'; import { useSourceViaHttp } from '../../../containers/source/use_source_via_http'; interface AlertContextMeta { diff --git a/x-pack/plugins/infra/public/components/waffle/lib/type_guards.ts b/x-pack/plugins/infra/public/components/waffle/lib/type_guards.ts deleted file mode 100644 index f793afee1b948b..00000000000000 --- a/x-pack/plugins/infra/public/components/waffle/lib/type_guards.ts +++ /dev/null @@ -1,17 +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 { InfraWaffleMapGradientLegend, InfraWaffleMapStepLegend } from '../../../lib/lib'; - -export function isInfraWaffleMapStepLegend(subject: any): subject is InfraWaffleMapStepLegend { - return subject.type && subject.type === 'step'; -} - -export function isInfraWaffleMapGradientLegend( - subject: any -): subject is InfraWaffleMapGradientLegend { - return subject.type && subject.type === 'gradient'; -} diff --git a/x-pack/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx b/x-pack/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx index d2fa49e8d5d9fc..04f518aa9080fe 100644 --- a/x-pack/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx +++ b/x-pack/plugins/infra/public/containers/metrics_explorer/with_metrics_explorer_options_url_state.tsx @@ -17,7 +17,7 @@ import { MetricsExplorerYAxisMode, MetricsExplorerChartType, MetricsExplorerChartOptions, -} from './use_metrics_explorer_options'; +} from '../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; interface MetricsExplorerUrlState { timerange?: MetricsExplorerTimeOptions; diff --git a/x-pack/plugins/infra/public/containers/with_options.tsx b/x-pack/plugins/infra/public/containers/with_options.tsx deleted file mode 100644 index e18fc85a68d60b..00000000000000 --- a/x-pack/plugins/infra/public/containers/with_options.tsx +++ /dev/null @@ -1,64 +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 moment from 'moment'; -import React from 'react'; - -import { euiPaletteColorBlind } from '@elastic/eui'; -import { InfraFormatterType, InfraOptions } from '../lib/lib'; -import { RendererFunction } from '../utils/typed_react'; - -const euiVisColorPalette = euiPaletteColorBlind(); - -const initialState = { - options: { - timerange: { - interval: '1m', - to: moment.utc().valueOf(), - from: moment - .utc() - .subtract(1, 'h') - .valueOf(), - }, - wafflemap: { - formatter: InfraFormatterType.percent, - formatTemplate: '{{value}}', - metric: { type: 'cpu' }, - groupBy: [], - legend: { - type: 'gradient', - rules: [ - { - value: 0, - color: '#D3DAE6', - }, - { - value: 1, - color: euiVisColorPalette[1], - }, - ], - }, - }, - } as InfraOptions, -}; - -interface WithOptionsProps { - children: RendererFunction; -} - -type State = Readonly; - -export const withOptions = (WrappedComponent: React.ComponentType) => ( - {args => } -); - -export class WithOptions extends React.Component { - public readonly state: State = initialState; - - public render() { - return this.props.children(this.state.options); - } -} diff --git a/x-pack/plugins/infra/public/pages/infrastructure/index.tsx b/x-pack/plugins/infra/public/pages/infrastructure/index.tsx deleted file mode 100644 index d592ae3480fc95..00000000000000 --- a/x-pack/plugins/infra/public/pages/infrastructure/index.tsx +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -import React from 'react'; -import { Route, RouteComponentProps, Switch } from 'react-router-dom'; - -import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; -import { DocumentTitle } from '../../components/document_title'; -import { HelpCenterContent } from '../../components/help_center_content'; -import { RoutedTabs } from '../../components/navigation/routed_tabs'; -import { ColumnarPage } from '../../components/page'; -import { Header } from '../../components/header'; -import { MetricsExplorerOptionsContainer } from '../../containers/metrics_explorer/use_metrics_explorer_options'; -import { WithMetricsExplorerOptionsUrlState } from '../../containers/metrics_explorer/with_metrics_explorer_options_url_state'; -import { WithSource } from '../../containers/with_source'; -import { Source } from '../../containers/source'; -import { MetricsExplorerPage } from './metrics_explorer'; -import { SnapshotPage } from './snapshot'; -import { MetricsSettingsPage } from './settings'; -import { AppNavigation } from '../../components/navigation/app_navigation'; -import { SourceLoadingPage } from '../../components/source_loading_page'; -import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; -import { WaffleOptionsProvider } from '../inventory_view/hooks/use_waffle_options'; -import { WaffleTimeProvider } from '../inventory_view/hooks/use_waffle_time'; -import { WaffleFiltersProvider } from '../inventory_view/hooks/use_waffle_filters'; -import { AlertDropdown } from '../../components/alerting/metrics/alert_dropdown'; - -export const InfrastructurePage = ({ match }: RouteComponentProps) => { - const uiCapabilities = useKibana().services.application?.capabilities; - - return ( - - - - - - - - - -
- - - - - - - - - - - - - - ( - - {({ configuration, createDerivedIndexPattern }) => ( - - - {configuration ? ( - - ) : ( - - )} - - )} - - )} - /> - - - - - - - - ); -}; diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx index b1dab3bd3f6737..3ad242c77412db 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { Redirect, RouteComponentProps } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; -import { replaceMetricTimeInQueryString } from '../metrics/hooks/use_metrics_time'; +import { replaceMetricTimeInQueryString } from '../metrics/metric_detail/hooks/use_metrics_time'; import { useHostIpToName } from './use_host_ip_to_name'; import { getFromFromLocation, getToFromLocation } from './query_params'; import { LoadingPage } from '../../components/loading_page'; diff --git a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx index 72a41f5264244f..3d25e4c6c258d3 100644 --- a/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/redirect_to_node_detail.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { Redirect, RouteComponentProps } from 'react-router-dom'; -import { replaceMetricTimeInQueryString } from '../metrics/hooks/use_metrics_time'; +import { replaceMetricTimeInQueryString } from '../metrics/metric_detail/hooks/use_metrics_time'; import { getFromFromLocation, getToFromLocation } from './query_params'; import { InventoryItemType } from '../../../common/inventory_models/types'; import { LinkDescriptor } from '../../hooks/use_link_props'; diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index 531be40d2dc43b..cc88dd9e0d0f8f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -3,138 +3,133 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import { i18n } from '@kbn/i18n'; -import React, { useContext, useState } from 'react'; -import { euiStyled, EuiTheme, withTheme } from '../../../../observability/public'; + +import React from 'react'; +import { Route, RouteComponentProps, Switch } from 'react-router-dom'; + +import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import { DocumentTitle } from '../../components/document_title'; +import { HelpCenterContent } from '../../components/help_center_content'; +import { RoutedTabs } from '../../components/navigation/routed_tabs'; +import { ColumnarPage } from '../../components/page'; import { Header } from '../../components/header'; -import { ColumnarPage, PageContent } from '../../components/page'; -import { withMetricPageProviders } from './page_providers'; -import { useMetadata } from '../../containers/metadata/use_metadata'; +import { MetricsExplorerOptionsContainer } from './metrics_explorer/hooks/use_metrics_explorer_options'; +import { WithMetricsExplorerOptionsUrlState } from '../../containers/metrics_explorer/with_metrics_explorer_options_url_state'; +import { WithSource } from '../../containers/with_source'; import { Source } from '../../containers/source'; -import { InfraLoadingPanel } from '../../components/loading'; -import { findInventoryModel } from '../../../common/inventory_models'; -import { NavItem } from './lib/side_nav_context'; -import { NodeDetailsPage } from './components/node_details_page'; +import { MetricsExplorerPage } from './metrics_explorer'; +import { SnapshotPage } from './inventory_view'; +import { MetricsSettingsPage } from './settings'; +import { AppNavigation } from '../../components/navigation/app_navigation'; +import { SourceLoadingPage } from '../../components/source_loading_page'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; -import { InventoryItemType } from '../../../common/inventory_models/types'; -import { useMetricsTimeContext } from './hooks/use_metrics_time'; -import { useLinkProps } from '../../hooks/use_link_props'; - -const DetailPageContent = euiStyled(PageContent)` - overflow: auto; - background-color: ${props => props.theme.eui.euiColorLightestShade}; -`; - -interface Props { - theme: EuiTheme; - match: { - params: { - type: string; - node: string; - }; - }; -} - -export const MetricDetail = withMetricPageProviders( - withTheme(({ match }: Props) => { - const uiCapabilities = useKibana().services.application?.capabilities; - const nodeId = match.params.node; - const nodeType = match.params.type as InventoryItemType; - const inventoryModel = findInventoryModel(nodeType); - const { sourceId } = useContext(Source.Context); - const { - timeRange, - parsedTimeRange, - setTimeRange, - refreshInterval, - setRefreshInterval, - isAutoReloading, - setAutoReload, - triggerRefresh, - } = useMetricsTimeContext(); - const { - name, - filteredRequiredMetrics, - loading: metadataLoading, - cloudId, - metadata, - } = useMetadata(nodeId, nodeType, inventoryModel.requiredMetrics, sourceId, parsedTimeRange); - - const [sideNav, setSideNav] = useState([]); +import { WaffleOptionsProvider } from './inventory_view/hooks/use_waffle_options'; +import { WaffleTimeProvider } from './inventory_view/hooks/use_waffle_time'; +import { WaffleFiltersProvider } from './inventory_view/hooks/use_waffle_filters'; +import { AlertDropdown } from '../../components/alerting/metrics/alert_dropdown'; - const addNavItem = React.useCallback( - (item: NavItem) => { - if (!sideNav.some(n => n.id === item.id)) { - setSideNav([item, ...sideNav]); - } - }, - [sideNav] - ); +export const InfrastructurePage = ({ match }: RouteComponentProps) => { + const uiCapabilities = useKibana().services.application?.capabilities; - const metricsLinkProps = useLinkProps({ - app: 'metrics', - pathname: '/', - }); + return ( + + + + + + - const breadcrumbs = [ - { - ...metricsLinkProps, - text: i18n.translate('xpack.infra.header.infrastructureTitle', { - defaultMessage: 'Metrics', - }), - }, - { text: name }, - ]; + - if (metadataLoading && !filteredRequiredMetrics.length) { - return ( - - ); - } +
+ + + + + + + + + + - return ( - -
- - - {metadata ? ( - - ) : null} - - - ); - }) -); + + + ( + + {({ configuration, createDerivedIndexPattern }) => ( + + + {configuration ? ( + + ) : ( + + )} + + )} + + )} + /> + + + + + + + + ); +}; diff --git a/x-pack/plugins/infra/public/components/inventory/layout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx similarity index 78% rename from x-pack/plugins/infra/public/components/inventory/layout.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx index 3c91f9fa5946fc..bc8be9862fe636 100644 --- a/x-pack/plugins/infra/public/components/inventory/layout.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/layout.tsx @@ -8,16 +8,16 @@ import React from 'react'; import { useInterval } from 'react-use'; import { euiPaletteColorBlind } from '@elastic/eui'; -import { NodesOverview } from '../nodes_overview'; +import { NodesOverview } from './nodes_overview'; import { Toolbar } from './toolbars/toolbar'; -import { PageContent } from '../page'; -import { useSnapshot } from '../../containers/waffle/use_snaphot'; -import { useInventoryMeta } from '../../containers/inventory_metadata/use_inventory_meta'; -import { useWaffleTimeContext } from '../../pages/inventory_view/hooks/use_waffle_time'; -import { useWaffleFiltersContext } from '../../pages/inventory_view/hooks/use_waffle_filters'; -import { useWaffleOptionsContext } from '../../pages/inventory_view/hooks/use_waffle_options'; -import { useSourceContext } from '../../containers/source'; -import { InfraFormatterType, InfraWaffleMapGradientLegend } from '../../lib/lib'; +import { PageContent } from '../../../../components/page'; +import { useSnapshot } from '../hooks/use_snaphot'; +import { useInventoryMeta } from '../hooks/use_inventory_meta'; +import { useWaffleTimeContext } from '../hooks/use_waffle_time'; +import { useWaffleFiltersContext } from '../hooks/use_waffle_filters'; +import { useWaffleOptionsContext } from '../hooks/use_waffle_options'; +import { useSourceContext } from '../../../../containers/source'; +import { InfraFormatterType, InfraWaffleMapGradientLegend } from '../../../../lib/lib'; const euiVisColorPalette = euiPaletteColorBlind(); diff --git a/x-pack/plugins/infra/public/components/nodes_overview/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/nodes_overview.tsx similarity index 88% rename from x-pack/plugins/infra/public/components/nodes_overview/index.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/nodes_overview.tsx index ef22e0486f8921..afbfd2a079253d 100644 --- a/x-pack/plugins/infra/public/components/nodes_overview/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/nodes_overview.tsx @@ -10,18 +10,25 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { get, max, min } from 'lodash'; import React from 'react'; -import { euiStyled } from '../../../../observability/public'; -import { InfraFormatterType, InfraWaffleMapBounds, InfraWaffleMapOptions } from '../../lib/lib'; -import { createFormatter } from '../../utils/formatters'; -import { NoData } from '../empty_states'; -import { InfraLoadingPanel } from '../loading'; -import { Map } from '../waffle/map'; -import { ViewSwitcher } from '../waffle/view_switcher'; -import { TableView } from './table'; -import { SnapshotNode, SnapshotCustomMetricInputRT } from '../../../common/http_api/snapshot_api'; -import { convertIntervalToString } from '../../utils/convert_interval_to_string'; -import { InventoryItemType } from '../../../common/inventory_models/types'; -import { createFormatterForMetric } from '../metrics_explorer/helpers/create_formatter_for_metric'; +import { euiStyled } from '../../../../../../observability/public'; +import { + InfraFormatterType, + InfraWaffleMapBounds, + InfraWaffleMapOptions, +} from '../../../../lib/lib'; +import { createFormatter } from '../../../../utils/formatters'; +import { NoData } from '../../../../components/empty_states'; +import { InfraLoadingPanel } from '../../../../components/loading'; +import { Map } from './waffle/map'; +import { ViewSwitcher } from './waffle/view_switcher'; +import { TableView } from './table_view'; +import { + SnapshotNode, + SnapshotCustomMetricInputRT, +} from '../../../../../common/http_api/snapshot_api'; +import { convertIntervalToString } from '../../../../utils/convert_interval_to_string'; +import { InventoryItemType } from '../../../../../common/inventory_models/types'; +import { createFormatterForMetric } from '../../metrics_explorer/components/helpers/create_formatter_for_metric'; export interface KueryFilterQuery { kind: 'kuery'; diff --git a/x-pack/plugins/infra/public/pages/inventory_view/compontents/search_bar.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/search_bar.tsx similarity index 86% rename from x-pack/plugins/infra/public/pages/inventory_view/compontents/search_bar.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/search_bar.tsx index f4fde46d434f83..1f84ef3685f342 100644 --- a/x-pack/plugins/infra/public/pages/inventory_view/compontents/search_bar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/search_bar.tsx @@ -5,9 +5,9 @@ */ import React, { useContext } from 'react'; import { i18n } from '@kbn/i18n'; -import { Source } from '../../../containers/source'; -import { AutocompleteField } from '../../../components/autocomplete_field'; -import { WithKueryAutocompletion } from '../../../containers/with_kuery_autocompletion'; +import { Source } from '../../../../containers/source'; +import { AutocompleteField } from '../../../../components/autocomplete_field'; +import { WithKueryAutocompletion } from '../../../../containers/with_kuery_autocompletion'; import { useWaffleFiltersContext } from '../hooks/use_waffle_filters'; export const SearchBar = () => { diff --git a/x-pack/plugins/infra/public/components/nodes_overview/table.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/table_view.tsx similarity index 92% rename from x-pack/plugins/infra/public/components/nodes_overview/table.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/table_view.tsx index 82991076255eef..0557343e735f91 100644 --- a/x-pack/plugins/infra/public/components/nodes_overview/table.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/table_view.tsx @@ -9,13 +9,13 @@ import { i18n } from '@kbn/i18n'; import { last } from 'lodash'; import React, { useState, useCallback, useEffect } from 'react'; -import { createWaffleMapNode } from '../../containers/waffle/nodes_to_wafflemap'; -import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../lib/lib'; -import { fieldToName } from '../waffle/lib/field_to_display_name'; -import { NodeContextMenu } from '../waffle/node_context_menu'; -import { InventoryItemType } from '../../../common/inventory_models/types'; -import { SnapshotNode, SnapshotNodePath } from '../../../common/http_api/snapshot_api'; -import { CONTAINER_CLASSNAME } from '../../apps/start_app'; +import { createWaffleMapNode } from '../lib/nodes_to_wafflemap'; +import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../lib/lib'; +import { fieldToName } from '../lib/field_to_display_name'; +import { NodeContextMenu } from './waffle/node_context_menu'; +import { InventoryItemType } from '../../../../../common/inventory_models/types'; +import { SnapshotNode, SnapshotNodePath } from '../../../../../common/http_api/snapshot_api'; +import { CONTAINER_CLASSNAME } from '../../../../apps/start_app'; interface Props { nodes: SnapshotNode[]; diff --git a/x-pack/plugins/infra/public/components/inventory/toolbars/save_views.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/save_views.tsx similarity index 68% rename from x-pack/plugins/infra/public/components/inventory/toolbars/save_views.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/save_views.tsx index cb315d3e17b032..eb40ea595662a2 100644 --- a/x-pack/plugins/infra/public/components/inventory/toolbars/save_views.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/save_views.tsx @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { SavedViewsToolbarControls } from '../../saved_views/toolbar_control'; -import { inventoryViewSavedObjectType } from '../../../../common/saved_objects/inventory_view'; -import { useWaffleViewState } from '../../../pages/inventory_view/hooks/use_waffle_view_state'; +import { SavedViewsToolbarControls } from '../../../../../components/saved_views/toolbar_control'; +import { inventoryViewSavedObjectType } from '../../../../../../common/saved_objects/inventory_view'; +import { useWaffleViewState } from '../../hooks/use_waffle_view_state'; export const SavedViews = () => { const { viewState, defaultViewState, onViewChange } = useWaffleViewState(); diff --git a/x-pack/plugins/infra/public/components/inventory/toolbars/toolbar.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar.tsx similarity index 77% rename from x-pack/plugins/infra/public/components/inventory/toolbars/toolbar.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar.tsx index 63ab6d2f4465a4..3ac9c2c1896286 100644 --- a/x-pack/plugins/infra/public/components/inventory/toolbars/toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar.tsx @@ -10,15 +10,15 @@ import { SnapshotMetricInput, SnapshotGroupBy, SnapshotCustomMetricInput, -} from '../../../../common/http_api/snapshot_api'; -import { InventoryCloudAccount } from '../../../../common/http_api/inventory_meta_api'; -import { findToolbar } from '../../../../common/inventory_models/toolbars'; +} from '../../../../../../common/http_api/snapshot_api'; +import { InventoryCloudAccount } from '../../../../../../common/http_api/inventory_meta_api'; +import { findToolbar } from '../../../../../../common/inventory_models/toolbars'; import { ToolbarWrapper } from './toolbar_wrapper'; -import { InfraGroupByOptions } from '../../../lib/lib'; -import { IIndexPattern } from '../../../../../../../src/plugins/data/public'; -import { InventoryItemType } from '../../../../common/inventory_models/types'; -import { WaffleOptionsState } from '../../../pages/inventory_view/hooks/use_waffle_options'; +import { InfraGroupByOptions } from '../../../../../lib/lib'; +import { IIndexPattern } from '../../../../../../../../../src/plugins/data/public'; +import { InventoryItemType } from '../../../../../../common/inventory_models/types'; +import { WaffleOptionsState } from '../../hooks/use_waffle_options'; import { SavedViews } from './save_views'; export interface ToolbarProps diff --git a/x-pack/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx similarity index 95% rename from x-pack/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx index fefda94372cfb4..86cc0d8ee62e00 100644 --- a/x-pack/plugins/infra/public/components/inventory/toolbars/toolbar_wrapper.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx @@ -7,12 +7,12 @@ import React from 'react'; import { EuiFlexGroup } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { SnapshotMetricType } from '../../../../common/inventory_models/types'; -import { Toolbar } from '../../eui/toolbar'; +import { SnapshotMetricType } from '../../../../../../common/inventory_models/types'; +import { Toolbar } from '../../../../../components/eui/toolbar'; import { ToolbarProps } from './toolbar'; -import { fieldToName } from '../../waffle/lib/field_to_display_name'; -import { useSourceContext } from '../../../containers/source'; -import { useWaffleOptionsContext } from '../../../pages/inventory_view/hooks/use_waffle_options'; +import { fieldToName } from '../../lib/field_to_display_name'; +import { useSourceContext } from '../../../../../containers/source'; +import { useWaffleOptionsContext } from '../../hooks/use_waffle_options'; interface Props { children: (props: Omit) => React.ReactElement; diff --git a/x-pack/plugins/infra/public/components/waffle/conditional_tooltip.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/waffle/conditional_tooltip.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/conditional_tooltip.tsx diff --git a/x-pack/plugins/infra/public/components/waffle/custom_field_panel.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/custom_field_panel.tsx similarity index 97% rename from x-pack/plugins/infra/public/components/waffle/custom_field_panel.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/custom_field_panel.tsx index d2dc535f6d6b32..090d53f1ff7379 100644 --- a/x-pack/plugins/infra/public/components/waffle/custom_field_panel.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/custom_field_panel.tsx @@ -8,7 +8,7 @@ import { EuiButton, EuiComboBox, EuiForm, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { IFieldType } from 'src/plugins/data/public'; -import { InfraGroupByOptions } from '../../lib/lib'; +import { InfraGroupByOptions } from '../../../../../lib/lib'; interface Props { onSubmit: (field: string) => void; diff --git a/x-pack/plugins/infra/public/components/waffle/gradient_legend.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/gradient_legend.tsx similarity index 96% rename from x-pack/plugins/infra/public/components/waffle/gradient_legend.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/gradient_legend.tsx index 6b0c4bb41dc980..87f7e4cbff11e6 100644 --- a/x-pack/plugins/infra/public/components/waffle/gradient_legend.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/gradient_legend.tsx @@ -6,13 +6,13 @@ import React from 'react'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../../observability/public'; import { InfraFormatter, InfraWaffleMapBounds, InfraWaffleMapGradientLegend, InfraWaffleMapGradientRule, -} from '../../lib/lib'; +} from '../../../../../lib/lib'; interface Props { legend: InfraWaffleMapGradientLegend; diff --git a/x-pack/plugins/infra/public/components/waffle/group_name.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_name.tsx similarity index 96% rename from x-pack/plugins/infra/public/components/waffle/group_name.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_name.tsx index 01bd3600a1624e..308460203b132e 100644 --- a/x-pack/plugins/infra/public/components/waffle/group_name.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_name.tsx @@ -6,8 +6,8 @@ import { EuiLink, EuiToolTip } from '@elastic/eui'; import React from 'react'; -import { euiStyled } from '../../../../observability/public'; -import { InfraWaffleMapGroup, InfraWaffleMapOptions } from '../../lib/lib'; +import { euiStyled } from '../../../../../../../observability/public'; +import { InfraWaffleMapGroup, InfraWaffleMapOptions } from '../../../../../lib/lib'; interface Props { onDrilldown: (filter: string) => void; diff --git a/x-pack/plugins/infra/public/components/waffle/group_of_groups.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_groups.tsx similarity index 90% rename from x-pack/plugins/infra/public/components/waffle/group_of_groups.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_groups.tsx index 9634293587d49b..6b3f22007f580c 100644 --- a/x-pack/plugins/infra/public/components/waffle/group_of_groups.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_groups.tsx @@ -6,15 +6,15 @@ import React from 'react'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../../observability/public'; import { InfraWaffleMapBounds, InfraWaffleMapGroupOfGroups, InfraWaffleMapOptions, -} from '../../lib/lib'; +} from '../../../../../lib/lib'; import { GroupName } from './group_name'; import { GroupOfNodes } from './group_of_nodes'; -import { InventoryItemType } from '../../../common/inventory_models/types'; +import { InventoryItemType } from '../../../../../../common/inventory_models/types'; interface Props { onDrilldown: (filter: string) => void; diff --git a/x-pack/plugins/infra/public/components/waffle/group_of_nodes.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx similarity index 90% rename from x-pack/plugins/infra/public/components/waffle/group_of_nodes.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx index 6b82671617df7a..fc438ed4ca0a2e 100644 --- a/x-pack/plugins/infra/public/components/waffle/group_of_nodes.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/group_of_nodes.tsx @@ -6,15 +6,15 @@ import React from 'react'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../../observability/public'; import { InfraWaffleMapBounds, InfraWaffleMapGroupOfNodes, InfraWaffleMapOptions, -} from '../../lib/lib'; +} from '../../../../../lib/lib'; import { GroupName } from './group_name'; import { Node } from './node'; -import { InventoryItemType } from '../../../common/inventory_models/types'; +import { InventoryItemType } from '../../../../../../common/inventory_models/types'; interface Props { onDrilldown: (filter: string) => void; diff --git a/x-pack/plugins/infra/public/components/waffle/legend.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend.tsx similarity index 88% rename from x-pack/plugins/infra/public/components/waffle/legend.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend.tsx index 13e533b225d4df..ccb4cc71924f42 100644 --- a/x-pack/plugins/infra/public/components/waffle/legend.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend.tsx @@ -5,13 +5,13 @@ */ import React from 'react'; -import { euiStyled } from '../../../../observability/public'; -import { InfraFormatter, InfraWaffleMapBounds, InfraWaffleMapLegend } from '../../lib/lib'; +import { euiStyled } from '../../../../../../../observability/public'; +import { InfraFormatter, InfraWaffleMapBounds, InfraWaffleMapLegend } from '../../../../../lib/lib'; import { GradientLegend } from './gradient_legend'; import { LegendControls } from './legend_controls'; -import { isInfraWaffleMapGradientLegend, isInfraWaffleMapStepLegend } from './lib/type_guards'; +import { isInfraWaffleMapGradientLegend, isInfraWaffleMapStepLegend } from '../../lib/type_guards'; import { StepLegend } from './steps_legend'; -import { useWaffleOptionsContext } from '../../pages/inventory_view/hooks/use_waffle_options'; +import { useWaffleOptionsContext } from '../../hooks/use_waffle_options'; interface Props { legend: InfraWaffleMapLegend; bounds: InfraWaffleMapBounds; diff --git a/x-pack/plugins/infra/public/components/waffle/legend_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx similarity index 97% rename from x-pack/plugins/infra/public/components/waffle/legend_controls.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx index 26b5b1c0af727d..6ec21ad2e1b49b 100644 --- a/x-pack/plugins/infra/public/components/waffle/legend_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/legend_controls.tsx @@ -23,8 +23,8 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { SyntheticEvent, useState } from 'react'; -import { euiStyled } from '../../../../observability/public'; -import { InfraWaffleMapBounds } from '../../lib/lib'; +import { euiStyled } from '../../../../../../../observability/public'; +import { InfraWaffleMapBounds } from '../../../../../lib/lib'; interface Props { onChange: (options: { auto: boolean; bounds: InfraWaffleMapBounds }) => void; diff --git a/x-pack/plugins/infra/public/components/waffle/map.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx similarity index 84% rename from x-pack/plugins/infra/public/components/waffle/map.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx index 7cab2307dacfa1..e34c63d332fd87 100644 --- a/x-pack/plugins/infra/public/components/waffle/map.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/map.tsx @@ -5,20 +5,17 @@ */ import React from 'react'; -import { euiStyled } from '../../../../observability/public'; -import { nodesToWaffleMap } from '../../containers/waffle/nodes_to_wafflemap'; -import { - isWaffleMapGroupWithGroups, - isWaffleMapGroupWithNodes, -} from '../../containers/waffle/type_guards'; -import { InfraWaffleMapBounds, InfraWaffleMapOptions } from '../../lib/lib'; -import { AutoSizer } from '../auto_sizer'; +import { euiStyled } from '../../../../../../../observability/public'; +import { nodesToWaffleMap } from '../../lib/nodes_to_wafflemap'; +import { isWaffleMapGroupWithGroups, isWaffleMapGroupWithNodes } from '../../lib/type_guards'; +import { InfraWaffleMapBounds, InfraWaffleMapOptions } from '../../../../../lib/lib'; +import { AutoSizer } from '../../../../../components/auto_sizer'; import { GroupOfGroups } from './group_of_groups'; import { GroupOfNodes } from './group_of_nodes'; import { Legend } from './legend'; -import { applyWaffleMapLayout } from './lib/apply_wafflemap_layout'; -import { SnapshotNode } from '../../../common/http_api/snapshot_api'; -import { InventoryItemType } from '../../../common/inventory_models/types'; +import { applyWaffleMapLayout } from '../../lib/apply_wafflemap_layout'; +import { SnapshotNode } from '../../../../../../common/http_api/snapshot_api'; +import { InventoryItemType } from '../../../../../../common/inventory_models/types'; interface Props { nodes: SnapshotNode[]; diff --git a/x-pack/plugins/infra/public/components/waffle/metric_control/custom_metric_form.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx similarity index 97% rename from x-pack/plugins/infra/public/components/waffle/metric_control/custom_metric_form.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx index 26e42061ed10bd..aec624c0aff1f5 100644 --- a/x-pack/plugins/infra/public/components/waffle/metric_control/custom_metric_form.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx @@ -26,8 +26,11 @@ import { SnapshotCustomMetricInput, SNAPSHOT_CUSTOM_AGGREGATIONS, SnapshotCustomAggregationRT, -} from '../../../../common/http_api/snapshot_api'; -import { EuiTheme, withTheme } from '../../../../../../legacy/common/eui_styled_components'; +} from '../../../../../../../common/http_api/snapshot_api'; +import { + EuiTheme, + withTheme, +} from '../../../../../../../../../legacy/common/eui_styled_components'; interface SelectedOption { label: string; diff --git a/x-pack/plugins/infra/public/components/waffle/metric_control/get_custom_metric_label.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/get_custom_metric_label.ts similarity index 91% rename from x-pack/plugins/infra/public/components/waffle/metric_control/get_custom_metric_label.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/get_custom_metric_label.ts index 4f88c1b29c1f25..495cc8197d2e78 100644 --- a/x-pack/plugins/infra/public/components/waffle/metric_control/get_custom_metric_label.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/get_custom_metric_label.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { SnapshotCustomMetricInput } from '../../../../common/http_api/snapshot_api'; +import { SnapshotCustomMetricInput } from '../../../../../../../common/http_api/snapshot_api'; export const getCustomMetricLabel = (metric: SnapshotCustomMetricInput) => { const METRIC_LABELS = { diff --git a/x-pack/plugins/infra/public/components/waffle/metric_control/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx similarity index 97% rename from x-pack/plugins/infra/public/components/waffle/metric_control/index.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx index 0f2034fe9cb258..08d5b3e9e06706 100644 --- a/x-pack/plugins/infra/public/components/waffle/metric_control/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx @@ -13,14 +13,14 @@ import { SnapshotMetricInput, SnapshotCustomMetricInput, SnapshotCustomMetricInputRT, -} from '../../../../common/http_api/snapshot_api'; +} from '../../../../../../../common/http_api/snapshot_api'; import { CustomMetricForm } from './custom_metric_form'; import { getCustomMetricLabel } from './get_custom_metric_label'; import { MetricsContextMenu } from './metrics_context_menu'; import { ModeSwitcher } from './mode_switcher'; import { MetricsEditMode } from './metrics_edit_mode'; import { CustomMetricMode } from './types'; -import { SnapshotMetricType } from '../../../../common/inventory_models/types'; +import { SnapshotMetricType } from '../../../../../../../common/inventory_models/types'; interface Props { options: Array<{ text: string; value: string }>; diff --git a/x-pack/plugins/infra/public/components/waffle/metric_control/metrics_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_context_menu.tsx similarity index 94% rename from x-pack/plugins/infra/public/components/waffle/metric_control/metrics_context_menu.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_context_menu.tsx index 1aacf54244c375..1cdef493aee4f2 100644 --- a/x-pack/plugins/infra/public/components/waffle/metric_control/metrics_context_menu.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_context_menu.tsx @@ -9,11 +9,11 @@ import { SnapshotMetricInput, SnapshotCustomMetricInput, SnapshotCustomMetricInputRT, -} from '../../../../common/http_api/snapshot_api'; +} from '../../../../../../../common/http_api/snapshot_api'; import { SnapshotMetricTypeRT, SnapshotMetricType, -} from '../../../../common/inventory_models/types'; +} from '../../../../../../../common/inventory_models/types'; import { getCustomMetricLabel } from './get_custom_metric_label'; interface Props { diff --git a/x-pack/plugins/infra/public/components/waffle/metric_control/metrics_edit_mode.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx similarity index 92% rename from x-pack/plugins/infra/public/components/waffle/metric_control/metrics_edit_mode.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx index ba1f46815db20c..4d1bc906de0b92 100644 --- a/x-pack/plugins/infra/public/components/waffle/metric_control/metrics_edit_mode.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/metrics_edit_mode.tsx @@ -6,9 +6,12 @@ import React from 'react'; import { EuiFlexItem, EuiFlexGroup, EuiButtonIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { SnapshotCustomMetricInput } from '../../../../common/http_api/snapshot_api'; +import { SnapshotCustomMetricInput } from '../../../../../../../common/http_api/snapshot_api'; import { getCustomMetricLabel } from './get_custom_metric_label'; -import { EuiTheme, withTheme } from '../../../../../../legacy/common/eui_styled_components'; +import { + EuiTheme, + withTheme, +} from '../../../../../../../../../legacy/common/eui_styled_components'; interface Props { theme: EuiTheme; diff --git a/x-pack/plugins/infra/public/components/waffle/metric_control/mode_switcher.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx similarity index 95% rename from x-pack/plugins/infra/public/components/waffle/metric_control/mode_switcher.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx index 43bb904594c68f..acb740f1750c8d 100644 --- a/x-pack/plugins/infra/public/components/waffle/metric_control/mode_switcher.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/mode_switcher.tsx @@ -8,8 +8,11 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { CustomMetricMode } from './types'; -import { SnapshotCustomMetricInput } from '../../../../common/http_api/snapshot_api'; -import { EuiTheme, withTheme } from '../../../../../../legacy/common/eui_styled_components'; +import { SnapshotCustomMetricInput } from '../../../../../../../common/http_api/snapshot_api'; +import { + EuiTheme, + withTheme, +} from '../../../../../../../../../legacy/common/eui_styled_components'; interface Props { theme: EuiTheme; diff --git a/x-pack/plugins/infra/public/components/waffle/metric_control/types.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/types.ts similarity index 100% rename from x-pack/plugins/infra/public/components/waffle/metric_control/types.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/types.ts diff --git a/x-pack/plugins/infra/public/components/waffle/node.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx similarity index 93% rename from x-pack/plugins/infra/public/components/waffle/node.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx index 4eb5ccb8c1b4d9..fd5b5b01f329fa 100644 --- a/x-pack/plugins/infra/public/components/waffle/node.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node.tsx @@ -10,11 +10,15 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { ConditionalToolTip } from './conditional_tooltip'; -import { euiStyled } from '../../../../observability/public'; -import { InfraWaffleMapBounds, InfraWaffleMapNode, InfraWaffleMapOptions } from '../../lib/lib'; -import { colorFromValue } from './lib/color_from_value'; +import { euiStyled } from '../../../../../../../observability/public'; +import { + InfraWaffleMapBounds, + InfraWaffleMapNode, + InfraWaffleMapOptions, +} from '../../../../../lib/lib'; +import { colorFromValue } from '../../lib/color_from_value'; import { NodeContextMenu } from './node_context_menu'; -import { InventoryItemType } from '../../../common/inventory_models/types'; +import { InventoryItemType } from '../../../../../../common/inventory_models/types'; const initialState = { isPopoverOpen: false, diff --git a/x-pack/plugins/infra/public/components/waffle/node_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx similarity index 91% rename from x-pack/plugins/infra/public/components/waffle/node_context_menu.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx index 5f05cebd8f616a..275635a33ec266 100644 --- a/x-pack/plugins/infra/public/components/waffle/node_context_menu.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx @@ -9,12 +9,13 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo, useState } from 'react'; -import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../lib/lib'; -import { getNodeDetailUrl, getNodeLogsUrl } from '../../pages/link_to'; -import { createUptimeLink } from './lib/create_uptime_link'; -import { findInventoryModel, findInventoryFields } from '../../../common/inventory_models'; -import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; -import { InventoryItemType } from '../../../common/inventory_models/types'; +import { AlertFlyout } from '../../../../../components/alerting/metrics/alert_flyout'; +import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../../lib/lib'; +import { getNodeDetailUrl, getNodeLogsUrl } from '../../../../link_to'; +import { createUptimeLink } from '../../lib/create_uptime_link'; +import { findInventoryModel, findInventoryFields } from '../../../../../../common/inventory_models'; +import { useKibana } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { InventoryItemType } from '../../../../../../common/inventory_models/types'; import { Section, SectionLinkProps, @@ -23,9 +24,8 @@ import { SectionSubtitle, SectionLinks, SectionLink, -} from '../../../../observability/public'; -import { useLinkProps } from '../../hooks/use_link_props'; -import { AlertFlyout } from '../alerting/metrics/alert_flyout'; +} from '../../../../../../../observability/public'; +import { useLinkProps } from '../../../../../hooks/use_link_props'; interface Props { options: InfraWaffleMapOptions; diff --git a/x-pack/plugins/infra/public/components/waffle/steps_legend.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/steps_legend.tsx similarity index 95% rename from x-pack/plugins/infra/public/components/waffle/steps_legend.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/steps_legend.tsx index d2079d53dad71b..1ef0f2d0c42889 100644 --- a/x-pack/plugins/infra/public/components/waffle/steps_legend.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/steps_legend.tsx @@ -7,13 +7,13 @@ import { darken } from 'polished'; import React from 'react'; -import { euiStyled } from '../../../../observability/public'; +import { euiStyled } from '../../../../../../../observability/public'; import { InfraFormatter, InfraWaffleMapRuleOperator, InfraWaffleMapStepLegend, InfraWaffleMapStepRule, -} from '../../lib/lib'; +} from '../../../../../lib/lib'; const OPERATORS = { [InfraWaffleMapRuleOperator.gte]: '>=', diff --git a/x-pack/plugins/infra/public/components/waffle/view_switcher.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/view_switcher.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/waffle/view_switcher.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/view_switcher.tsx diff --git a/x-pack/plugins/infra/public/components/waffle/waffle_accounts_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_accounts_controls.tsx similarity index 94% rename from x-pack/plugins/infra/public/components/waffle/waffle_accounts_controls.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_accounts_controls.tsx index 56c3a205b05d48..a8b0cf21bce85e 100644 --- a/x-pack/plugins/infra/public/components/waffle/waffle_accounts_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_accounts_controls.tsx @@ -14,7 +14,7 @@ import { import React, { useCallback, useState, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { InventoryCloudAccount } from '../../../common/http_api/inventory_meta_api'; +import { InventoryCloudAccount } from '../../../../../../common/http_api/inventory_meta_api'; interface Props { accountId: string; @@ -24,7 +24,7 @@ interface Props { export const WaffleAccountsControls = (props: Props) => { const { accountId, options } = props; - const [isOpen, setIsOpen] = useState(); + const [isOpen, setIsOpen] = useState(false); const showPopover = useCallback(() => { setIsOpen(true); diff --git a/x-pack/plugins/infra/public/components/waffle/waffle_group_by_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx similarity index 95% rename from x-pack/plugins/infra/public/components/waffle/waffle_group_by_controls.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx index 1a3cef591bc077..bc763d2cf93785 100644 --- a/x-pack/plugins/infra/public/components/waffle/waffle_group_by_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_group_by_controls.tsx @@ -17,11 +17,11 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import { IFieldType } from 'src/plugins/data/public'; -import { InfraGroupByOptions } from '../../lib/lib'; +import { InfraGroupByOptions } from '../../../../../lib/lib'; import { CustomFieldPanel } from './custom_field_panel'; -import { euiStyled } from '../../../../observability/public'; -import { InventoryItemType } from '../../../common/inventory_models/types'; -import { SnapshotGroupBy } from '../../../common/http_api/snapshot_api'; +import { euiStyled } from '../../../../../../../observability/public'; +import { InventoryItemType } from '../../../../../../common/inventory_models/types'; +import { SnapshotGroupBy } from '../../../../../../common/http_api/snapshot_api'; interface Props { options: Array<{ text: string; field: string; toolTipContent?: string }>; diff --git a/x-pack/plugins/infra/public/components/waffle/waffle_inventory_switcher.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx similarity index 94% rename from x-pack/plugins/infra/public/components/waffle/waffle_inventory_switcher.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx index 21da10a0a76502..23e06823f407f4 100644 --- a/x-pack/plugins/infra/public/components/waffle/waffle_inventory_switcher.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_inventory_switcher.tsx @@ -14,9 +14,9 @@ import { import React, { useCallback, useState, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { findInventoryModel } from '../../../common/inventory_models'; -import { InventoryItemType } from '../../../common/inventory_models/types'; -import { useWaffleOptionsContext } from '../../pages/inventory_view/hooks/use_waffle_options'; +import { findInventoryModel } from '../../../../../../common/inventory_models'; +import { InventoryItemType } from '../../../../../../common/inventory_models/types'; +import { useWaffleOptionsContext } from '../../hooks/use_waffle_options'; const getDisplayNameForType = (type: InventoryItemType) => { const inventoryModel = findInventoryModel(type); diff --git a/x-pack/plugins/infra/public/components/waffle/waffle_region_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_region_controls.tsx similarity index 97% rename from x-pack/plugins/infra/public/components/waffle/waffle_region_controls.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_region_controls.tsx index 52377bf4b8296e..671e44f42ef6a5 100644 --- a/x-pack/plugins/infra/public/components/waffle/waffle_region_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_region_controls.tsx @@ -23,7 +23,7 @@ interface Props { export const WaffleRegionControls = (props: Props) => { const { region, options } = props; - const [isOpen, setIsOpen] = useState(); + const [isOpen, setIsOpen] = useState(false); const showPopover = useCallback(() => { setIsOpen(true); diff --git a/x-pack/plugins/infra/public/components/waffle/waffle_time_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_time_controls.tsx similarity index 95% rename from x-pack/plugins/infra/public/components/waffle/waffle_time_controls.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_time_controls.tsx index 458bb674afadef..7f190f21484d93 100644 --- a/x-pack/plugins/infra/public/components/waffle/waffle_time_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/waffle_time_controls.tsx @@ -8,7 +8,7 @@ import { EuiButtonEmpty, EuiDatePicker, EuiFormControlLayout } from '@elastic/eu import { FormattedMessage } from '@kbn/i18n/react'; import moment, { Moment } from 'moment'; import React, { useCallback } from 'react'; -import { useWaffleTimeContext } from '../../pages/inventory_view/hooks/use_waffle_time'; +import { useWaffleTimeContext } from '../../hooks/use_waffle_time'; export const WaffleTimeControls = () => { const { diff --git a/x-pack/plugins/infra/public/containers/inventory_metadata/use_inventory_meta.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_inventory_meta.ts similarity index 79% rename from x-pack/plugins/infra/public/containers/inventory_metadata/use_inventory_meta.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_inventory_meta.ts index 0ed1f3e35449b4..b038491690a137 100644 --- a/x-pack/plugins/infra/public/containers/inventory_metadata/use_inventory_meta.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_inventory_meta.ts @@ -7,13 +7,13 @@ import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; import { useEffect } from 'react'; -import { throwErrors, createPlainError } from '../../../common/runtime_types'; -import { useHTTPRequest } from '../../hooks/use_http_request'; +import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { useHTTPRequest } from '../../../../hooks/use_http_request'; import { InventoryMetaResponseRT, InventoryMetaResponse, -} from '../../../common/http_api/inventory_meta_api'; -import { InventoryItemType } from '../../../common/inventory_models/types'; +} from '../../../../../common/http_api/inventory_meta_api'; +import { InventoryItemType } from '../../../../../common/inventory_models/types'; export function useInventoryMeta(sourceId: string, nodeType: InventoryItemType) { const decodeResponse = (response: any) => { diff --git a/x-pack/plugins/infra/public/containers/waffle/use_snaphot.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts similarity index 83% rename from x-pack/plugins/infra/public/containers/waffle/use_snaphot.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts index 31f02f46caedac..3ec63d7b2de286 100644 --- a/x-pack/plugins/infra/public/containers/waffle/use_snaphot.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_snaphot.ts @@ -8,14 +8,17 @@ import { useEffect } from 'react'; import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; -import { throwErrors, createPlainError } from '../../../common/runtime_types'; -import { useHTTPRequest } from '../../hooks/use_http_request'; +import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { useHTTPRequest } from '../../../../hooks/use_http_request'; import { SnapshotNodeResponseRT, SnapshotNodeResponse, SnapshotGroupBy, -} from '../../../common/http_api/snapshot_api'; -import { InventoryItemType, SnapshotMetricType } from '../../../common/inventory_models/types'; +} from '../../../../../common/http_api/snapshot_api'; +import { + InventoryItemType, + SnapshotMetricType, +} from '../../../../../common/inventory_models/types'; export function useSnapshot( filterQuery: string | null | undefined, diff --git a/x-pack/plugins/infra/public/pages/inventory_view/hooks/use_waffle_filters.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts similarity index 90% rename from x-pack/plugins/infra/public/pages/inventory_view/hooks/use_waffle_filters.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts index 02c079dcaddc40..f6cbb59779039a 100644 --- a/x-pack/plugins/infra/public/pages/inventory_view/hooks/use_waffle_filters.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts @@ -10,10 +10,10 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; import createContainter from 'constate'; -import { useUrlState } from '../../../utils/use_url_state'; -import { useSourceContext } from '../../../containers/source'; -import { convertKueryToElasticSearchQuery } from '../../../utils/kuery'; -import { esKuery } from '../../../../../../../src/plugins/data/public'; +import { useUrlState } from '../../../../utils/use_url_state'; +import { useSourceContext } from '../../../../containers/source'; +import { convertKueryToElasticSearchQuery } from '../../../../utils/kuery'; +import { esKuery } from '../../../../../../../../src/plugins/data/public'; const validateKuery = (expression: string) => { try { diff --git a/x-pack/plugins/infra/public/pages/inventory_view/hooks/use_waffle_options.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts similarity index 95% rename from x-pack/plugins/infra/public/pages/inventory_view/hooks/use_waffle_options.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts index 2853917d5f6838..32bfe6e085b4e4 100644 --- a/x-pack/plugins/infra/public/pages/inventory_view/hooks/use_waffle_options.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_options.ts @@ -17,9 +17,9 @@ import { SnapshotMetricInputRT, SnapshotGroupByRT, SnapshotCustomMetricInputRT, -} from '../../../../common/http_api/snapshot_api'; -import { useUrlState } from '../../../utils/use_url_state'; -import { InventoryItemType, ItemTypeRT } from '../../../../common/inventory_models/types'; +} from '../../../../../common/http_api/snapshot_api'; +import { useUrlState } from '../../../../utils/use_url_state'; +import { InventoryItemType, ItemTypeRT } from '../../../../../common/inventory_models/types'; export const DEFAULT_WAFFLE_OPTIONS_STATE: WaffleOptionsState = { metric: { type: 'cpu' }, diff --git a/x-pack/plugins/infra/public/pages/inventory_view/hooks/use_waffle_time.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts similarity index 97% rename from x-pack/plugins/infra/public/pages/inventory_view/hooks/use_waffle_time.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts index 051b5e598cb757..db3abd37b58dd8 100644 --- a/x-pack/plugins/infra/public/pages/inventory_view/hooks/use_waffle_time.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_time.ts @@ -9,7 +9,7 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; import createContainer from 'constate'; -import { useUrlState } from '../../../utils/use_url_state'; +import { useUrlState } from '../../../../utils/use_url_state'; export const DEFAULT_WAFFLE_TIME_STATE: WaffleTimeState = { currentTime: Date.now(), diff --git a/x-pack/plugins/infra/public/pages/inventory_view/hooks/use_waffle_view_state.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts similarity index 100% rename from x-pack/plugins/infra/public/pages/inventory_view/hooks/use_waffle_view_state.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_waffle_view_state.ts diff --git a/x-pack/plugins/infra/public/pages/infrastructure/snapshot/index.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/index.tsx similarity index 98% rename from x-pack/plugins/infra/public/pages/infrastructure/snapshot/index.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/index.tsx index 48cc56388c0f24..e473aea7a1f0bc 100644 --- a/x-pack/plugins/infra/public/pages/infrastructure/snapshot/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/index.tsx @@ -20,7 +20,7 @@ import { ViewSourceConfigurationButton } from '../../../components/source_config import { Source } from '../../../containers/source'; import { useTrackPageview } from '../../../../../observability/public'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { Layout } from '../../../components/inventory/layout'; +import { Layout } from './components/layout'; import { useLinkProps } from '../../../hooks/use_link_props'; export const SnapshotPage = () => { diff --git a/x-pack/plugins/infra/public/components/waffle/lib/apply_wafflemap_layout.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/apply_wafflemap_layout.ts similarity index 94% rename from x-pack/plugins/infra/public/components/waffle/lib/apply_wafflemap_layout.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/apply_wafflemap_layout.ts index 5f3c06fcfbba77..68600ac5d2ce49 100644 --- a/x-pack/plugins/infra/public/components/waffle/lib/apply_wafflemap_layout.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/apply_wafflemap_layout.ts @@ -4,11 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { first, sortBy } from 'lodash'; -import { - isWaffleMapGroupWithGroups, - isWaffleMapGroupWithNodes, -} from '../../../containers/waffle/type_guards'; -import { InfraWaffleMapGroup } from '../../../lib/lib'; +import { isWaffleMapGroupWithGroups, isWaffleMapGroupWithNodes } from './type_guards'; +import { InfraWaffleMapGroup } from '../../../../lib/lib'; import { sizeOfSquares } from './size_of_squares'; export function getColumns(n: number, w = 1, h = 1) { diff --git a/x-pack/plugins/infra/public/components/waffle/lib/color_from_value.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/color_from_value.ts similarity index 98% rename from x-pack/plugins/infra/public/components/waffle/lib/color_from_value.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/color_from_value.ts index c082686bb9d63a..334865306ee88e 100644 --- a/x-pack/plugins/infra/public/components/waffle/lib/color_from_value.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/color_from_value.ts @@ -12,7 +12,7 @@ import { InfraWaffleMapLegend, InfraWaffleMapRuleOperator, InfraWaffleMapStepLegend, -} from '../../../lib/lib'; +} from '../../../../lib/lib'; import { isInfraWaffleMapGradientLegend, isInfraWaffleMapStepLegend } from './type_guards'; const OPERATOR_TO_FN = { diff --git a/x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.test.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_uptime_link.test.ts similarity index 96% rename from x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.test.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_uptime_link.test.ts index 902969c83ba393..5f760cf2f591e2 100644 --- a/x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.test.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_uptime_link.test.ts @@ -5,8 +5,8 @@ */ import { createUptimeLink } from './create_uptime_link'; -import { InfraWaffleMapOptions, InfraFormatterType } from '../../../lib/lib'; -import { SnapshotMetricType } from '../../../../common/inventory_models/types'; +import { InfraWaffleMapOptions, InfraFormatterType } from '../../../../lib/lib'; +import { SnapshotMetricType } from '../../../../../common/inventory_models/types'; const options: InfraWaffleMapOptions = { fields: { diff --git a/x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_uptime_link.ts similarity index 83% rename from x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_uptime_link.ts index 72b46f4fb5c7b7..6c089ee8d22f46 100644 --- a/x-pack/plugins/infra/public/components/waffle/lib/create_uptime_link.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_uptime_link.ts @@ -5,9 +5,9 @@ */ import { get } from 'lodash'; -import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../lib/lib'; -import { InventoryItemType } from '../../../../common/inventory_models/types'; -import { LinkDescriptor } from '../../../hooks/use_link_props'; +import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../lib/lib'; +import { InventoryItemType } from '../../../../../common/inventory_models/types'; +import { LinkDescriptor } from '../../../../hooks/use_link_props'; export const createUptimeLink = ( options: InfraWaffleMapOptions, diff --git a/x-pack/plugins/infra/public/components/waffle/lib/field_to_display_name.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/field_to_display_name.ts similarity index 100% rename from x-pack/plugins/infra/public/components/waffle/lib/field_to_display_name.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/field_to_display_name.ts diff --git a/x-pack/plugins/infra/public/containers/waffle/nodes_to_wafflemap.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/nodes_to_wafflemap.ts similarity index 97% rename from x-pack/plugins/infra/public/containers/waffle/nodes_to_wafflemap.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/nodes_to_wafflemap.ts index d2511ba7e669e6..469b54b2d9d684 100644 --- a/x-pack/plugins/infra/public/containers/waffle/nodes_to_wafflemap.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/nodes_to_wafflemap.ts @@ -11,9 +11,9 @@ import { InfraWaffleMapGroupOfGroups, InfraWaffleMapGroupOfNodes, InfraWaffleMapNode, -} from '../../lib/lib'; +} from '../../../../lib/lib'; import { isWaffleMapGroupWithGroups, isWaffleMapGroupWithNodes } from './type_guards'; -import { SnapshotNodePath, SnapshotNode } from '../../../common/http_api/snapshot_api'; +import { SnapshotNodePath, SnapshotNode } from '../../../../../common/http_api/snapshot_api'; export function createId(path: SnapshotNodePath[]) { return path.map(p => p.value).join('/'); diff --git a/x-pack/plugins/infra/public/components/waffle/lib/size_of_squares.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/size_of_squares.ts similarity index 100% rename from x-pack/plugins/infra/public/components/waffle/lib/size_of_squares.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/size_of_squares.ts diff --git a/x-pack/plugins/infra/public/containers/waffle/type_guards.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/type_guards.ts similarity index 55% rename from x-pack/plugins/infra/public/containers/waffle/type_guards.ts rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/type_guards.ts index 3e21e3a56a6c6a..62d02022d4c939 100644 --- a/x-pack/plugins/infra/public/containers/waffle/type_guards.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/type_guards.ts @@ -4,7 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { InfraWaffleMapGroupOfGroups, InfraWaffleMapGroupOfNodes } from '../../lib/lib'; +import { + InfraWaffleMapGroupOfGroups, + InfraWaffleMapGroupOfNodes, + InfraWaffleMapGradientLegend, + InfraWaffleMapStepLegend, +} from '../../../../lib/lib'; + +export function isInfraWaffleMapStepLegend(subject: any): subject is InfraWaffleMapStepLegend { + return subject.type && subject.type === 'step'; +} + +export function isInfraWaffleMapGradientLegend( + subject: any +): subject is InfraWaffleMapGradientLegend { + return subject.type && subject.type === 'gradient'; +} export function isWaffleMapGroupWithNodes(subject: any): subject is InfraWaffleMapGroupOfNodes { return subject && subject.nodes != null && Array.isArray(subject.nodes); diff --git a/x-pack/plugins/infra/public/pages/infrastructure/snapshot/toolbar.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/toolbar.tsx similarity index 75% rename from x-pack/plugins/infra/public/pages/infrastructure/snapshot/toolbar.tsx rename to x-pack/plugins/infra/public/pages/metrics/inventory_view/toolbar.tsx index ccdaa5e8dc7857..d6a87a0197f5f7 100644 --- a/x-pack/plugins/infra/public/pages/infrastructure/snapshot/toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/toolbar.tsx @@ -8,9 +8,9 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React from 'react'; import { Toolbar } from '../../../components/eui/toolbar'; -import { WaffleTimeControls } from '../../../components/waffle/waffle_time_controls'; -import { WaffleInventorySwitcher } from '../../../components/waffle/waffle_inventory_switcher'; -import { SearchBar } from '../../inventory_view/compontents/search_bar'; +import { WaffleTimeControls } from './components/waffle/waffle_time_controls'; +import { WaffleInventorySwitcher } from './components/waffle/waffle_inventory_switcher'; +import { SearchBar } from './components/search_bar'; export const SnapshotToolbar = () => ( diff --git a/x-pack/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/chart_section_vis.tsx similarity index 94% rename from x-pack/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/chart_section_vis.tsx index 290f0eda452ce2..588a0d84918c6c 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/chart_section_vis.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/chart_section_vis.tsx @@ -8,7 +8,7 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { Axis, Chart, niceTimeFormatter, Position, Settings, TooltipValue } from '@elastic/charts'; import { EuiPageContentBody } from '@elastic/eui'; -import { getChartTheme } from '../../../components/metrics_explorer/helpers/get_chart_theme'; +import { getChartTheme } from '../../metrics_explorer/components/helpers/get_chart_theme'; import { SeriesChart } from './series_chart'; import { getFormatter, @@ -19,8 +19,8 @@ import { seriesHasLessThen2DataPoints, } from './helpers'; import { ErrorMessage } from './error_message'; -import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting'; -import { useUiSetting } from '../../../../../../../src/plugins/kibana_react/public'; +import { useKibanaUiSetting } from '../../../../utils/use_kibana_ui_setting'; +import { useUiSetting } from '../../../../../../../../src/plugins/kibana_react/public'; import { VisSectionProps } from '../types'; export const ChartSectionVis = ({ diff --git a/x-pack/plugins/infra/public/pages/metrics/components/error_message.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/error_message.tsx similarity index 100% rename from x-pack/plugins/infra/public/pages/metrics/components/error_message.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/error_message.tsx diff --git a/x-pack/plugins/infra/public/pages/metrics/components/gauges_section_vis.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx similarity index 92% rename from x-pack/plugins/infra/public/pages/metrics/components/gauges_section_vis.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx index e069b52be8be70..0aab676b7d6c54 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/gauges_section_vis.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx @@ -16,9 +16,9 @@ import { import { get, last, max } from 'lodash'; import React, { ReactText } from 'react'; -import { euiStyled } from '../../../../../observability/public'; -import { createFormatter } from '../../../utils/formatters'; -import { InventoryFormatterType } from '../../../../common/inventory_models/types'; +import { euiStyled } from '../../../../../../observability/public'; +import { createFormatter } from '../../../../utils/formatters'; +import { InventoryFormatterType } from '../../../../../common/inventory_models/types'; import { SeriesOverrides, VisSectionProps } from '../types'; import { getChartName } from './helpers'; diff --git a/x-pack/plugins/infra/public/pages/metrics/components/helpers.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/helpers.ts similarity index 91% rename from x-pack/plugins/infra/public/pages/metrics/components/helpers.ts rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/helpers.ts index 4449196f2fb537..bb4ad326609520 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/helpers.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/helpers.ts @@ -7,15 +7,15 @@ import { ReactText } from 'react'; import Color from 'color'; import { get, first, last, min, max } from 'lodash'; -import { createFormatter } from '../../../utils/formatters'; -import { InfraDataSeries } from '../../../graphql/types'; +import { createFormatter } from '../../../../utils/formatters'; +import { InfraDataSeries } from '../../../../graphql/types'; import { InventoryVisTypeRT, InventoryFormatterType, InventoryVisType, -} from '../../../../common/inventory_models/types'; +} from '../../../../../common/inventory_models/types'; import { SeriesOverrides } from '../types'; -import { NodeDetailsMetricData } from '../../../../common/http_api/node_details_api'; +import { NodeDetailsMetricData } from '../../../../../common/http_api/node_details_api'; /** * Returns a formatter diff --git a/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx similarity index 90% rename from x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx index b089e2237c2e57..6d83017a3f6892 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/invalid_node.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx @@ -7,9 +7,9 @@ import { EuiButton, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { euiStyled } from '../../../../../observability/public'; -import { ViewSourceConfigurationButton } from '../../../components/source_configuration'; -import { useLinkProps } from '../../../hooks/use_link_props'; +import { euiStyled } from '../../../../../../observability/public'; +import { ViewSourceConfigurationButton } from '../../../../components/source_configuration'; +import { useLinkProps } from '../../../../hooks/use_link_props'; interface InvalidNodeErrorProps { nodeName: string; diff --git a/x-pack/plugins/infra/public/pages/metrics/components/layout_content.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/layout_content.tsx similarity index 84% rename from x-pack/plugins/infra/public/pages/metrics/components/layout_content.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/layout_content.tsx index 4e10f245acdccc..46201025175492 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/layout_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/layout_content.tsx @@ -5,7 +5,7 @@ */ import { EuiPageContent } from '@elastic/eui'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../observability/public'; export const LayoutContent = euiStyled(EuiPageContent)` position: relative; diff --git a/x-pack/plugins/infra/public/pages/metrics/components/metadata_details.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/metadata_details.tsx similarity index 97% rename from x-pack/plugins/infra/public/pages/metrics/components/metadata_details.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/metadata_details.tsx index 3bf492d398f2cf..7ca69dd56251d3 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/metadata_details.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/metadata_details.tsx @@ -8,8 +8,8 @@ import React, { useContext, useState, useCallback, useMemo } from 'react'; import { EuiButtonIcon, EuiFlexGrid, EuiFlexItem, EuiTitle, EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; -import { InfraMetadata } from '../../../../common/http_api'; -import { euiStyled } from '../../../../../observability/public'; +import { InfraMetadata } from '../../../../../common/http_api'; +import { euiStyled } from '../../../../../../observability/public'; import { MetadataContext } from '../containers/metadata_context'; interface FieldDef { diff --git a/x-pack/plugins/infra/public/pages/metrics/components/node_details_page.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx similarity index 90% rename from x-pack/plugins/infra/public/pages/metrics/components/node_details_page.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx index dd2a5f2bdb39e6..0d0bc8c82397e8 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/node_details_page.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx @@ -13,19 +13,19 @@ import { EuiHideFor, EuiTitle, } from '@elastic/eui'; -import { InfraTimerangeInput } from '../../../../common/http_api/snapshot_api'; -import { InventoryMetric, InventoryItemType } from '../../../../common/inventory_models/types'; -import { useNodeDetails } from '../../../containers/node_details/use_node_details'; +import { InfraTimerangeInput } from '../../../../../common/http_api/snapshot_api'; +import { InventoryMetric, InventoryItemType } from '../../../../../common/inventory_models/types'; +import { useNodeDetails } from '../hooks/use_node_details'; import { MetricsSideNav } from './side_nav'; -import { AutoSizer } from '../../../components/auto_sizer'; +import { AutoSizer } from '../../../../components/auto_sizer'; import { MetricsTimeControls } from './time_controls'; import { SideNavContext, NavItem } from '../lib/side_nav_context'; import { PageBody } from './page_body'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../observability/public'; import { MetricsTimeInput } from '../hooks/use_metrics_time'; -import { InfraMetadata } from '../../../../common/http_api/metadata_api'; +import { InfraMetadata } from '../../../../../common/http_api/metadata_api'; import { PageError } from './page_error'; -import { MetadataContext } from '../../../pages/metrics/containers/metadata_context'; +import { MetadataContext } from '../containers/metadata_context'; interface Props { name: string; diff --git a/x-pack/plugins/infra/public/pages/metrics/components/page_body.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_body.tsx similarity index 82% rename from x-pack/plugins/infra/public/pages/metrics/components/page_body.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_body.tsx index e651d6b92d9811..68166a6e53bfda 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/page_body.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_body.tsx @@ -6,13 +6,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { findLayout } from '../../../../common/inventory_models/layouts'; -import { InventoryItemType } from '../../../../common/inventory_models/types'; +import { findLayout } from '../../../../../common/inventory_models/layouts'; +import { InventoryItemType } from '../../../../../common/inventory_models/types'; import { MetricsTimeInput } from '../hooks/use_metrics_time'; -import { InfraLoadingPanel } from '../../../components/loading'; -import { NoData } from '../../../components/empty_states'; -import { NodeDetailsMetricData } from '../../../../common/http_api/node_details_api'; +import { InfraLoadingPanel } from '../../../../components/loading'; +import { NoData } from '../../../../components/empty_states'; +import { NodeDetailsMetricData } from '../../../../../common/http_api/node_details_api'; interface Props { loading: boolean; diff --git a/x-pack/plugins/infra/public/pages/metrics/components/page_error.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.tsx similarity index 90% rename from x-pack/plugins/infra/public/pages/metrics/components/page_error.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.tsx index e54cdcd151f6fc..bda2a5941e0230 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/page_error.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/page_error.tsx @@ -10,8 +10,8 @@ import { i18n } from '@kbn/i18n'; import { IHttpFetchError } from 'src/core/public'; import { InvalidNodeError } from './invalid_node'; // import { InfraMetricsErrorCodes } from '../../../../common/errors'; -import { DocumentTitle } from '../../../components/document_title'; -import { ErrorPageBody } from '../../error'; +import { DocumentTitle } from '../../../../components/document_title'; +import { ErrorPageBody } from '../../../error'; interface Props { name: string; diff --git a/x-pack/plugins/infra/public/pages/metrics/components/section.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/section.tsx similarity index 100% rename from x-pack/plugins/infra/public/pages/metrics/components/section.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/section.tsx diff --git a/x-pack/plugins/infra/public/pages/metrics/components/series_chart.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/series_chart.tsx similarity index 93% rename from x-pack/plugins/infra/public/pages/metrics/components/series_chart.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/series_chart.tsx index 849a5b89221650..0d7716ad3cc669 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/series_chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/series_chart.tsx @@ -13,8 +13,8 @@ import { BarSeriesStyle, AreaSeriesStyle, } from '@elastic/charts'; -import { InfraDataSeries } from '../../../graphql/types'; -import { InventoryVisType } from '../../../../common/inventory_models/types'; +import { InfraDataSeries } from '../../../../graphql/types'; +import { InventoryVisType } from '../../../../../common/inventory_models/types'; interface Props { id: string; diff --git a/x-pack/plugins/infra/public/pages/metrics/components/side_nav.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/side_nav.tsx similarity index 95% rename from x-pack/plugins/infra/public/pages/metrics/components/side_nav.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/side_nav.tsx index 94f97c7f45e61f..1cba3366acbbbf 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/side_nav.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/side_nav.tsx @@ -6,7 +6,7 @@ import { EuiHideFor, EuiPageSideBar, EuiShowFor, EuiSideNav } from '@elastic/eui'; import React, { useState, useCallback } from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../observability/public'; import { NavItem } from '../lib/side_nav_context'; interface Props { diff --git a/x-pack/plugins/infra/public/pages/metrics/components/sub_section.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/sub_section.tsx similarity index 94% rename from x-pack/plugins/infra/public/pages/metrics/components/sub_section.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/sub_section.tsx index 325d5102931354..7b269adc96638b 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/sub_section.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/sub_section.tsx @@ -6,7 +6,7 @@ import React, { isValidElement, cloneElement, FunctionComponent, Children, useMemo } from 'react'; import { EuiTitle } from '@elastic/eui'; -import { InventoryMetric } from '../../../../common/inventory_models/types'; +import { InventoryMetric } from '../../../../../common/inventory_models/types'; import { LayoutProps } from '../types'; type SubSectionProps = LayoutProps & { diff --git a/x-pack/plugins/infra/public/pages/metrics/components/time_controls.test.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.test.tsx similarity index 96% rename from x-pack/plugins/infra/public/pages/metrics/components/time_controls.test.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.test.tsx index 02ba506e8abe14..83f5187f8a46c0 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/time_controls.test.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.test.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -jest.mock('../../../utils/use_kibana_ui_setting', () => ({ +jest.mock('../../../../utils/use_kibana_ui_setting', () => ({ _esModule: true, useKibanaUiSetting: jest.fn(() => [ [ diff --git a/x-pack/plugins/infra/public/pages/metrics/components/time_controls.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.tsx similarity index 91% rename from x-pack/plugins/infra/public/pages/metrics/components/time_controls.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.tsx index cdbdc9bb7ecdb5..ef6486eac0fdb1 100644 --- a/x-pack/plugins/infra/public/pages/metrics/components/time_controls.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/time_controls.tsx @@ -6,10 +6,10 @@ import { EuiSuperDatePicker, OnRefreshChangeProps, OnTimeChangeProps } from '@elastic/eui'; import React, { useCallback } from 'react'; -import { euiStyled } from '../../../../../observability/public'; +import { euiStyled } from '../../../../../../observability/public'; import { MetricsTimeInput } from '../hooks/use_metrics_time'; -import { useKibanaUiSetting } from '../../../utils/use_kibana_ui_setting'; -import { mapKibanaQuickRangesToDatePickerRanges } from '../../../utils/map_timepicker_quickranges_to_datepicker_ranges'; +import { useKibanaUiSetting } from '../../../../utils/use_kibana_ui_setting'; +import { mapKibanaQuickRangesToDatePickerRanges } from '../../../../utils/map_timepicker_quickranges_to_datepicker_ranges'; interface MetricsTimeControlsProps { currentTimeRange: MetricsTimeInput; diff --git a/x-pack/plugins/infra/public/pages/metrics/containers/metadata_context.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/containers/metadata_context.ts similarity index 84% rename from x-pack/plugins/infra/public/pages/metrics/containers/metadata_context.ts rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/containers/metadata_context.ts index 4ecf7fa15548cb..b8995d27d4dbe9 100644 --- a/x-pack/plugins/infra/public/pages/metrics/containers/metadata_context.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/containers/metadata_context.ts @@ -5,5 +5,5 @@ */ import React from 'react'; -import { InfraMetadata } from '../../../../common/http_api'; +import { InfraMetadata } from '../../../../../common/http_api'; export const MetadataContext = React.createContext(null); diff --git a/x-pack/plugins/infra/public/pages/metrics/hooks/metrics_time.test.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/metrics_time.test.tsx similarity index 100% rename from x-pack/plugins/infra/public/pages/metrics/hooks/metrics_time.test.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/metrics_time.test.tsx diff --git a/x-pack/plugins/infra/public/containers/metadata/use_metadata.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metadata.ts similarity index 76% rename from x-pack/plugins/infra/public/containers/metadata/use_metadata.ts rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metadata.ts index 1ba016195bef40..d2a20d449d89ac 100644 --- a/x-pack/plugins/infra/public/containers/metadata/use_metadata.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metadata.ts @@ -8,12 +8,12 @@ import { useEffect } from 'react'; import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; -import { InfraMetadata, InfraMetadataRT } from '../../../common/http_api/metadata_api'; -import { useHTTPRequest } from '../../hooks/use_http_request'; -import { throwErrors, createPlainError } from '../../../common/runtime_types'; -import { InventoryMetric, InventoryItemType } from '../../../common/inventory_models/types'; -import { getFilteredMetrics } from './lib/get_filtered_metrics'; -import { MetricsTimeInput } from '../../pages/metrics/hooks/use_metrics_time'; +import { InfraMetadata, InfraMetadataRT } from '../../../../../common/http_api/metadata_api'; +import { useHTTPRequest } from '../../../../hooks/use_http_request'; +import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { InventoryMetric, InventoryItemType } from '../../../../../common/inventory_models/types'; +import { getFilteredMetrics } from '../lib/get_filtered_metrics'; +import { MetricsTimeInput } from './use_metrics_time'; export function useMetadata( nodeId: string, diff --git a/x-pack/plugins/infra/public/pages/metrics/hooks/use_metrics_time.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts similarity index 96% rename from x-pack/plugins/infra/public/pages/metrics/hooks/use_metrics_time.ts rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts index 2ed86863535ff7..98803ef2e69c6a 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hooks/use_metrics_time.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_metrics_time.ts @@ -12,8 +12,8 @@ import * as rt from 'io-ts'; import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { constant, identity } from 'fp-ts/lib/function'; -import { useUrlState } from '../../../utils/use_url_state'; -import { replaceStateKeyInQueryString } from '../../../utils/url_state'; +import { useUrlState } from '../../../../utils/use_url_state'; +import { replaceStateKeyInQueryString } from '../../../../utils/url_state'; const parseRange = (range: MetricsTimeInput) => { const parsedFrom = dateMath.parse(range.from.toString()); diff --git a/x-pack/plugins/infra/public/containers/node_details/use_node_details.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_node_details.ts similarity index 75% rename from x-pack/plugins/infra/public/containers/node_details/use_node_details.ts rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_node_details.ts index 189c48ba1a70c4..3bb6c98c8eac06 100644 --- a/x-pack/plugins/infra/public/containers/node_details/use_node_details.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/hooks/use_node_details.ts @@ -6,14 +6,14 @@ import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; -import { throwErrors, createPlainError } from '../../../common/runtime_types'; -import { useHTTPRequest } from '../../hooks/use_http_request'; +import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { useHTTPRequest } from '../../../../hooks/use_http_request'; import { NodeDetailsMetricDataResponseRT, NodeDetailsMetricDataResponse, -} from '../../../common/http_api/node_details_api'; -import { InventoryMetric, InventoryItemType } from '../../../common/inventory_models/types'; -import { InfraTimerangeInput } from '../../../common/http_api/snapshot_api'; +} from '../../../../../common/http_api/node_details_api'; +import { InventoryMetric, InventoryItemType } from '../../../../../common/inventory_models/types'; +import { InfraTimerangeInput } from '../../../../../common/http_api/snapshot_api'; export function useNodeDetails( metrics: InventoryMetric[], diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx new file mode 100644 index 00000000000000..197a735f7fd1f2 --- /dev/null +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +import React, { useContext, useState } from 'react'; +import { euiStyled, EuiTheme, withTheme } from '../../../../../observability/public'; +import { DocumentTitle } from '../../../components/document_title'; +import { Header } from '../../../components/header'; +import { ColumnarPage, PageContent } from '../../../components/page'; +import { withMetricPageProviders } from './page_providers'; +import { useMetadata } from './hooks/use_metadata'; +import { Source } from '../../../containers/source'; +import { InfraLoadingPanel } from '../../../components/loading'; +import { findInventoryModel } from '../../../../common/inventory_models'; +import { NavItem } from './lib/side_nav_context'; +import { NodeDetailsPage } from './components/node_details_page'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { InventoryItemType } from '../../../../common/inventory_models/types'; +import { useMetricsTimeContext } from './hooks/use_metrics_time'; +import { useLinkProps } from '../../../hooks/use_link_props'; + +const DetailPageContent = euiStyled(PageContent)` + overflow: auto; + background-color: ${props => props.theme.eui.euiColorLightestShade}; +`; + +interface Props { + theme: EuiTheme; + match: { + params: { + type: string; + node: string; + }; + }; +} + +export const MetricDetail = withMetricPageProviders( + withTheme(({ match }: Props) => { + const uiCapabilities = useKibana().services.application?.capabilities; + const nodeId = match.params.node; + const nodeType = match.params.type as InventoryItemType; + const inventoryModel = findInventoryModel(nodeType); + const { sourceId } = useContext(Source.Context); + const { + timeRange, + parsedTimeRange, + setTimeRange, + refreshInterval, + setRefreshInterval, + isAutoReloading, + setAutoReload, + triggerRefresh, + } = useMetricsTimeContext(); + const { + name, + filteredRequiredMetrics, + loading: metadataLoading, + cloudId, + metadata, + } = useMetadata(nodeId, nodeType, inventoryModel.requiredMetrics, sourceId, parsedTimeRange); + + const [sideNav, setSideNav] = useState([]); + + const addNavItem = React.useCallback( + (item: NavItem) => { + if (!sideNav.some(n => n.id === item.id)) { + setSideNav([item, ...sideNav]); + } + }, + [sideNav] + ); + + const metricsLinkProps = useLinkProps({ + app: 'metrics', + pathname: '/', + }); + + const breadcrumbs = [ + { + ...metricsLinkProps, + text: i18n.translate('xpack.infra.header.infrastructureTitle', { + defaultMessage: 'Metrics', + }), + }, + { text: name }, + ]; + + if (metadataLoading && !filteredRequiredMetrics.length) { + return ( + + ); + } + + return ( + +
+ + + {metadata ? ( + + ) : null} + + + ); + }) +); diff --git a/x-pack/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/lib/get_filtered_metrics.ts similarity index 78% rename from x-pack/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/lib/get_filtered_metrics.ts index b485c90700145a..57ff182f01963d 100644 --- a/x-pack/plugins/infra/public/containers/metadata/lib/get_filtered_metrics.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/lib/get_filtered_metrics.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { InfraMetadataFeature } from '../../../../common/http_api/metadata_api'; -import { InventoryMetric } from '../../../../common/inventory_models/types'; -import { metrics } from '../../../../common/inventory_models/metrics'; +import { InfraMetadataFeature } from '../../../../../common/http_api/metadata_api'; +import { InventoryMetric } from '../../../../../common/inventory_models/types'; +import { metrics } from '../../../../../common/inventory_models/metrics'; export const getFilteredMetrics = ( requiredMetrics: InventoryMetric[], diff --git a/x-pack/plugins/infra/public/pages/metrics/lib/side_nav_context.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/lib/side_nav_context.ts similarity index 100% rename from x-pack/plugins/infra/public/pages/metrics/lib/side_nav_context.ts rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/lib/side_nav_context.ts diff --git a/x-pack/plugins/infra/public/pages/metrics/page_providers.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/page_providers.tsx similarity index 91% rename from x-pack/plugins/infra/public/pages/metrics/page_providers.tsx rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/page_providers.tsx index d3f10adec06ed7..597977d9d2735d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/page_providers.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/page_providers.tsx @@ -6,7 +6,7 @@ import React from 'react'; -import { Source } from '../../containers/source'; +import { Source } from '../../../containers/source'; import { MetricsTimeProvider } from './hooks/use_metrics_time'; export const withMetricPageProviders = (Component: React.ComponentType) => ( diff --git a/x-pack/plugins/infra/public/pages/metrics/types.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/types.ts similarity index 86% rename from x-pack/plugins/infra/public/pages/metrics/types.ts rename to x-pack/plugins/infra/public/pages/metrics/metric_detail/types.ts index 2cc261df289772..3ec57e23a425d2 100644 --- a/x-pack/plugins/infra/public/pages/metrics/types.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/types.ts @@ -5,10 +5,10 @@ */ import rt from 'io-ts'; -import { EuiTheme } from '../../../../observability/public'; -import { InventoryFormatterTypeRT } from '../../../common/inventory_models/types'; +import { EuiTheme } from '../../../../../observability/public'; +import { InventoryFormatterTypeRT } from '../../../../common/inventory_models/types'; import { MetricsTimeInput } from './hooks/use_metrics_time'; -import { NodeDetailsMetricData } from '../../../common/http_api/node_details_api'; +import { NodeDetailsMetricData } from '../../../../common/http_api/node_details_api'; export interface LayoutProps { metrics?: NodeDetailsMetricData[]; diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/aggregation.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/aggregation.tsx similarity index 89% rename from x-pack/plugins/infra/public/components/metrics_explorer/aggregation.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/aggregation.tsx index 76fa519ab37567..0c0f7b33b3a4a0 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/aggregation.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/aggregation.tsx @@ -8,12 +8,12 @@ import { EuiSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useCallback } from 'react'; -import { MetricsExplorerAggregation } from '../../../common/http_api/metrics_explorer'; -import { MetricsExplorerOptions } from '../../containers/metrics_explorer/use_metrics_explorer_options'; +import { MetricsExplorerAggregation } from '../../../../../common/http_api/metrics_explorer'; +import { MetricsExplorerOptions } from '../hooks/use_metrics_explorer_options'; import { metricsExplorerAggregationRT, METRIC_EXPLORER_AGGREGATIONS, -} from '../../../common/http_api/metrics_explorer'; +} from '../../../../../common/http_api/metrics_explorer'; interface Props { options: MetricsExplorerOptions; diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/chart.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx similarity index 92% rename from x-pack/plugins/infra/public/components/metrics_explorer/chart.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx index 43b08f45eed34e..089e1abfc4c912 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart.tsx @@ -10,24 +10,24 @@ import { EuiTitle, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { Axis, Chart, niceTimeFormatter, Position, Settings, TooltipValue } from '@elastic/charts'; import { first, last } from 'lodash'; import moment from 'moment'; -import { MetricsExplorerSeries } from '../../../common/http_api/metrics_explorer'; +import { MetricsExplorerSeries } from '../../../../../common/http_api/metrics_explorer'; import { MetricsExplorerOptions, MetricsExplorerTimeOptions, MetricsExplorerYAxisMode, MetricsExplorerChartOptions, -} from '../../containers/metrics_explorer/use_metrics_explorer_options'; -import { euiStyled } from '../../../../observability/public'; +} from '../hooks/use_metrics_explorer_options'; +import { euiStyled } from '../../../../../../observability/public'; import { createFormatterForMetric } from './helpers/create_formatter_for_metric'; import { MetricExplorerSeriesChart } from './series_chart'; import { MetricsExplorerChartContextMenu } from './chart_context_menu'; -import { SourceQuery } from '../../graphql/types'; +import { SourceQuery } from '../../../../graphql/types'; import { MetricsExplorerEmptyChart } from './empty_chart'; import { MetricsExplorerNoMetrics } from './no_metrics'; import { getChartTheme } from './helpers/get_chart_theme'; -import { useKibanaUiSetting } from '../../utils/use_kibana_ui_setting'; +import { useKibanaUiSetting } from '../../../../utils/use_kibana_ui_setting'; import { calculateDomain } from './helpers/calculate_domain'; -import { useKibana, useUiSetting } from '../../../../../../src/plugins/kibana_react/public'; +import { useKibana, useUiSetting } from '../../../../../../../../src/plugins/kibana_react/public'; interface Props { title?: string | null; diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.test.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx similarity index 96% rename from x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.test.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx index 8ffef269a42ea1..5c0abb8fd845f2 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.test.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx @@ -7,9 +7,14 @@ import React from 'react'; import { MetricsExplorerChartContextMenu, createNodeDetailLink, Props } from './chart_context_menu'; import { ReactWrapper, mount } from 'enzyme'; -import { options, source, timeRange, chartOptions } from '../../utils/fixtures/metrics_explorer'; +import { + options, + source, + timeRange, + chartOptions, +} from '../../../../utils/fixtures/metrics_explorer'; import { Capabilities } from 'src/core/public'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; import { coreMock } from 'src/core/public/mocks'; const coreStartMock = coreMock.createStart(); diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx similarity index 91% rename from x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx index 75a04cbe9799e5..31086a21ca13f8 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/chart_context_menu.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx @@ -14,18 +14,18 @@ import { } from '@elastic/eui'; import DateMath from '@elastic/datemath'; import { Capabilities } from 'src/core/public'; -import { MetricsExplorerSeries } from '../../../common/http_api/metrics_explorer'; +import { AlertFlyout } from '../../../../components/alerting/metrics/alert_flyout'; +import { MetricsExplorerSeries } from '../../../../../common/http_api/metrics_explorer'; import { MetricsExplorerOptions, MetricsExplorerTimeOptions, MetricsExplorerChartOptions, -} from '../../containers/metrics_explorer/use_metrics_explorer_options'; +} from '../hooks/use_metrics_explorer_options'; import { createTSVBLink } from './helpers/create_tsvb_link'; -import { getNodeDetailUrl } from '../../pages/link_to/redirect_to_node_detail'; -import { SourceConfiguration } from '../../utils/source_configuration'; -import { InventoryItemType } from '../../../common/inventory_models/types'; -import { AlertFlyout } from '../alerting/metrics/alert_flyout'; -import { useLinkProps } from '../../hooks/use_link_props'; +import { getNodeDetailUrl } from '../../../link_to/redirect_to_node_detail'; +import { SourceConfiguration } from '../../../../utils/source_configuration'; +import { InventoryItemType } from '../../../../../common/inventory_models/types'; +import { useLinkProps } from '../../../../hooks/use_link_props'; export interface Props { options: MetricsExplorerOptions; diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/chart_options.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_options.tsx similarity index 98% rename from x-pack/plugins/infra/public/components/metrics_explorer/chart_options.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_options.tsx index 657c4cea30f3ab..ba28075ededb6e 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/chart_options.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_options.tsx @@ -19,7 +19,7 @@ import { MetricsExplorerChartOptions as ChartOptions, MetricsExplorerYAxisMode, MetricsExplorerChartType, -} from '../../containers/metrics_explorer/use_metrics_explorer_options'; +} from '../hooks/use_metrics_explorer_options'; interface Props { chartOptions: ChartOptions; diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/charts.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/charts.tsx similarity index 92% rename from x-pack/plugins/infra/public/components/metrics_explorer/charts.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/charts.tsx index 64e7b27f5722f0..ecec1163108756 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/charts.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/charts.tsx @@ -8,16 +8,16 @@ import { EuiButton, EuiFlexGrid, EuiFlexItem, EuiText, EuiHorizontalRule } from import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { MetricsExplorerResponse } from '../../../common/http_api/metrics_explorer'; +import { MetricsExplorerResponse } from '../../../../../common/http_api/metrics_explorer'; import { MetricsExplorerOptions, MetricsExplorerTimeOptions, MetricsExplorerChartOptions, -} from '../../containers/metrics_explorer/use_metrics_explorer_options'; -import { InfraLoadingPanel } from '../loading'; -import { NoData } from '../empty_states/no_data'; +} from '../hooks/use_metrics_explorer_options'; +import { InfraLoadingPanel } from '../../../../components/loading'; +import { NoData } from '../../../../components/empty_states/no_data'; import { MetricsExplorerChart } from './chart'; -import { SourceQuery } from '../../graphql/types'; +import { SourceQuery } from '../../../../graphql/types'; interface Props { loading: boolean; diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/empty_chart.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/empty_chart.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/metrics_explorer/empty_chart.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/empty_chart.tsx diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/group_by.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/group_by.tsx similarity index 72% rename from x-pack/plugins/infra/public/components/metrics_explorer/group_by.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/group_by.tsx index 750894fd0188bc..bfe8ddb2e08291 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/group_by.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/group_by.tsx @@ -9,8 +9,7 @@ import { i18n } from '@kbn/i18n'; import React, { useCallback } from 'react'; import { IFieldType } from 'src/plugins/data/public'; -import { MetricsExplorerOptions } from '../../containers/metrics_explorer/use_metrics_explorer_options'; -import { isDisplayable } from '../../utils/is_displayable'; +import { MetricsExplorerOptions } from '../hooks/use_metrics_explorer_options'; interface Props { options: MetricsExplorerOptions; @@ -27,18 +26,6 @@ export const MetricsExplorerGroupBy = ({ options, onChange, fields }: Props) => [onChange] ); - const metricPrefixes = options.metrics - .map( - metric => - (metric.field && - metric.field - .split(/\./) - .slice(0, 2) - .join('.')) || - null - ) - .filter(metric => metric) as string[]; - return ( singleSelection={true} selectedOptions={(options.groupBy && [{ label: options.groupBy }]) || []} options={fields - .filter(f => isDisplayable(f, metricPrefixes) && f.aggregatable && f.type === 'string') + .filter(f => f.aggregatable && f.type === 'string') .map(f => ({ label: f.name }))} onChange={handleChange} isClearable={true} diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domain.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/calculate_domain.ts similarity index 87% rename from x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domain.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/calculate_domain.ts index 90569854b833b3..811486d355f2e5 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domain.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/calculate_domain.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { min, max, sum, isNumber } from 'lodash'; -import { MetricsExplorerSeries } from '../../../../common/http_api/metrics_explorer'; -import { MetricsExplorerOptionsMetric } from '../../../containers/metrics_explorer/use_metrics_explorer_options'; +import { MetricsExplorerSeries } from '../../../../../../common/http_api/metrics_explorer'; +import { MetricsExplorerOptionsMetric } from '../../hooks/use_metrics_explorer_options'; const getMin = (values: Array) => { const minValue = min(values); diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/calculate_domian.test.ts similarity index 85% rename from x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/calculate_domian.test.ts index 4b45534d41db82..f94c6b6156ae4b 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/calculate_domian.test.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/calculate_domian.test.ts @@ -5,9 +5,9 @@ */ import { calculateDomain } from './calculate_domain'; -import { MetricsExplorerSeries } from '../../../../common/http_api/metrics_explorer'; -import { MetricsExplorerOptionsMetric } from '../../../containers/metrics_explorer/use_metrics_explorer_options'; -import { MetricsExplorerColor } from '../../../../common/color_palette'; +import { MetricsExplorerSeries } from '../../../../../../common/http_api/metrics_explorer'; +import { MetricsExplorerOptionsMetric } from '../../hooks/use_metrics_explorer_options'; +import { MetricsExplorerColor } from '../../../../../../common/color_palette'; describe('calculateDomain()', () => { const series: MetricsExplorerSeries = { id: 'test-01', diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric.ts similarity index 76% rename from x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric.ts index 33ec2ce2715a3c..d07a6b45f02bef 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metric.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer'; -import { createFormatter } from '../../../utils/formatters'; -import { InfraFormatterType } from '../../../lib/lib'; +import { MetricsExplorerMetric } from '../../../../../../common/http_api/metrics_explorer'; +import { createFormatter } from '../../../../../utils/formatters'; +import { InfraFormatterType } from '../../../../../lib/lib'; import { metricToFormat } from './metric_to_format'; export const createFormatterForMetric = (metric?: MetricsExplorerMetric) => { if (metric && metric.field) { diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metrics.test.ts similarity index 94% rename from x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metrics.test.ts index ec41e90e441a44..e039d5d4b3eeb4 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_formatter_for_metrics.test.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metrics.test.ts @@ -5,7 +5,7 @@ */ import { createFormatterForMetric } from './create_formatter_for_metric'; -import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer'; +import { MetricsExplorerMetric } from '../../../../../../common/http_api/metrics_explorer'; describe('createFormatterForMetric()', () => { it('should just work for count', () => { diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_metric_label.test.ts similarity index 88% rename from x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_metric_label.test.ts index cbf6904d246c70..367c472f414e42 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.test.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_metric_label.test.ts @@ -5,7 +5,7 @@ */ import { createMetricLabel } from './create_metric_label'; -import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer'; +import { MetricsExplorerMetric } from '../../../../../../common/http_api/metrics_explorer'; describe('createMetricLabel()', () => { it('should work with metrics with fields', () => { diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_metric_label.ts similarity index 80% rename from x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_metric_label.ts index b6453a81317b13..1607302a6259a5 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_metric_label.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_metric_label.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer'; +import { MetricsExplorerMetric } from '../../../../../../common/http_api/metrics_explorer'; export const createMetricLabel = (metric: MetricsExplorerMetric) => { return `${metric.aggregation}(${metric.field || ''})`; diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.test.ts similarity index 97% rename from x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.test.ts index 05637642b8dd9e..47bb4c8c57716d 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.test.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.test.ts @@ -5,14 +5,19 @@ */ import { createTSVBLink, createFilterFromOptions } from './create_tsvb_link'; -import { source, options, timeRange, chartOptions } from '../../../utils/fixtures/metrics_explorer'; +import { + source, + options, + timeRange, + chartOptions, +} from '../../../../../utils/fixtures/metrics_explorer'; import uuid from 'uuid'; import { OutputBuffer } from 'uuid/interfaces'; import { MetricsExplorerYAxisMode, MetricsExplorerChartType, -} from '../../../containers/metrics_explorer/use_metrics_explorer_options'; -import { MetricsExplorerOptions } from '../../../containers/metrics_explorer/use_metrics_explorer_options'; +} from '../../hooks/use_metrics_explorer_options'; +import { MetricsExplorerOptions } from '../../hooks/use_metrics_explorer_options'; jest.mock('uuid'); const mockedUuid = uuid as jest.Mocked; diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.ts similarity index 91% rename from x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.ts index 20706f563ec637..559422584f579a 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/create_tsvb_link.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.ts @@ -7,8 +7,8 @@ import { encode } from 'rison-node'; import uuid from 'uuid'; import { set } from 'lodash'; -import { colorTransformer, MetricsExplorerColor } from '../../../../common/color_palette'; -import { MetricsExplorerSeries } from '../../../../common/http_api/metrics_explorer'; +import { colorTransformer, MetricsExplorerColor } from '../../../../../../common/color_palette'; +import { MetricsExplorerSeries } from '../../../../../../common/http_api/metrics_explorer'; import { MetricsExplorerOptions, MetricsExplorerOptionsMetric, @@ -16,12 +16,12 @@ import { MetricsExplorerChartOptions, MetricsExplorerYAxisMode, MetricsExplorerChartType, -} from '../../../containers/metrics_explorer/use_metrics_explorer_options'; +} from '../../hooks/use_metrics_explorer_options'; import { metricToFormat } from './metric_to_format'; -import { InfraFormatterType } from '../../../lib/lib'; -import { SourceQuery } from '../../../graphql/types'; +import { InfraFormatterType } from '../../../../../lib/lib'; +import { SourceQuery } from '../../../../../graphql/types'; import { createMetricLabel } from './create_metric_label'; -import { LinkDescriptor } from '../../../hooks/use_link_props'; +import { LinkDescriptor } from '../../../../../hooks/use_link_props'; export const metricsExplorerMetricToTSVBMetric = (metric: MetricsExplorerOptionsMetric) => { if (metric.aggregation === 'rate') { diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/get_chart_theme.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/get_chart_theme.ts similarity index 100% rename from x-pack/plugins/infra/public/components/metrics_explorer/helpers/get_chart_theme.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/get_chart_theme.ts diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/metric_to_format.test.ts similarity index 90% rename from x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/metric_to_format.test.ts index 4cb27b4fb65c3e..6b27627a9fb5b2 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.test.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/metric_to_format.test.ts @@ -5,8 +5,8 @@ */ import { metricToFormat } from './metric_to_format'; -import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer'; -import { InfraFormatterType } from '../../../lib/lib'; +import { MetricsExplorerMetric } from '../../../../../../common/http_api/metrics_explorer'; +import { InfraFormatterType } from '../../../../../lib/lib'; describe('metricToFormat()', () => { it('should just work for numeric metrics', () => { const metric: MetricsExplorerMetric = { aggregation: 'avg', field: 'system.load.1' }; diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/metric_to_format.ts similarity index 82% rename from x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/metric_to_format.ts index 63272c86a5dc79..1dbbf97a32217b 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/helpers/metric_to_format.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/metric_to_format.ts @@ -5,8 +5,8 @@ */ import { last } from 'lodash'; -import { MetricsExplorerMetric } from '../../../../common/http_api/metrics_explorer'; -import { InfraFormatterType } from '../../../lib/lib'; +import { MetricsExplorerMetric } from '../../../../../../common/http_api/metrics_explorer'; +import { InfraFormatterType } from '../../../../../lib/lib'; export const metricToFormat = (metric?: MetricsExplorerMetric) => { if (metric && metric.field) { const suffix = last(metric.field.split(/\./)); diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx similarity index 84% rename from x-pack/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx index dcc160d05b6ad9..e9826e1ff39552 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx @@ -7,10 +7,9 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; -import { WithKueryAutocompletion } from '../../containers/with_kuery_autocompletion'; -import { AutocompleteField } from '../autocomplete_field'; -import { isDisplayable } from '../../utils/is_displayable'; -import { esKuery, IIndexPattern } from '../../../../../../src/plugins/data/public'; +import { WithKueryAutocompletion } from '../../../../containers/with_kuery_autocompletion'; +import { AutocompleteField } from '../../../../components/autocomplete_field'; +import { esKuery, IIndexPattern } from '../../../../../../../../src/plugins/data/public'; interface Props { derivedIndexPattern: IIndexPattern; @@ -51,7 +50,7 @@ export const MetricsExplorerKueryBar = ({ const filteredDerivedIndexPattern = { ...derivedIndexPattern, - fields: derivedIndexPattern.fields.filter(field => isDisplayable(field)), + fields: derivedIndexPattern.fields, }; const defaultPlaceholder = i18n.translate( diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/metrics.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/metrics.tsx similarity index 85% rename from x-pack/plugins/infra/public/components/metrics_explorer/metrics.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/metrics.tsx index 79d4122733c551..612735e2ba772e 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/metrics.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/metrics.tsx @@ -9,10 +9,9 @@ import { i18n } from '@kbn/i18n'; import React, { useCallback, useState } from 'react'; import { IFieldType } from 'src/plugins/data/public'; -import { colorTransformer, MetricsExplorerColor } from '../../../common/color_palette'; -import { MetricsExplorerMetric } from '../../../common/http_api/metrics_explorer'; -import { MetricsExplorerOptions } from '../../containers/metrics_explorer/use_metrics_explorer_options'; -import { isDisplayable } from '../../utils/is_displayable'; +import { colorTransformer, MetricsExplorerColor } from '../../../../../common/color_palette'; +import { MetricsExplorerMetric } from '../../../../../common/http_api/metrics_explorer'; +import { MetricsExplorerOptions } from '../hooks/use_metrics_explorer_options'; interface Props { autoFocus?: boolean; @@ -54,9 +53,7 @@ export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus = [onChange, options.aggregation, colors] ); - const comboOptions = fields - .filter(field => isDisplayable(field)) - .map(field => ({ label: field.name, value: field.name })); + const comboOptions = fields.map(field => ({ label: field.name, value: field.name })); const selectedOptions = options.metrics .filter(m => m.aggregation !== 'count') .map(metric => ({ diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/no_metrics.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/no_metrics.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/metrics_explorer/no_metrics.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/no_metrics.tsx diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/series_chart.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/series_chart.tsx similarity index 93% rename from x-pack/plugins/infra/public/components/metrics_explorer/series_chart.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/series_chart.tsx index ed7a994dd2bbec..ad7ce83539526a 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/series_chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/series_chart.tsx @@ -13,13 +13,13 @@ import { AreaSeriesStyle, BarSeriesStyle, } from '@elastic/charts'; -import { MetricsExplorerSeries } from '../../../common/http_api/metrics_explorer'; -import { colorTransformer, MetricsExplorerColor } from '../../../common/color_palette'; +import { MetricsExplorerSeries } from '../../../../../common/http_api/metrics_explorer'; +import { colorTransformer, MetricsExplorerColor } from '../../../../../common/color_palette'; import { createMetricLabel } from './helpers/create_metric_label'; import { MetricsExplorerOptionsMetric, MetricsExplorerChartType, -} from '../../containers/metrics_explorer/use_metrics_explorer_options'; +} from '../hooks/use_metrics_explorer_options'; interface Props { metric: MetricsExplorerOptionsMetric; diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/toolbar.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/toolbar.tsx similarity index 88% rename from x-pack/plugins/infra/public/components/metrics_explorer/toolbar.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/toolbar.tsx index 0fbb0b6acad176..81971bd31a9739 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/toolbar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/toolbar.tsx @@ -11,23 +11,23 @@ import { IIndexPattern } from 'src/plugins/data/public'; import { MetricsExplorerMetric, MetricsExplorerAggregation, -} from '../../../common/http_api/metrics_explorer'; +} from '../../../../../common/http_api/metrics_explorer'; import { MetricsExplorerOptions, MetricsExplorerTimeOptions, MetricsExplorerChartOptions, -} from '../../containers/metrics_explorer/use_metrics_explorer_options'; -import { Toolbar } from '../eui/toolbar'; +} from '../hooks/use_metrics_explorer_options'; +import { Toolbar } from '../../../../components/eui/toolbar'; import { MetricsExplorerKueryBar } from './kuery_bar'; import { MetricsExplorerMetrics } from './metrics'; import { MetricsExplorerGroupBy } from './group_by'; import { MetricsExplorerAggregationPicker } from './aggregation'; import { MetricsExplorerChartOptions as MetricsExplorerChartOptionsComponent } from './chart_options'; -import { SavedViewsToolbarControls } from '../saved_views/toolbar_control'; -import { MetricExplorerViewState } from '../../pages/infrastructure/metrics_explorer/use_metric_explorer_state'; -import { metricsExplorerViewSavedObjectType } from '../../../common/saved_objects/metrics_explorer_view'; -import { useKibanaUiSetting } from '../../utils/use_kibana_ui_setting'; -import { mapKibanaQuickRangesToDatePickerRanges } from '../../utils/map_timepicker_quickranges_to_datepicker_ranges'; +import { SavedViewsToolbarControls } from '../../../../components/saved_views/toolbar_control'; +import { MetricExplorerViewState } from '../hooks/use_metric_explorer_state'; +import { metricsExplorerViewSavedObjectType } from '../../../../../common/saved_objects/metrics_explorer_view'; +import { useKibanaUiSetting } from '../../../../utils/use_kibana_ui_setting'; +import { mapKibanaQuickRangesToDatePickerRanges } from '../../../../utils/map_timepicker_quickranges_to_datepicker_ranges'; interface Props { derivedIndexPattern: IIndexPattern; diff --git a/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.test.tsx similarity index 96% rename from x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.test.tsx index 874ac0987023bd..f0734f76cfacd9 100644 --- a/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.test.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.test.tsx @@ -6,14 +6,14 @@ import { renderHook, act } from '@testing-library/react-hooks'; import { useMetricsExplorerState } from './use_metric_explorer_state'; -import { MetricsExplorerOptionsContainer } from '../../../containers/metrics_explorer/use_metrics_explorer_options'; +import { MetricsExplorerOptionsContainer } from './use_metrics_explorer_options'; import React from 'react'; import { source, derivedIndexPattern, resp, createSeries, -} from '../../../utils/fixtures/metrics_explorer'; +} from '../../../../utils/fixtures/metrics_explorer'; const renderUseMetricsExplorerStateHook = () => renderHook(props => useMetricsExplorerState(props.source, props.derivedIndexPattern), { @@ -27,7 +27,7 @@ const renderUseMetricsExplorerStateHook = () => const mockedUseMetricsExplorerData = jest.fn(); -jest.mock('../../../containers/metrics_explorer/use_metrics_explorer_data', () => { +jest.mock('./use_metrics_explorer_data', () => { return { useMetricsExplorerData: () => { return mockedUseMetricsExplorerData(); diff --git a/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts similarity index 92% rename from x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts index 88e6d9d800661d..8a9ed901de0b06 100644 --- a/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/use_metric_explorer_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts @@ -9,15 +9,15 @@ import { IIndexPattern } from 'src/plugins/data/public'; import { MetricsExplorerMetric, MetricsExplorerAggregation, -} from '../../../../common/http_api/metrics_explorer'; -import { useMetricsExplorerData } from '../../../containers/metrics_explorer/use_metrics_explorer_data'; +} from '../../../../../common/http_api/metrics_explorer'; +import { useMetricsExplorerData } from './use_metrics_explorer_data'; import { MetricsExplorerOptionsContainer, MetricsExplorerChartOptions, MetricsExplorerTimeOptions, MetricsExplorerOptions, -} from '../../../containers/metrics_explorer/use_metrics_explorer_options'; -import { SourceQuery } from '../../../graphql/types'; +} from './use_metrics_explorer_options'; +import { SourceQuery } from '../../../../graphql/types'; export interface MetricExplorerViewState { chartOptions: MetricsExplorerChartOptions; diff --git a/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx similarity index 96% rename from x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx index bbc8778545b4a0..94edab54fb71e9 100644 --- a/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.test.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { useMetricsExplorerData } from './use_metrics_explorer_data'; import { renderHook } from '@testing-library/react-hooks'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; import { options, @@ -17,7 +17,7 @@ import { timeRange, resp, createSeries, -} from '../../utils/fixtures/metrics_explorer'; +} from '../../../../utils/fixtures/metrics_explorer'; const mockedFetch = jest.fn(); @@ -54,7 +54,7 @@ const renderUseMetricsExplorerDataHook = () => { ); }; -jest.mock('../../utils/kuery', () => { +jest.mock('../../../../utils/kuery', () => { return { convertKueryToElasticSearchQuery: (query: string) => query, }; diff --git a/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts similarity index 91% rename from x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts index b32496fbf30a11..93aacb586a5cde 100644 --- a/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_data.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts @@ -8,15 +8,15 @@ import DateMath from '@elastic/datemath'; import { isEqual } from 'lodash'; import { useEffect, useState } from 'react'; import { IIndexPattern } from 'src/plugins/data/public'; -import { SourceQuery } from '../../../common/graphql/types'; +import { SourceQuery } from '../../../../../common/graphql/types'; import { MetricsExplorerResponse, metricsExplorerResponseRT, -} from '../../../common/http_api/metrics_explorer'; -import { convertKueryToElasticSearchQuery } from '../../utils/kuery'; +} from '../../../../../common/http_api/metrics_explorer'; +import { convertKueryToElasticSearchQuery } from '../../../../utils/kuery'; import { MetricsExplorerOptions, MetricsExplorerTimeOptions } from './use_metrics_explorer_options'; -import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; -import { decodeOrThrow } from '../../../common/runtime_types'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; function isSameOptions(current: MetricsExplorerOptions, next: MetricsExplorerOptions) { return isEqual(current, next); diff --git a/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.test.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.test.tsx similarity index 100% rename from x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.test.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.test.tsx diff --git a/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.ts similarity index 96% rename from x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.ts index 2b802af8e8c15f..9d124a6af80123 100644 --- a/x-pack/plugins/infra/public/containers/metrics_explorer/use_metrics_explorer_options.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.ts @@ -6,11 +6,11 @@ import createContainer from 'constate'; import { useState, useEffect, Dispatch, SetStateAction } from 'react'; -import { MetricsExplorerColor } from '../../../common/color_palette'; +import { MetricsExplorerColor } from '../../../../../common/color_palette'; import { MetricsExplorerAggregation, MetricsExplorerMetric, -} from '../../../common/http_api/metrics_explorer'; +} from '../../../../../common/http_api/metrics_explorer'; export type MetricsExplorerOptionsMetric = MetricsExplorerMetric & { color?: MetricsExplorerColor; diff --git a/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/index.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx similarity index 92% rename from x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/index.tsx rename to x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx index 0999cea59731ca..a213671e9436eb 100644 --- a/x-pack/plugins/infra/public/pages/infrastructure/metrics_explorer/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/index.tsx @@ -9,11 +9,11 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { IIndexPattern } from 'src/plugins/data/public'; import { DocumentTitle } from '../../../components/document_title'; -import { MetricsExplorerCharts } from '../../../components/metrics_explorer/charts'; -import { MetricsExplorerToolbar } from '../../../components/metrics_explorer/toolbar'; +import { MetricsExplorerCharts } from './components/charts'; +import { MetricsExplorerToolbar } from './components/toolbar'; import { SourceQuery } from '../../../../common/graphql/types'; import { NoData } from '../../../components/empty_states'; -import { useMetricsExplorerState } from './use_metric_explorer_state'; +import { useMetricsExplorerState } from './hooks/use_metric_explorer_state'; import { useTrackPageview } from '../../../../../observability/public'; interface MetricsExplorerPageProps { diff --git a/x-pack/plugins/infra/public/pages/infrastructure/settings.tsx b/x-pack/plugins/infra/public/pages/metrics/settings.tsx similarity index 100% rename from x-pack/plugins/infra/public/pages/infrastructure/settings.tsx rename to x-pack/plugins/infra/public/pages/metrics/settings.tsx diff --git a/x-pack/plugins/infra/public/routers/metrics_router.tsx b/x-pack/plugins/infra/public/routers/metrics_router.tsx index 7cb9de65e72917..0e427150a46cc6 100644 --- a/x-pack/plugins/infra/public/routers/metrics_router.tsx +++ b/x-pack/plugins/infra/public/routers/metrics_router.tsx @@ -8,12 +8,12 @@ import React from 'react'; import { Route, Router, Switch } from 'react-router-dom'; import { NotFoundPage } from '../pages/404'; -import { InfrastructurePage } from '../pages/infrastructure'; -import { LinkToMetricsPage } from '../pages/link_to'; -import { MetricDetail } from '../pages/metrics'; +import { InfrastructurePage } from '../pages/metrics'; +import { MetricDetail } from '../pages/metrics/metric_detail'; import { RedirectWithQueryParams } from '../utils/redirect_with_query_params'; import { useKibana } from '../../../../../src/plugins/kibana_react/public'; import { AppRouter } from './index'; +import { LinkToMetricsPage } from '../pages/link_to'; export const MetricsRouter: AppRouter = ({ history }) => { const uiCapabilities = useKibana().services.application?.capabilities; diff --git a/x-pack/plugins/infra/public/utils/fixtures/metrics_explorer.ts b/x-pack/plugins/infra/public/utils/fixtures/metrics_explorer.ts index e39d6f785d53e2..15159ad45c7f33 100644 --- a/x-pack/plugins/infra/public/utils/fixtures/metrics_explorer.ts +++ b/x-pack/plugins/infra/public/utils/fixtures/metrics_explorer.ts @@ -14,7 +14,7 @@ import { MetricsExplorerChartType, MetricsExplorerYAxisMode, MetricsExplorerChartOptions, -} from '../../containers/metrics_explorer/use_metrics_explorer_options'; +} from '../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; export const options: MetricsExplorerOptions = { limit: 3, diff --git a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts index 3496ea782ee997..17509571f19853 100644 --- a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts +++ b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts @@ -3,11 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { NewDatasource, DatasourceInput } from '../types'; +import { Datasource, NewDatasource, DatasourceInput } from '../types'; import { storedDatasourceToAgentDatasource } from './datasource_to_agent_datasource'; describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { - const mockDatasource: NewDatasource = { + const mockNewDatasource: NewDatasource = { name: 'mock-datasource', description: '', config_id: '', @@ -17,6 +17,12 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { inputs: [], }; + const mockDatasource: Datasource = { + ...mockNewDatasource, + id: 'some-uuid', + revision: 1, + }; + const mockInput: DatasourceInput = { type: 'test-logs', enabled: true, @@ -70,7 +76,8 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { it('returns agent datasource config for datasource with no inputs', () => { expect(storedDatasourceToAgentDatasource(mockDatasource)).toEqual({ - id: 'mock-datasource', + id: 'some-uuid', + name: 'mock-datasource', namespace: 'default', enabled: true, use_output: 'default', @@ -87,7 +94,8 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { }, }) ).toEqual({ - id: 'mock-datasource', + id: 'some-uuid', + name: 'mock-datasource', namespace: 'default', enabled: true, use_output: 'default', @@ -99,9 +107,21 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { }); }); + it('uses name for id when id is not provided in case of new datasource', () => { + expect(storedDatasourceToAgentDatasource(mockNewDatasource)).toEqual({ + id: 'mock-datasource', + name: 'mock-datasource', + namespace: 'default', + enabled: true, + use_output: 'default', + inputs: [], + }); + }); + it('returns agent datasource config with flattened input and package stream', () => { expect(storedDatasourceToAgentDatasource({ ...mockDatasource, inputs: [mockInput] })).toEqual({ - id: 'mock-datasource', + id: 'some-uuid', + name: 'mock-datasource', namespace: 'default', enabled: true, use_output: 'default', @@ -140,7 +160,8 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { ], }) ).toEqual({ - id: 'mock-datasource', + id: 'some-uuid', + name: 'mock-datasource', namespace: 'default', enabled: true, use_output: 'default', @@ -169,7 +190,8 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { inputs: [{ ...mockInput, enabled: false }], }) ).toEqual({ - id: 'mock-datasource', + id: 'some-uuid', + name: 'mock-datasource', namespace: 'default', enabled: true, use_output: 'default', diff --git a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts index b509878b7f9452..9e09d3fa3153a0 100644 --- a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts +++ b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts @@ -12,7 +12,8 @@ export const storedDatasourceToAgentDatasource = ( const { name, namespace, enabled, package: pkg, inputs } = datasource; const fullDatasource: FullAgentConfigDatasource = { - id: name, + id: 'id' in datasource ? datasource.id : name, + name, namespace, enabled, use_output: DEFAULT_OUTPUT.name, // TODO: hardcoded to default output for now diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts b/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts index 002c3784446a8e..2372caee512af6 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent_config.ts @@ -34,8 +34,10 @@ export interface AgentConfig extends NewAgentConfig, SavedObjectAttributes { revision: number; } -export type FullAgentConfigDatasource = Pick & { - id: string; +export type FullAgentConfigDatasource = Pick< + Datasource, + 'id' | 'name' | 'namespace' | 'enabled' +> & { package?: Pick; use_output: string; inputs: Array< diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_messaging.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_messaging.tsx new file mode 100644 index 00000000000000..0f3ddee29fa443 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_messaging.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import styled from 'styled-components'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiText } from '@elastic/eui'; + +const Message = styled(EuiText).attrs(props => ({ + color: 'subdued', + textAlign: 'center', +}))` + padding: ${props => props.theme.eui.paddingSizes.m}; +`; + +export const AlphaMessaging: React.FC<{}> = () => ( + +

+ + + + + {' – '} + + +

+
+); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx index 1aab6d901a9926..ceb87fb048ae3c 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/header.tsx @@ -31,7 +31,7 @@ const Tabs = styled(EuiTabs)` `; export interface HeaderProps { - restrictHeaderWidth?: number; + maxWidth?: number; leftColumn?: JSX.Element; rightColumn?: JSX.Element; rightColumnGrow?: EuiFlexItemProps['grow']; @@ -52,10 +52,10 @@ export const Header: React.FC = ({ rightColumn, rightColumnGrow, tabs, - restrictHeaderWidth, + maxWidth, }) => ( - + = ({ section, childre {children} + ); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx index c77a50d95dca3c..d5ce5e17ad84ec 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/with_header.tsx @@ -14,12 +14,18 @@ const Page = styled(EuiPage)` interface Props extends HeaderProps { restrictWidth?: number; + restrictHeaderWidth?: number; children?: React.ReactNode; } -export const WithHeaderLayout: React.FC = ({ restrictWidth, children, ...rest }) => ( +export const WithHeaderLayout: React.FC = ({ + restrictWidth, + restrictHeaderWidth, + children, + ...rest +}) => ( -
+
diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx index 73a7ba8ec119d9..39d882f7fdf65e 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/layout.tsx @@ -18,16 +18,14 @@ import { import { WithHeaderLayout } from '../../../../layouts'; import { AgentConfig, PackageInfo } from '../../../../types'; import { PackageIcon } from '../../../../components/package_icon'; -import { CreateDatasourceFrom, CreateDatasourceStep } from '../types'; +import { CreateDatasourceFrom } from '../types'; export const CreateDatasourcePageLayout: React.FunctionComponent<{ from: CreateDatasourceFrom; - basePath: string; cancelUrl: string; - maxStep: CreateDatasourceStep | ''; agentConfig?: AgentConfig; packageInfo?: PackageInfo; -}> = ({ from, basePath, cancelUrl, maxStep, agentConfig, packageInfo, children }) => { +}> = ({ from, cancelUrl, agentConfig, packageInfo, children }) => { const leftColumn = ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx index 1ad579d591b211..3c79fe17fdbb51 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx @@ -28,12 +28,11 @@ import { } from '../../../hooks'; import { useLinks as useEPMLinks } from '../../epm/hooks'; import { CreateDatasourcePageLayout, ConfirmCreateDatasourceModal } from './components'; -import { CreateDatasourceFrom, CreateDatasourceStep } from './types'; +import { CreateDatasourceFrom } from './types'; import { DatasourceValidationResults, validateDatasource, validationHasErrors } from './services'; import { StepSelectPackage } from './step_select_package'; import { StepSelectConfig } from './step_select_config'; import { StepConfigureDatasource } from './step_configure_datasource'; - import { StepDefineDatasource } from './step_define_datasource'; export const CreateDatasourcePage: React.FunctionComponent = () => { @@ -43,11 +42,9 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { } = useConfig(); const { params: { configId, pkgkey }, - url: basePath, } = useRouteMatch(); const history = useHistory(); const from: CreateDatasourceFrom = configId ? 'config' : 'package'; - const [maxStep, setMaxStep] = useState(''); // Agent config and package info states const [agentConfig, setAgentConfig] = useState(); @@ -91,7 +88,6 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { } else { setFormState('INVALID'); setPackageInfo(undefined); - setMaxStep(''); } // eslint-disable-next-line no-console @@ -105,7 +101,6 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { } else { setFormState('INVALID'); setAgentConfig(undefined); - setMaxStep(''); } // eslint-disable-next-line no-console @@ -189,9 +184,7 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { const layoutProps = { from, - basePath, cancelUrl, - maxStep, agentConfig, packageInfo, }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/types.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/types.ts index bd05be2d8a558d..85cc758fc4c464 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/types.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/types.ts @@ -5,4 +5,3 @@ */ export type CreateDatasourceFrom = 'package' | 'config'; -export type CreateDatasourceStep = 'selectConfig' | 'selectPackage' | 'configure' | 'review'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx index 56b109a9bc062f..ad27c590d5eaae 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx @@ -27,7 +27,9 @@ import { Loading } from '../../../../../components'; const CONFIG_KEYS_ORDER = [ 'id', + 'name', 'revision', + 'type', 'outputs', 'datasources', 'enabled', @@ -52,7 +54,7 @@ export const ConfigYamlView = memo<{ config: AgentConfig }>(({ config }) => { return ( - + {dump(fullConfigRequest.data.item, { sortKeys: (keyA: string, keyB: string) => { const indexA = CONFIG_KEYS_ORDER.indexOf(keyA); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx index ea6b045f504ec1..05d150fd9ae231 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx @@ -3,12 +3,71 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { EuiText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { useState } from 'react'; +import styled from 'styled-components'; +import { + EuiButton, + EuiButtonEmpty, + EuiPanel, + EuiText, + EuiTitle, + EuiDescriptionList, + EuiDescriptionListDescription, + EuiDescriptionListTitle, + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { WithHeaderLayout } from '../../layouts'; +import { useLink, useGetAgentConfigs } from '../../hooks'; +import { AgentEnrollmentFlyout } from '../fleet/agent_list_page/components'; +import { EPM_PATH, FLEET_PATH, AGENT_CONFIG_PATH } from '../../constants'; + +const OverviewPanel = styled(EuiPanel).attrs(props => ({ + paddingSize: 'm', +}))` + header { + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid ${props => props.theme.eui.euiColorLightShade}; + margin: -${props => props.theme.eui.paddingSizes.m} -${props => props.theme.eui.paddingSizes.m} + ${props => props.theme.eui.paddingSizes.m}; + padding: ${props => props.theme.eui.paddingSizes.s} ${props => props.theme.eui.paddingSizes.m}; + } + + h2 { + padding: ${props => props.theme.eui.paddingSizes.xs} 0; + } +`; + +const OverviewStats = styled(EuiDescriptionList).attrs(props => ({ + compressed: true, + textStyle: 'reverse', + type: 'column', +}))` + & > * { + margin-top: ${props => props.theme.eui.paddingSizes.s} !important; + + &:first-child, + &:nth-child(2) { + margin-top: 0 !important; + } + } +`; export const IngestManagerOverview: React.FunctionComponent = () => { + // Agent enrollment flyout state + const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); + + // Agent configs required for enrollment flyout + const agentConfigsRequest = useGetAgentConfigs({ + page: 1, + perPage: 1000, + }); + const agentConfigs = agentConfigsRequest.data ? agentConfigsRequest.data.items : []; + return ( {

} - /> + rightColumn={ + + + setIsEnrollmentFlyoutOpen(true)}> + + + + + } + > + {isEnrollmentFlyoutOpen && ( + setIsEnrollmentFlyoutOpen(false)} + /> + )} + + + + +
+ +

+ +

+
+ + + +
+ + Total available + 999 + Installed + 1 + Updated available + 0 + +
+
+ + + +
+ +

+ +

+
+ + + +
+ + Total configs + 1 + Data sources + 1 + +
+
+ + + +
+ +

+ +

+
+ + + +
+ + Total agents + 0 + Active + 0 + Offline + 0 + Error + 0 + +
+
+ + + +
+ +

+ +

+
+ + + +
+ + Data streams + 0 + Name spaces + 0 + Total size + 0 MB + +
+
+
+ ); }; diff --git a/x-pack/plugins/logstash/kibana.json b/x-pack/plugins/logstash/kibana.json index bcc926535d3c28..97dbf58865a882 100644 --- a/x-pack/plugins/logstash/kibana.json +++ b/x-pack/plugins/logstash/kibana.json @@ -4,9 +4,13 @@ "kibanaVersion": "kibana", "configPath": ["xpack", "logstash"], "requiredPlugins": [ - "licensing" + "licensing", + "management" + ], + "optionalPlugins": [ + "home", + "security" ], - "optionalPlugins": ["security"], "server": true, - "ui": false + "ui": true } diff --git a/x-pack/legacy/plugins/logstash/public/sections/breadcrumbs.js b/x-pack/plugins/logstash/public/application/breadcrumbs.js similarity index 80% rename from x-pack/legacy/plugins/logstash/public/sections/breadcrumbs.js rename to x-pack/plugins/logstash/public/application/breadcrumbs.js index 3121a58ff6a74f..322b9860b37854 100644 --- a/x-pack/legacy/plugins/logstash/public/sections/breadcrumbs.js +++ b/x-pack/plugins/logstash/public/application/breadcrumbs.js @@ -5,11 +5,9 @@ */ import { i18n } from '@kbn/i18n'; -import { MANAGEMENT_BREADCRUMB } from 'ui/management'; export function getPipelineListBreadcrumbs() { return [ - MANAGEMENT_BREADCRUMB, { text: i18n.translate('xpack.logstash.pipelines.listBreadcrumb', { defaultMessage: 'Pipelines', @@ -19,12 +17,11 @@ export function getPipelineListBreadcrumbs() { ]; } -export function getPipelineEditBreadcrumbs($route) { - const { pipeline } = $route.current.locals; +export function getPipelineEditBreadcrumbs(pipelineId) { return [ ...getPipelineListBreadcrumbs(), { - text: pipeline.id, + text: pipelineId, }, ]; } diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/confirm_delete_pipeline_modal.test.js.snap b/x-pack/plugins/logstash/public/application/components/pipeline_editor/__snapshots__/confirm_delete_pipeline_modal.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/confirm_delete_pipeline_modal.test.js.snap rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/__snapshots__/confirm_delete_pipeline_modal.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/flex_item_setting.test.js.snap b/x-pack/plugins/logstash/public/application/components/pipeline_editor/__snapshots__/flex_item_setting.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/flex_item_setting.test.js.snap rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/__snapshots__/flex_item_setting.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/form_label_with_icon_tip.test.js.snap b/x-pack/plugins/logstash/public/application/components/pipeline_editor/__snapshots__/form_label_with_icon_tip.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/form_label_with_icon_tip.test.js.snap rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/__snapshots__/form_label_with_icon_tip.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/pipeline_editor.test.js.snap b/x-pack/plugins/logstash/public/application/components/pipeline_editor/__snapshots__/pipeline_editor.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/__snapshots__/pipeline_editor.test.js.snap rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/__snapshots__/pipeline_editor.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/confirm_delete_pipeline_modal.js b/x-pack/plugins/logstash/public/application/components/pipeline_editor/confirm_delete_pipeline_modal.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/confirm_delete_pipeline_modal.js rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/confirm_delete_pipeline_modal.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/confirm_delete_pipeline_modal.test.js b/x-pack/plugins/logstash/public/application/components/pipeline_editor/confirm_delete_pipeline_modal.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/confirm_delete_pipeline_modal.test.js rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/confirm_delete_pipeline_modal.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/constants.js b/x-pack/plugins/logstash/public/application/components/pipeline_editor/constants.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/constants.js rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/constants.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/flex_item_setting.js b/x-pack/plugins/logstash/public/application/components/pipeline_editor/flex_item_setting.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/flex_item_setting.js rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/flex_item_setting.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/flex_item_setting.test.js b/x-pack/plugins/logstash/public/application/components/pipeline_editor/flex_item_setting.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/flex_item_setting.test.js rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/flex_item_setting.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/form_label_with_icon_tip.js b/x-pack/plugins/logstash/public/application/components/pipeline_editor/form_label_with_icon_tip.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/form_label_with_icon_tip.js rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/form_label_with_icon_tip.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/form_label_with_icon_tip.test.js b/x-pack/plugins/logstash/public/application/components/pipeline_editor/form_label_with_icon_tip.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/form_label_with_icon_tip.test.js rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/form_label_with_icon_tip.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/index.js b/x-pack/plugins/logstash/public/application/components/pipeline_editor/index.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/index.js rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/index.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/pipeline_editor.js b/x-pack/plugins/logstash/public/application/components/pipeline_editor/pipeline_editor.js similarity index 97% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/pipeline_editor.js rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/pipeline_editor.js index 5e430ccbd8cebc..e45820d56cc03d 100644 --- a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/pipeline_editor.js +++ b/x-pack/plugins/logstash/public/application/components/pipeline_editor/pipeline_editor.js @@ -13,7 +13,7 @@ import 'brace/mode/plain_text'; import 'brace/theme/github'; import { isEmpty } from 'lodash'; -import { TOOLTIPS } from '../../../../../../plugins/logstash/common/constants/tooltips'; +import { TOOLTIPS } from '../../../../common/constants/tooltips'; import { EuiButton, EuiButtonEmpty, @@ -40,7 +40,6 @@ class PipelineEditorUi extends React.Component { const { pipeline: { id, description, pipeline, settings }, - username, } = this.props; const pipelineWorkersSet = typeof settings['pipeline.workers'] === 'number'; @@ -60,7 +59,6 @@ class PipelineEditorUi extends React.Component { 'queue.max_bytes': settings['queue.max_bytes.number'] + settings['queue.max_bytes.units'], 'queue.type': settings['queue.type'], }, - username, }, pipelineIdErrors: [], pipelineIdPattern: /^[A-Za-z\_][A-Za-z0-9\-\_]*$/, @@ -236,15 +234,7 @@ class PipelineEditorUi extends React.Component { }; getPipelineHeadingText = () => { - const { - routeService: { - current: { - params: { clone, id }, - }, - }, - isNewPipeline, - intl, - } = this.props; + const { clone, id, isNewPipeline, intl } = this.props; if (!!clone && id) { return intl.formatMessage( @@ -502,6 +492,8 @@ class PipelineEditorUi extends React.Component { } PipelineEditorUi.propTypes = { + id: PropTypes.string, + clone: PropTypes.bool.isRequired, close: PropTypes.func.isRequired, isNewPipeline: PropTypes.bool.isRequired, licenseService: PropTypes.shape({ @@ -527,20 +519,11 @@ PipelineEditorUi.propTypes = { deletePipeline: PropTypes.func.isRequired, savePipeline: PropTypes.func.isRequired, }).isRequired, - routeService: PropTypes.shape({ - current: PropTypes.shape({ - params: PropTypes.shape({ - clone: PropTypes.oneOf([true, undefined]), - id: PropTypes.string, - }), - }), - }).isRequired, toastNotifications: PropTypes.shape({ addWarning: PropTypes.func.isRequired, addSuccess: PropTypes.func.isRequired, addError: PropTypes.func.isRequired, }).isRequired, - username: PropTypes.string, }; export const PipelineEditor = injectI18n(PipelineEditorUi); diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/pipeline_editor.test.js b/x-pack/plugins/logstash/public/application/components/pipeline_editor/pipeline_editor.test.js similarity index 96% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_editor/pipeline_editor.test.js rename to x-pack/plugins/logstash/public/application/components/pipeline_editor/pipeline_editor.test.js index 2d7ed5f257fbda..bb5961ce36120c 100644 --- a/x-pack/legacy/plugins/logstash/public/components/pipeline_editor/pipeline_editor.test.js +++ b/x-pack/plugins/logstash/public/application/components/pipeline_editor/pipeline_editor.test.js @@ -17,7 +17,6 @@ describe('PipelineEditor component', () => { let open; let pipeline; let pipelineService; - let routeService; let toastNotifications; let username; @@ -47,14 +46,6 @@ describe('PipelineEditor component', () => { deletePipeline: jest.fn(), savePipeline: jest.fn(), }; - routeService = { - current: { - params: { - clone: undefined, - id: undefined, - }, - }, - }; toastNotifications = { addWarning: jest.fn(), addSuccess: jest.fn(), @@ -62,13 +53,14 @@ describe('PipelineEditor component', () => { }; username = 'elastic'; props = { + clone: false, + id: 'pipelineId', close, isNewPipeline, licenseService, open, pipeline, pipelineService, - routeService, toastNotifications, username, }; @@ -79,10 +71,8 @@ describe('PipelineEditor component', () => { }); it('matches snapshot for clone pipeline', () => { - routeService.current.params = { - clone: true, - id: 'pipelineToClone', - }; + props.clone = true; + props.id = 'pipelineToClone'; expect(shallowWithIntl()).toMatchSnapshot(); }); diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/add_role_alert.test.js.snap b/x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/add_role_alert.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/add_role_alert.test.js.snap rename to x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/add_role_alert.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/alert_call_out.test.js.snap b/x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/alert_call_out.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/alert_call_out.test.js.snap rename to x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/alert_call_out.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/confirm_delete_modal.test.js.snap b/x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/confirm_delete_modal.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/confirm_delete_modal.test.js.snap rename to x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/confirm_delete_modal.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/enable_monitoring_alert.test.js.snap b/x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/enable_monitoring_alert.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/enable_monitoring_alert.test.js.snap rename to x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/enable_monitoring_alert.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/info_alerts.test.js.snap b/x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/info_alerts.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/info_alerts.test.js.snap rename to x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/info_alerts.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/pipelines_table.test.js.snap b/x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/pipelines_table.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/__snapshots__/pipelines_table.test.js.snap rename to x-pack/plugins/logstash/public/application/components/pipeline_list/__snapshots__/pipelines_table.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/add_role_alert.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/add_role_alert.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/add_role_alert.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/add_role_alert.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/add_role_alert.test.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/add_role_alert.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/add_role_alert.test.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/add_role_alert.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/alert_call_out.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/alert_call_out.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/alert_call_out.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/alert_call_out.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/alert_call_out.test.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/alert_call_out.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/alert_call_out.test.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/alert_call_out.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/confirm_delete_modal.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/confirm_delete_modal.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/confirm_delete_modal.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/confirm_delete_modal.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/confirm_delete_modal.test.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/confirm_delete_modal.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/confirm_delete_modal.test.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/confirm_delete_modal.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/constants.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/constants.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/constants.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/constants.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/enable_monitoring_alert.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/enable_monitoring_alert.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/enable_monitoring_alert.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/enable_monitoring_alert.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/enable_monitoring_alert.test.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/enable_monitoring_alert.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/enable_monitoring_alert.test.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/enable_monitoring_alert.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/index.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/index.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/index.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/index.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/info_alerts.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/info_alerts.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/info_alerts.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/info_alerts.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/info_alerts.test.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/info_alerts.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/info_alerts.test.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/info_alerts.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/pipeline_list.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/pipeline_list.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/pipeline_list.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/pipeline_list.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/pipeline_list.test.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/pipeline_list.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/pipeline_list.test.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/pipeline_list.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/pipelines_table.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/pipelines_table.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/pipelines_table.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/pipelines_table.js diff --git a/x-pack/legacy/plugins/logstash/public/components/pipeline_list/pipelines_table.test.js b/x-pack/plugins/logstash/public/application/components/pipeline_list/pipelines_table.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/pipeline_list/pipelines_table.test.js rename to x-pack/plugins/logstash/public/application/components/pipeline_list/pipelines_table.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/upgrade_failure/__snapshots__/upgrade_failure.test.js.snap b/x-pack/plugins/logstash/public/application/components/upgrade_failure/__snapshots__/upgrade_failure.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/upgrade_failure/__snapshots__/upgrade_failure.test.js.snap rename to x-pack/plugins/logstash/public/application/components/upgrade_failure/__snapshots__/upgrade_failure.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/upgrade_failure/__snapshots__/upgrade_failure_actions.test.js.snap b/x-pack/plugins/logstash/public/application/components/upgrade_failure/__snapshots__/upgrade_failure_actions.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/upgrade_failure/__snapshots__/upgrade_failure_actions.test.js.snap rename to x-pack/plugins/logstash/public/application/components/upgrade_failure/__snapshots__/upgrade_failure_actions.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/upgrade_failure/__snapshots__/upgrade_failure_title.test.js.snap b/x-pack/plugins/logstash/public/application/components/upgrade_failure/__snapshots__/upgrade_failure_title.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/upgrade_failure/__snapshots__/upgrade_failure_title.test.js.snap rename to x-pack/plugins/logstash/public/application/components/upgrade_failure/__snapshots__/upgrade_failure_title.test.js.snap diff --git a/x-pack/legacy/plugins/logstash/public/components/upgrade_failure/constants.js b/x-pack/plugins/logstash/public/application/components/upgrade_failure/constants.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/upgrade_failure/constants.js rename to x-pack/plugins/logstash/public/application/components/upgrade_failure/constants.js diff --git a/x-pack/legacy/plugins/logstash/public/components/upgrade_failure/index.js b/x-pack/plugins/logstash/public/application/components/upgrade_failure/index.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/upgrade_failure/index.js rename to x-pack/plugins/logstash/public/application/components/upgrade_failure/index.js diff --git a/x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure.js b/x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure.js rename to x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure.js diff --git a/x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure.test.js b/x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure.test.js rename to x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure_actions.js b/x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure_actions.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure_actions.js rename to x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure_actions.js diff --git a/x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure_actions.test.js b/x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure_actions.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure_actions.test.js rename to x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure_actions.test.js diff --git a/x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure_title.js b/x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure_title.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure_title.js rename to x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure_title.js diff --git a/x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure_title.test.js b/x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure_title.test.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/components/upgrade_failure/upgrade_failure_title.test.js rename to x-pack/plugins/logstash/public/application/components/upgrade_failure/upgrade_failure_title.test.js diff --git a/x-pack/plugins/logstash/public/application/index.tsx b/x-pack/plugins/logstash/public/application/index.tsx new file mode 100644 index 00000000000000..438038d6c885e0 --- /dev/null +++ b/x-pack/plugins/logstash/public/application/index.tsx @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { HashRouter, Route, Switch, Redirect } from 'react-router-dom'; +import { Observable } from 'rxjs'; +import { first } from 'rxjs/operators'; + +import { CoreStart } from 'src/core/public'; +import { ManagementAppMountParams } from '../../../../../src/plugins/management/public'; +import { + ClusterService, + MonitoringService, + PipelineService, + PipelinesService, + UpgradeService, + // @ts-ignore +} from '../services'; +// @ts-ignore +import { PipelineList } from './components/pipeline_list'; +import { PipelineEditView } from './pipeline_edit_view'; +// @ts-ignore +import { Pipeline } from '../models/pipeline'; +// @ts-ignore +import * as Breadcrumbs from './breadcrumbs'; + +export const renderApp = async ( + core: CoreStart, + { basePath, element, setBreadcrumbs }: ManagementAppMountParams, + licenseService$: Observable +) => { + const logstashLicenseService = await licenseService$.pipe(first()).toPromise(); + const clusterService = new ClusterService(core.http); + const monitoringService = new MonitoringService( + core.http, + // When monitoring is migrated this should be fetched from monitoring's plugin contract + core.injectedMetadata.getInjectedVar('monitoringUiEnabled'), + clusterService + ); + const pipelinesService = new PipelinesService(core.http, monitoringService); + const pipelineService = new PipelineService(core.http, pipelinesService); + const upgradeService = new UpgradeService(core.http); + + ReactDOM.render( + + + + { + setBreadcrumbs(Breadcrumbs.getPipelineListBreadcrumbs()); + return ( + history.push(`/pipeline/${id}/edit`)} + clonePipeline={(id: string) => history.push(`/pipeline/${id}/edit?clone`)} + createPipeline={() => history.push(`/pipeline/new-pipeline`)} + pipelinesService={pipelinesService} + toastNotifications={core.notifications.toasts} + /> + ); + }} + /> + ( + + )} + /> + } + /> + ( + + )} + /> + + + , + element + ); + + return () => { + ReactDOM.unmountComponentAtNode(element); + }; +}; diff --git a/x-pack/plugins/logstash/public/application/pipeline_edit_view.tsx b/x-pack/plugins/logstash/public/application/pipeline_edit_view.tsx new file mode 100644 index 00000000000000..c1b465febcd9ba --- /dev/null +++ b/x-pack/plugins/logstash/public/application/pipeline_edit_view.tsx @@ -0,0 +1,153 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState, useLayoutEffect, useCallback } from 'react'; +import { usePromise } from 'react-use'; +import { History } from 'history'; + +import { i18n } from '@kbn/i18n'; +import { ToastsStart } from 'src/core/public'; + +// @ts-ignore +import { UpgradeFailure } from './components/upgrade_failure'; +// @ts-ignore +import { PipelineEditor } from './components/pipeline_editor'; +// @ts-ignore +import { Pipeline } from '../models/pipeline'; +import { ManagementAppMountParams } from '../../../../../src/plugins/management/public'; +// @ts-ignore +import * as Breadcrumbs from './breadcrumbs'; + +const usePipeline = ( + pipelineService: any, + logstashLicenseService: any, + toasts: ToastsStart, + shouldClone: boolean, + id?: string +) => { + const mounted = usePromise(); + const [pipeline, setPipeline] = useState(null); + + useLayoutEffect(() => { + (async () => { + if (!id) { + return setPipeline(new Pipeline()); + } + + try { + const result = await mounted(pipelineService.loadPipeline(id) as Promise); + setPipeline(shouldClone ? result.clone : result); + } catch (e) { + await logstashLicenseService.checkValidity(); + if (e.status !== 403) { + toasts.addDanger( + i18n.translate('xpack.logstash.couldNotLoadPipelineErrorNotification', { + defaultMessage: `Couldn't load pipeline. Error: '{errStatusText}'.`, + values: { + errStatusText: e.statusText, + }, + }) + ); + } + } + })(); + }, [pipelineService, id, mounted, shouldClone, logstashLicenseService, toasts]); + + return pipeline; +}; + +const useIsUpgraded = (upgradeService: any) => { + const [isUpgraded, setIsUpgraded] = useState(null); + const mounted = usePromise(); + + useLayoutEffect(() => { + mounted(upgradeService.executeUpgrade() as Promise).then(result => + setIsUpgraded(result) + ); + }, [mounted, upgradeService]); + + return isUpgraded; +}; + +interface EditProps { + pipelineService: any; + logstashLicenseService: any; + upgradeService: any; + toasts: ToastsStart; + history: History; + setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs']; + + // URL params + id?: string; +} + +export const PipelineEditView: React.FC = ({ + pipelineService, + logstashLicenseService, + upgradeService, + toasts, + history, + setBreadcrumbs, + id, +}) => { + const params = new URLSearchParams(history.location.search); + const shouldRetry = params.get('retry') === 'true'; + const shouldClone = params.get('clone') === ''; + + const pipeline = usePipeline(pipelineService, logstashLicenseService, toasts, shouldClone, id); + const isUpgraded = useIsUpgraded(upgradeService); + + const onRetry = useCallback(() => { + const newParams = new URLSearchParams(history.location.search); + newParams.set('retry', 'true'); + history.replace({ search: newParams.toString() }); + }, [history]); + const close = useCallback(() => { + history.push('/'); + }, [history]); + const open = useCallback( + (newId: string) => { + history.push(`/pipeline/${newId}/edit`); + }, + [history] + ); + + if (!pipeline || isUpgraded === null) { + return null; + } + + const isNewPipeline = !pipeline.id; + setBreadcrumbs( + isNewPipeline + ? Breadcrumbs.getPipelineCreateBreadcrumbs() + : Breadcrumbs.getPipelineEditBreadcrumbs(pipeline.id) + ); + + if (!isUpgraded) { + return ( + + ); + } + + return ( + + ); +}; diff --git a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/index.ts b/x-pack/plugins/logstash/public/index.ts similarity index 72% rename from x-pack/plugins/snapshot_restore/public/application/lib/authorization/index.ts rename to x-pack/plugins/logstash/public/index.ts index 73bbde465146c0..26a1ca4e8c6c43 100644 --- a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/index.ts +++ b/x-pack/plugins/logstash/public/index.ts @@ -4,4 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './components'; +import { LogstashPlugin } from './plugin'; + +export const plugin = () => new LogstashPlugin(); diff --git a/x-pack/legacy/plugins/logstash/public/lib/get_search_value/get_search_value.js b/x-pack/plugins/logstash/public/lib/get_search_value/get_search_value.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/lib/get_search_value/get_search_value.js rename to x-pack/plugins/logstash/public/lib/get_search_value/get_search_value.js diff --git a/x-pack/legacy/plugins/logstash/public/lib/get_search_value/index.js b/x-pack/plugins/logstash/public/lib/get_search_value/index.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/lib/get_search_value/index.js rename to x-pack/plugins/logstash/public/lib/get_search_value/index.js diff --git a/x-pack/legacy/plugins/logstash/public/models/cluster/cluster.js b/x-pack/plugins/logstash/public/models/cluster/cluster.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/models/cluster/cluster.js rename to x-pack/plugins/logstash/public/models/cluster/cluster.js diff --git a/x-pack/legacy/plugins/logstash/public/models/cluster/index.js b/x-pack/plugins/logstash/public/models/cluster/index.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/models/cluster/index.js rename to x-pack/plugins/logstash/public/models/cluster/index.js diff --git a/x-pack/legacy/plugins/logstash/public/models/pipeline/index.js b/x-pack/plugins/logstash/public/models/pipeline/index.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/models/pipeline/index.js rename to x-pack/plugins/logstash/public/models/pipeline/index.js diff --git a/x-pack/legacy/plugins/logstash/public/models/pipeline/pipeline.js b/x-pack/plugins/logstash/public/models/pipeline/pipeline.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/models/pipeline/pipeline.js rename to x-pack/plugins/logstash/public/models/pipeline/pipeline.js diff --git a/x-pack/legacy/plugins/logstash/public/models/pipeline_list_item/index.js b/x-pack/plugins/logstash/public/models/pipeline_list_item/index.js similarity index 100% rename from x-pack/legacy/plugins/logstash/public/models/pipeline_list_item/index.js rename to x-pack/plugins/logstash/public/models/pipeline_list_item/index.js diff --git a/x-pack/legacy/plugins/logstash/public/models/pipeline_list_item/pipeline_list_item.js b/x-pack/plugins/logstash/public/models/pipeline_list_item/pipeline_list_item.js similarity index 83% rename from x-pack/legacy/plugins/logstash/public/models/pipeline_list_item/pipeline_list_item.js rename to x-pack/plugins/logstash/public/models/pipeline_list_item/pipeline_list_item.js index 06d01a05bac278..3a304e467e0c0d 100755 --- a/x-pack/legacy/plugins/logstash/public/models/pipeline_list_item/pipeline_list_item.js +++ b/x-pack/plugins/logstash/public/models/pipeline_list_item/pipeline_list_item.js @@ -5,10 +5,10 @@ */ import { pick, capitalize } from 'lodash'; +import moment from 'moment'; -import { getSearchValue } from 'plugins/logstash/lib/get_search_value'; -import { getMoment } from 'plugins/logstash/../common/lib/get_moment'; -import { PIPELINE } from '../../../../../../plugins/logstash/common/constants'; +import { getSearchValue } from '../../lib/get_search_value'; +import { PIPELINE } from '../../../common/constants'; /** * Represents the model for listing pipelines in the UI @@ -25,7 +25,7 @@ export class PipelineListItem { this.username = props.username; if (props.lastModified) { - this.lastModified = getMoment(props.lastModified); + this.lastModified = getMomentDate(props.lastModified); this.lastModifiedHumanized = capitalize(this.lastModified.fromNow()); } } @@ -51,3 +51,11 @@ export class PipelineListItem { return new PipelineListItem(props); } } + +function getMomentDate(date) { + if (!date) { + return null; + } + + return moment(date); +} diff --git a/x-pack/plugins/logstash/public/plugin.ts b/x-pack/plugins/logstash/public/plugin.ts new file mode 100644 index 00000000000000..91d1a39d3970cf --- /dev/null +++ b/x-pack/plugins/logstash/public/plugin.ts @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { Subscription } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { once } from 'lodash'; + +import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import { + HomePublicPluginSetup, + FeatureCatalogueCategory, +} from '../../../../src/plugins/home/public'; +import { LicensingPluginSetup } from '../../licensing/public'; +import { ManagementSetup } from '../../../../src/plugins/management/public'; + +// @ts-ignore +import { LogstashLicenseService } from './services'; + +interface SetupDeps { + licensing: LicensingPluginSetup; + management: ManagementSetup; + + home?: HomePublicPluginSetup; +} + +export class LogstashPlugin implements Plugin { + private licenseSubscription?: Subscription; + + public setup(core: CoreSetup, plugins: SetupDeps) { + const logstashLicense$ = plugins.licensing.license$.pipe( + map(license => new LogstashLicenseService(license)) + ); + const section = plugins.management.sections.register({ + id: 'logstash', + title: 'Logstash', + order: 30, + euiIconType: 'logoLogstash', + }); + const managementApp = section.registerApp({ + id: 'pipelines', + title: i18n.translate('xpack.logstash.managementSection.pipelinesTitle', { + defaultMessage: 'Pipelines', + }), + order: 10, + mount: async params => { + const [coreStart] = await core.getStartServices(); + const { renderApp } = await import('./application'); + + return renderApp(coreStart, params, logstashLicense$); + }, + }); + + this.licenseSubscription = logstashLicense$.subscribe((license: any) => { + if (license.enableLinks) { + managementApp.enable(); + } else { + managementApp.disable(); + } + + if (plugins.home && license.enableLinks) { + // Ensure that we don't register the feature more than once + once(() => { + plugins.home!.featureCatalogue.register({ + id: 'management_logstash', + title: i18n.translate('xpack.logstash.homeFeature.logstashPipelinesTitle', { + defaultMessage: 'Logstash Pipelines', + }), + description: i18n.translate('xpack.logstash.homeFeature.logstashPipelinesDescription', { + defaultMessage: 'Create, delete, update, and clone data ingestion pipelines.', + }), + icon: 'pipelineApp', + path: '/app/kibana#/management/logstash/pipelines', + showOnHomePage: true, + category: FeatureCatalogueCategory.ADMIN, + }); + }); + } + }); + } + + public start(core: CoreStart) {} + + public stop() { + if (this.licenseSubscription) { + this.licenseSubscription.unsubscribe(); + } + } +} diff --git a/x-pack/legacy/plugins/logstash/public/services/cluster/cluster_service.js b/x-pack/plugins/logstash/public/services/cluster/cluster_service.js similarity index 51% rename from x-pack/legacy/plugins/logstash/public/services/cluster/cluster_service.js rename to x-pack/plugins/logstash/public/services/cluster/cluster_service.js index e89c2fe7d11bf9..20f3b0d349c803 100755 --- a/x-pack/legacy/plugins/logstash/public/services/cluster/cluster_service.js +++ b/x-pack/plugins/logstash/public/services/cluster/cluster_service.js @@ -4,22 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome from 'ui/chrome'; -import { ROUTES } from '../../../../../../plugins/logstash/common/constants'; -import { Cluster } from 'plugins/logstash/models/cluster'; +import { ROUTES } from '../../../common/constants'; +import { Cluster } from '../../models/cluster'; export class ClusterService { - constructor($http) { - this.$http = $http; - this.basePath = chrome.addBasePath(ROUTES.API_ROOT); + constructor(http) { + this.http = http; } loadCluster() { - return this.$http.get(`${this.basePath}/cluster`).then(response => { - if (!response.data) { + return this.http.get(`${ROUTES.API_ROOT}/cluster`).then(response => { + if (!response) { return; } - return Cluster.fromUpstreamJSON(response.data.cluster); + return Cluster.fromUpstreamJSON(response.cluster); }); } diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/pipeline_edit/index.js b/x-pack/plugins/logstash/public/services/cluster/index.js similarity index 82% rename from x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/pipeline_edit/index.js rename to x-pack/plugins/logstash/public/services/cluster/index.js index 5889bbdf96a93c..4417262d9f442e 100755 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/pipeline_edit/index.js +++ b/x-pack/plugins/logstash/public/services/cluster/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import './pipeline_edit'; +export { ClusterService } from './cluster_service'; diff --git a/x-pack/plugins/logstash/public/services/index.js b/x-pack/plugins/logstash/public/services/index.js new file mode 100644 index 00000000000000..a7e8aa5c6259f5 --- /dev/null +++ b/x-pack/plugins/logstash/public/services/index.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ClusterService } from './cluster'; +export { LogstashLicenseService } from './license'; +export { MonitoringService } from './monitoring'; +export { PipelineService } from './pipeline'; +export { PipelinesService } from './pipelines'; +export { UpgradeService } from './upgrade'; diff --git a/x-pack/legacy/plugins/logstash/public/lib/update_management_sections/index.js b/x-pack/plugins/logstash/public/services/license/index.js similarity index 77% rename from x-pack/legacy/plugins/logstash/public/lib/update_management_sections/index.js rename to x-pack/plugins/logstash/public/services/license/index.js index 9d53d4dd61163c..64f39b1144cee3 100755 --- a/x-pack/legacy/plugins/logstash/public/lib/update_management_sections/index.js +++ b/x-pack/plugins/logstash/public/services/license/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { updateLogstashSections } from './update_logstash_sections'; +export { LogstashLicenseService } from './logstash_license_service'; diff --git a/x-pack/plugins/logstash/public/services/license/logstash_license_service.js b/x-pack/plugins/logstash/public/services/license/logstash_license_service.js new file mode 100755 index 00000000000000..b836b75b89cc70 --- /dev/null +++ b/x-pack/plugins/logstash/public/services/license/logstash_license_service.js @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export class LogstashLicenseService { + constructor(license, navigateToApp, toasts) { + this.license = license; + this.navigateToApp = navigateToApp; + this.toasts = toasts; + } + + get enableLinks() { + return this.calculated.enableLinks; + } + + get isAvailable() { + return this.calculated.isAvailable; + } + + get isReadOnly() { + return this.calculated.isReadOnly; + } + + get message() { + return this.calculated.message; + } + + get isSecurityEnabled() { + return this.license.getFeature(`security`).isEnabled; + } + + /** + * Checks if the license is valid or the license can perform downgraded UI tasks. + * Rejects if the plugin is not available due to license. + */ + checkValidity() { + return new Promise((resolve, reject) => { + if (this.isAvailable) { + return resolve(); + } + + return reject(); + }); + } + + get calculated() { + if (!this.license) { + throw new Error(`No license available!`); + } + + if (!this.isSecurityEnabled) { + return { + isAvailable: false, + enableLinks: false, + isReadOnly: false, + message: i18n.translate('xpack.logstash.managementSection.enableSecurityDescription', { + defaultMessage: + 'Security must be enabled in order to use Logstash pipeline management features.' + + ' Please set xpack.security.enabled: true in your elasticsearch.yml.', + }), + }; + } + + if (!this.license.hasAtLeast('standard')) { + return { + isAvailable: false, + enableLinks: false, + isReadOnly: false, + message: i18n.translate( + 'xpack.logstash.managementSection.licenseDoesNotSupportDescription', + { + defaultMessage: + 'Your {licenseType} license does not support Logstash pipeline management features. Please upgrade your license.', + values: { licenseType: this.license.type }, + } + ), + }; + } + + if (!this.license.isActive) { + return { + isAvailable: true, + enableLinks: true, + isReadonly: true, + message: i18n.translate( + 'xpack.logstash.managementSection.pipelineCrudOperationsNotAllowedDescription', + { + defaultMessage: + 'You cannot edit, create, or delete your Logstash pipelines because your {licenseType} license has expired.', + values: { licenseType: this.license.type }, + } + ), + }; + } + + return { + isAvailable: true, + enableLinks: true, + isReadOnly: false, + }; + } +} diff --git a/x-pack/plugins/logstash/public/services/monitoring/index.js b/x-pack/plugins/logstash/public/services/monitoring/index.js new file mode 100755 index 00000000000000..bc0e8b6bc978a7 --- /dev/null +++ b/x-pack/plugins/logstash/public/services/monitoring/index.js @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { MonitoringService } from './monitoring_service'; diff --git a/x-pack/legacy/plugins/logstash/public/services/monitoring/monitoring_service.js b/x-pack/plugins/logstash/public/services/monitoring/monitoring_service.js similarity index 58% rename from x-pack/legacy/plugins/logstash/public/services/monitoring/monitoring_service.js rename to x-pack/plugins/logstash/public/services/monitoring/monitoring_service.js index 6103e730c21714..d551f4fba61d2e 100755 --- a/x-pack/legacy/plugins/logstash/public/services/monitoring/monitoring_service.js +++ b/x-pack/plugins/logstash/public/services/monitoring/monitoring_service.js @@ -5,17 +5,14 @@ */ import moment from 'moment'; -import chrome from 'ui/chrome'; -import { ROUTES, MONITORING } from '../../../../../../plugins/logstash/common/constants'; -import { PipelineListItem } from 'plugins/logstash/models/pipeline_list_item'; +import { ROUTES, MONITORING } from '../../../common/constants'; +import { PipelineListItem } from '../../models/pipeline_list_item'; export class MonitoringService { - constructor($http, Promise, monitoringUiEnabled, clusterService) { - this.$http = $http; - this.Promise = Promise; + constructor(http, monitoringUiEnabled, clusterService) { + this.http = http; this.monitoringUiEnabled = monitoringUiEnabled; this.clusterService = clusterService; - this.basePath = chrome.addBasePath(ROUTES.MONITORING_API_ROOT); } isMonitoringEnabled() { @@ -30,18 +27,18 @@ export class MonitoringService { return this.clusterService .loadCluster() .then(cluster => { - const url = `${this.basePath}/v1/clusters/${cluster.uuid}/logstash/pipeline_ids`; + const url = `${ROUTES.MONITORING_API_ROOT}/v1/clusters/${cluster.uuid}/logstash/pipeline_ids`; const now = moment.utc(); - const body = { + const body = JSON.stringify({ timeRange: { max: now.toISOString(), min: now.subtract(MONITORING.ACTIVE_PIPELINE_RANGE_S, 'seconds').toISOString(), }, - }; - return this.$http.post(url, body); + }); + return this.http.post(url, { body }); }) .then(response => - response.data.map(pipeline => PipelineListItem.fromUpstreamMonitoringJSON(pipeline)) + response.map(pipeline => PipelineListItem.fromUpstreamMonitoringJSON(pipeline)) ) .catch(() => []); } diff --git a/x-pack/legacy/plugins/logstash/common/lib/index.js b/x-pack/plugins/logstash/public/services/pipeline/index.js similarity index 81% rename from x-pack/legacy/plugins/logstash/common/lib/index.js rename to x-pack/plugins/logstash/public/services/pipeline/index.js index 6ed1d24a377916..70d228b34860b9 100755 --- a/x-pack/legacy/plugins/logstash/common/lib/index.js +++ b/x-pack/plugins/logstash/public/services/pipeline/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { getMoment } from './get_moment'; +export { PipelineService } from './pipeline_service'; diff --git a/x-pack/plugins/logstash/public/services/pipeline/pipeline_service.js b/x-pack/plugins/logstash/public/services/pipeline/pipeline_service.js new file mode 100755 index 00000000000000..7c3e18e745d821 --- /dev/null +++ b/x-pack/plugins/logstash/public/services/pipeline/pipeline_service.js @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ROUTES } from '../../../common/constants'; +import { Pipeline } from '../../models/pipeline'; + +export class PipelineService { + constructor(http, pipelinesService) { + this.http = http; + this.pipelinesService = pipelinesService; + } + + loadPipeline(id) { + return this.http.get(`${ROUTES.API_ROOT}/pipeline/${id}`).then(response => { + return Pipeline.fromUpstreamJSON(response); + }); + } + + savePipeline(pipelineModel) { + return this.http + .put(`${ROUTES.API_ROOT}/pipeline/${pipelineModel.id}`, { + body: JSON.stringify(pipelineModel.upstreamJSON), + }) + .catch(e => { + throw e.message; + }); + } + + deletePipeline(id) { + return this.http + .delete(`${ROUTES.API_ROOT}/pipeline/${id}`) + .then(() => this.pipelinesService.addToRecentlyDeleted(id)) + .catch(e => { + throw e.message; + }); + } +} diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/index.js b/x-pack/plugins/logstash/public/services/pipelines/index.js similarity index 81% rename from x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/index.js rename to x-pack/plugins/logstash/public/services/pipelines/index.js index 4b699ed79cd26b..a932dd4b951f4e 100755 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/index.js +++ b/x-pack/plugins/logstash/public/services/pipelines/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import './pipeline_edit_route'; +export { PipelinesService } from './pipelines_service'; diff --git a/x-pack/plugins/logstash/public/services/pipelines/pipelines_service.js b/x-pack/plugins/logstash/public/services/pipelines/pipelines_service.js new file mode 100755 index 00000000000000..00610a23f27177 --- /dev/null +++ b/x-pack/plugins/logstash/public/services/pipelines/pipelines_service.js @@ -0,0 +1,128 @@ +/* + * 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 { ROUTES, MONITORING } from '../../../common/constants'; +import { PipelineListItem } from '../../models/pipeline_list_item'; + +const RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY = 'xpack.logstash.recentlyDeletedPipelines'; + +export class PipelinesService { + constructor(http, monitoringService) { + this.http = http; + this.monitoringService = monitoringService; + } + + getPipelineList() { + return Promise.all([this.getManagementPipelineList(), this.getMonitoringPipelineList()]).then( + ([managementPipelines, monitoringPipelines]) => { + const now = Date.now(); + + // Monitoring will report centrally-managed pipelines as well, including recently-deleted centrally-managed ones. + // If there's a recently-deleted pipeline we're keeping track of BUT monitoring doesn't report it, that means + // it's not running in Logstash any more. So we can stop tracking it as a recently-deleted pipeline. + const monitoringPipelineIds = monitoringPipelines.map(pipeline => pipeline.id); + this.getRecentlyDeleted().forEach(recentlyDeletedPipeline => { + // We don't want to stop tracking the recently-deleted pipeline until Monitoring has had some + // time to report on it. Otherwise, if we stop tracking first, *then* Monitoring reports it, we'll + // still end up showing it in the list until Monitoring stops reporting it. + if (now - recentlyDeletedPipeline.deletedOn < MONITORING.ACTIVE_PIPELINE_RANGE_S * 1000) { + return; + } + + // If Monitoring is still reporting the pipeline, don't stop tracking it yet + if (monitoringPipelineIds.includes(recentlyDeletedPipeline.id)) { + return; + } + + this.removeFromRecentlyDeleted(recentlyDeletedPipeline.id); + }); + + // Merge centrally-managed pipelines with pipelines reported by monitoring. Take care to dedupe + // while merging because monitoring will (rightly) report centrally-managed pipelines as well, + // including recently-deleted ones! + const managementPipelineIds = managementPipelines.map(pipeline => pipeline.id); + return managementPipelines.concat( + monitoringPipelines.filter( + monitoringPipeline => + !managementPipelineIds.includes(monitoringPipeline.id) && + !this.isRecentlyDeleted(monitoringPipeline.id) + ) + ); + } + ); + } + + getManagementPipelineList() { + return this.http.get(`${ROUTES.API_ROOT}/pipelines`).then(response => { + return response.pipelines.map(pipeline => PipelineListItem.fromUpstreamJSON(pipeline)); + }); + } + + getMonitoringPipelineList() { + return this.monitoringService.getPipelineList(); + } + + /** + * Delete a collection of pipelines + * + * @param pipelineIds Array of pipeline IDs + * @return Promise { numSuccesses, numErrors } + */ + deletePipelines(pipelineIds) { + const body = JSON.stringify({ + pipelineIds, + }); + return this.http.post(`${ROUTES.API_ROOT}/pipelines/delete`, { body }).then(response => { + this.addToRecentlyDeleted(...pipelineIds); + return response.results; + }); + } + + addToRecentlyDeleted(...pipelineIds) { + const recentlyDeletedPipelines = this.getRecentlyDeleted(); + const recentlyDeletedPipelineIds = recentlyDeletedPipelines.map(pipeline => pipeline.id); + pipelineIds.forEach(pipelineId => { + if (!recentlyDeletedPipelineIds.includes(pipelineId)) { + recentlyDeletedPipelines.push({ + id: pipelineId, + deletedOn: Date.now(), + }); + } + }); + this.setRecentlyDeleted(recentlyDeletedPipelines); + } + + removeFromRecentlyDeleted(...pipelineIds) { + const recentlyDeletedPipelinesToKeep = this.getRecentlyDeleted().filter( + recentlyDeletedPipeline => !pipelineIds.includes(recentlyDeletedPipeline.id) + ); + this.setRecentlyDeleted(recentlyDeletedPipelinesToKeep); + } + + isRecentlyDeleted(pipelineId) { + return this.getRecentlyDeleted() + .map(pipeline => pipeline.id) + .includes(pipelineId); + } + + getRecentlyDeleted() { + const recentlyDeletedPipelines = window.localStorage.getItem( + RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY + ); + if (!recentlyDeletedPipelines) { + return []; + } + + return JSON.parse(recentlyDeletedPipelines); + } + + setRecentlyDeleted(recentlyDeletedPipelineIds) { + window.localStorage.setItem( + RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY, + JSON.stringify(recentlyDeletedPipelineIds) + ); + } +} diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/upgrade_failure/index.js b/x-pack/plugins/logstash/public/services/upgrade/index.js similarity index 82% rename from x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/upgrade_failure/index.js rename to x-pack/plugins/logstash/public/services/upgrade/index.js index 3a9a6b860c51f1..1c835b11ae4233 100755 --- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/upgrade_failure/index.js +++ b/x-pack/plugins/logstash/public/services/upgrade/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import './upgrade_failure'; +export { UpgradeService } from './upgrade_service'; diff --git a/x-pack/plugins/logstash/public/services/upgrade/upgrade_service.js b/x-pack/plugins/logstash/public/services/upgrade/upgrade_service.js new file mode 100755 index 00000000000000..7bd101ebee6b00 --- /dev/null +++ b/x-pack/plugins/logstash/public/services/upgrade/upgrade_service.js @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ROUTES } from '../../../common/constants'; + +export class UpgradeService { + constructor(http) { + this.http = http; + } + + executeUpgrade() { + return this.http + .post(`${ROUTES.API_ROOT}/upgrade`) + .then(response => response.is_upgraded) + .catch(e => { + throw e.message; + }); + } +} diff --git a/x-pack/plugins/logstash/server/routes/pipeline/save.ts b/x-pack/plugins/logstash/server/routes/pipeline/save.ts index 556c281944a856..e484d0e221b6d1 100644 --- a/x-pack/plugins/logstash/server/routes/pipeline/save.ts +++ b/x-pack/plugins/logstash/server/routes/pipeline/save.ts @@ -25,7 +25,6 @@ export function registerPipelineSaveRoute(router: IRouter, security?: SecurityPl id: schema.string(), description: schema.string(), pipeline: schema.string(), - username: schema.string(), settings: schema.maybe(schema.object({}, { unknowns: 'allow' })), }), }, diff --git a/x-pack/legacy/plugins/maps/common/migrations/add_field_meta_options.js b/x-pack/plugins/maps/common/migrations/add_field_meta_options.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/add_field_meta_options.js rename to x-pack/plugins/maps/common/migrations/add_field_meta_options.js diff --git a/x-pack/legacy/plugins/maps/common/migrations/add_field_meta_options.test.js b/x-pack/plugins/maps/common/migrations/add_field_meta_options.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/add_field_meta_options.test.js rename to x-pack/plugins/maps/common/migrations/add_field_meta_options.test.js diff --git a/x-pack/legacy/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.js b/x-pack/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.js rename to x-pack/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.js diff --git a/x-pack/legacy/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.test.js b/x-pack/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.test.js rename to x-pack/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.test.js diff --git a/x-pack/legacy/plugins/maps/common/migrations/join_agg_key.test.ts b/x-pack/plugins/maps/common/migrations/join_agg_key.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/join_agg_key.test.ts rename to x-pack/plugins/maps/common/migrations/join_agg_key.test.ts diff --git a/x-pack/legacy/plugins/maps/common/migrations/join_agg_key.ts b/x-pack/plugins/maps/common/migrations/join_agg_key.ts similarity index 97% rename from x-pack/legacy/plugins/maps/common/migrations/join_agg_key.ts rename to x-pack/plugins/maps/common/migrations/join_agg_key.ts index 29661aedb550c5..97b9ee4692c253 100644 --- a/x-pack/legacy/plugins/maps/common/migrations/join_agg_key.ts +++ b/x-pack/plugins/maps/common/migrations/join_agg_key.ts @@ -20,7 +20,7 @@ import { LayerDescriptor, VectorLayerDescriptor, } from '../descriptor_types'; -import { MapSavedObjectAttributes } from '../../../../../plugins/maps/common/map_saved_object_type'; +import { MapSavedObjectAttributes } from '../map_saved_object_type'; const GROUP_BY_DELIMITER = '_groupby_'; diff --git a/x-pack/legacy/plugins/maps/common/migrations/migrate_symbol_style_descriptor.js b/x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/migrate_symbol_style_descriptor.js rename to x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.js diff --git a/x-pack/legacy/plugins/maps/common/migrations/migrate_symbol_style_descriptor.test.js b/x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/migrate_symbol_style_descriptor.test.js rename to x-pack/plugins/maps/common/migrations/migrate_symbol_style_descriptor.test.js diff --git a/x-pack/legacy/plugins/maps/common/migrations/move_apply_global_query.js b/x-pack/plugins/maps/common/migrations/move_apply_global_query.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/move_apply_global_query.js rename to x-pack/plugins/maps/common/migrations/move_apply_global_query.js diff --git a/x-pack/legacy/plugins/maps/common/migrations/move_apply_global_query.test.js b/x-pack/plugins/maps/common/migrations/move_apply_global_query.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/move_apply_global_query.test.js rename to x-pack/plugins/maps/common/migrations/move_apply_global_query.test.js diff --git a/x-pack/legacy/plugins/maps/common/migrations/references.js b/x-pack/plugins/maps/common/migrations/references.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/references.js rename to x-pack/plugins/maps/common/migrations/references.js diff --git a/x-pack/legacy/plugins/maps/common/migrations/references.test.js b/x-pack/plugins/maps/common/migrations/references.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/references.test.js rename to x-pack/plugins/maps/common/migrations/references.test.js diff --git a/x-pack/legacy/plugins/maps/common/migrations/scaling_type.test.ts b/x-pack/plugins/maps/common/migrations/scaling_type.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/scaling_type.test.ts rename to x-pack/plugins/maps/common/migrations/scaling_type.test.ts diff --git a/x-pack/legacy/plugins/maps/common/migrations/scaling_type.ts b/x-pack/plugins/maps/common/migrations/scaling_type.ts similarity index 93% rename from x-pack/legacy/plugins/maps/common/migrations/scaling_type.ts rename to x-pack/plugins/maps/common/migrations/scaling_type.ts index 551975fbacea52..98a06a764f4ec4 100644 --- a/x-pack/legacy/plugins/maps/common/migrations/scaling_type.ts +++ b/x-pack/plugins/maps/common/migrations/scaling_type.ts @@ -7,7 +7,7 @@ import _ from 'lodash'; import { SOURCE_TYPES, SCALING_TYPES } from '../constants'; import { LayerDescriptor, ESSearchSourceDescriptor } from '../descriptor_types'; -import { MapSavedObjectAttributes } from '../../../../../plugins/maps/common/map_saved_object_type'; +import { MapSavedObjectAttributes } from '../map_saved_object_type'; function isEsDocumentSource(layerDescriptor: LayerDescriptor) { const sourceType = _.get(layerDescriptor, 'sourceDescriptor.type'); diff --git a/x-pack/legacy/plugins/maps/common/migrations/top_hits_time_to_sort.js b/x-pack/plugins/maps/common/migrations/top_hits_time_to_sort.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/top_hits_time_to_sort.js rename to x-pack/plugins/maps/common/migrations/top_hits_time_to_sort.js diff --git a/x-pack/legacy/plugins/maps/common/migrations/top_hits_time_to_sort.test.js b/x-pack/plugins/maps/common/migrations/top_hits_time_to_sort.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/common/migrations/top_hits_time_to_sort.test.js rename to x-pack/plugins/maps/common/migrations/top_hits_time_to_sort.test.js diff --git a/x-pack/plugins/maps/kibana.json b/x-pack/plugins/maps/kibana.json index b2aec30c113eb6..00c5e70ad6b8de 100644 --- a/x-pack/plugins/maps/kibana.json +++ b/x-pack/plugins/maps/kibana.json @@ -3,6 +3,14 @@ "version": "8.0.0", "kibanaVersion": "kibana", "configPath": ["xpack", "maps"], - "requiredPlugins": ["inspector"], + "requiredPlugins": [ + "inspector", + "home", + "data", + "fileUpload", + "uiActions", + "navigation", + "visualizations" + ], "ui": true } diff --git a/x-pack/legacy/plugins/maps/public/_main.scss b/x-pack/plugins/maps/public/_main.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/_main.scss rename to x-pack/plugins/maps/public/_main.scss diff --git a/x-pack/legacy/plugins/maps/public/_mapbox_hacks.scss b/x-pack/plugins/maps/public/_mapbox_hacks.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/_mapbox_hacks.scss rename to x-pack/plugins/maps/public/_mapbox_hacks.scss diff --git a/x-pack/plugins/maps/public/actions/map_actions.js b/x-pack/plugins/maps/public/actions/map_actions.js index 13cb3d5f898601..572385d628b16a 100644 --- a/x-pack/plugins/maps/public/actions/map_actions.js +++ b/x-pack/plugins/maps/public/actions/map_actions.js @@ -4,6 +4,37 @@ * you may not use this file except in compliance with the Elastic License. */ +import _ from 'lodash'; +import turf from 'turf'; +import turfBooleanContains from '@turf/boolean-contains'; +import uuid from 'uuid/v4'; +import { + getLayerList, + getLayerListRaw, + getDataFilters, + getSelectedLayerId, + getMapReady, + getWaitingForMapReadyLayerListRaw, + getTransientLayerId, + getOpenTooltips, + getQuery, + getDataRequestDescriptor, +} from '../selectors/map_selectors'; + +import { FLYOUT_STATE } from '../reducers/ui'; +import { + cancelRequest, + registerCancelCallback, + unregisterCancelCallback, + getEventHandlers, +} from '../reducers/non_serializable_instances'; +import { updateFlyout } from './ui_actions'; +import { + FEATURE_ID_PROPERTY_NAME, + LAYER_TYPE, + SOURCE_DATA_ID_ORIGIN, +} from '../../common/constants'; + export const SET_SELECTED_LAYER = 'SET_SELECTED_LAYER'; export const SET_TRANSIENT_LAYER = 'SET_TRANSIENT_LAYER'; export const UPDATE_LAYER_ORDER = 'UPDATE_LAYER_ORDER'; @@ -45,3 +76,897 @@ export const HIDE_TOOLBAR_OVERLAY = 'HIDE_TOOLBAR_OVERLAY'; export const HIDE_LAYER_CONTROL = 'HIDE_LAYER_CONTROL'; export const HIDE_VIEW_CONTROL = 'HIDE_VIEW_CONTROL'; export const SET_WAITING_FOR_READY_HIDDEN_LAYERS = 'SET_WAITING_FOR_READY_HIDDEN_LAYERS'; + +function getLayerLoadingCallbacks(dispatch, getState, layerId) { + return { + startLoading: (dataId, requestToken, meta) => + dispatch(startDataLoad(layerId, dataId, requestToken, meta)), + stopLoading: (dataId, requestToken, data, meta) => + dispatch(endDataLoad(layerId, dataId, requestToken, data, meta)), + onLoadError: (dataId, requestToken, errorMessage) => + dispatch(onDataLoadError(layerId, dataId, requestToken, errorMessage)), + updateSourceData: newData => { + dispatch(updateSourceDataRequest(layerId, newData)); + }, + isRequestStillActive: (dataId, requestToken) => { + const dataRequest = getDataRequestDescriptor(getState(), layerId, dataId); + if (!dataRequest) { + return false; + } + return dataRequest.dataRequestToken === requestToken; + }, + registerCancelCallback: (requestToken, callback) => + dispatch(registerCancelCallback(requestToken, callback)), + }; +} + +function getLayerById(layerId, state) { + return getLayerList(state).find(layer => { + return layerId === layer.getId(); + }); +} + +async function syncDataForAllLayers(dispatch, getState, dataFilters) { + const state = getState(); + const layerList = getLayerList(state); + const syncs = layerList.map(layer => { + const loadingFunctions = getLayerLoadingCallbacks(dispatch, getState, layer.getId()); + return layer.syncData({ ...loadingFunctions, dataFilters }); + }); + await Promise.all(syncs); +} + +export function cancelAllInFlightRequests() { + return (dispatch, getState) => { + getLayerList(getState()).forEach(layer => { + dispatch(clearDataRequests(layer)); + }); + }; +} + +function clearDataRequests(layer) { + return dispatch => { + layer.getInFlightRequestTokens().forEach(requestToken => { + dispatch(cancelRequest(requestToken)); + }); + dispatch({ + type: UPDATE_LAYER_PROP, + id: layer.getId(), + propName: '__dataRequests', + newValue: [], + }); + }; +} + +export function setMapInitError(errorMessage) { + return { + type: SET_MAP_INIT_ERROR, + errorMessage, + }; +} + +export function trackCurrentLayerState(layerId) { + return { + type: TRACK_CURRENT_LAYER_STATE, + layerId: layerId, + }; +} + +export function rollbackToTrackedLayerStateForSelectedLayer() { + return async (dispatch, getState) => { + const layerId = getSelectedLayerId(getState()); + await dispatch({ + type: ROLLBACK_TO_TRACKED_LAYER_STATE, + layerId: layerId, + }); + + // Ensure updateStyleMeta is triggered + // syncDataForLayer may not trigger endDataLoad if no re-fetch is required + dispatch(updateStyleMeta(layerId)); + + dispatch(syncDataForLayer(layerId)); + }; +} + +export function removeTrackedLayerStateForSelectedLayer() { + return (dispatch, getState) => { + const layerId = getSelectedLayerId(getState()); + dispatch({ + type: REMOVE_TRACKED_LAYER_STATE, + layerId: layerId, + }); + }; +} + +export function replaceLayerList(newLayerList) { + return (dispatch, getState) => { + const isMapReady = getMapReady(getState()); + if (!isMapReady) { + dispatch({ + type: CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST, + }); + } else { + getLayerListRaw(getState()).forEach(({ id }) => { + dispatch(removeLayerFromLayerList(id)); + }); + } + + newLayerList.forEach(layerDescriptor => { + dispatch(addLayer(layerDescriptor)); + }); + }; +} + +export function cloneLayer(layerId) { + return async (dispatch, getState) => { + const layer = getLayerById(layerId, getState()); + if (!layer) { + return; + } + + const clonedDescriptor = await layer.cloneDescriptor(); + dispatch(addLayer(clonedDescriptor)); + }; +} + +export function addLayer(layerDescriptor) { + return (dispatch, getState) => { + const isMapReady = getMapReady(getState()); + if (!isMapReady) { + dispatch({ + type: ADD_WAITING_FOR_MAP_READY_LAYER, + layer: layerDescriptor, + }); + return; + } + + dispatch({ + type: ADD_LAYER, + layer: layerDescriptor, + }); + dispatch(syncDataForLayer(layerDescriptor.id)); + }; +} + +// Do not use when rendering a map. Method exists to enable selectors for getLayerList when +// rendering is not needed. +export function addLayerWithoutDataSync(layerDescriptor) { + return { + type: ADD_LAYER, + layer: layerDescriptor, + }; +} + +function setLayerDataLoadErrorStatus(layerId, errorMessage) { + return dispatch => { + dispatch({ + type: SET_LAYER_ERROR_STATUS, + isInErrorState: errorMessage !== null, + layerId, + errorMessage, + }); + }; +} + +export function cleanTooltipStateForLayer(layerId, layerFeatures = []) { + return (dispatch, getState) => { + let featuresRemoved = false; + const openTooltips = getOpenTooltips(getState()) + .map(tooltipState => { + const nextFeatures = tooltipState.features.filter(tooltipFeature => { + if (tooltipFeature.layerId !== layerId) { + // feature from another layer, keep it + return true; + } + + // Keep feature if it is still in layer + return layerFeatures.some(layerFeature => { + return layerFeature.properties[FEATURE_ID_PROPERTY_NAME] === tooltipFeature.id; + }); + }); + + if (tooltipState.features.length !== nextFeatures.length) { + featuresRemoved = true; + } + + return { ...tooltipState, features: nextFeatures }; + }) + .filter(tooltipState => { + return tooltipState.features.length > 0; + }); + + if (featuresRemoved) { + dispatch({ + type: SET_OPEN_TOOLTIPS, + openTooltips, + }); + } + }; +} + +export function setLayerVisibility(layerId, makeVisible) { + return async (dispatch, getState) => { + //if the current-state is invisible, we also want to sync data + //e.g. if a layer was invisible at start-up, it won't have any data loaded + const layer = getLayerById(layerId, getState()); + + // If the layer visibility is already what we want it to be, do nothing + if (!layer || layer.isVisible() === makeVisible) { + return; + } + + if (!makeVisible) { + dispatch(cleanTooltipStateForLayer(layerId)); + } + + await dispatch({ + type: SET_LAYER_VISIBILITY, + layerId, + visibility: makeVisible, + }); + if (makeVisible) { + dispatch(syncDataForLayer(layerId)); + } + }; +} + +export function toggleLayerVisible(layerId) { + return async (dispatch, getState) => { + const layer = getLayerById(layerId, getState()); + if (!layer) { + return; + } + const makeVisible = !layer.isVisible(); + + dispatch(setLayerVisibility(layerId, makeVisible)); + }; +} + +export function setSelectedLayer(layerId) { + return async (dispatch, getState) => { + const oldSelectedLayer = getSelectedLayerId(getState()); + if (oldSelectedLayer) { + await dispatch(rollbackToTrackedLayerStateForSelectedLayer()); + } + if (layerId) { + dispatch(trackCurrentLayerState(layerId)); + } + dispatch({ + type: SET_SELECTED_LAYER, + selectedLayerId: layerId, + }); + }; +} + +export function removeTransientLayer() { + return async (dispatch, getState) => { + const transientLayerId = getTransientLayerId(getState()); + if (transientLayerId) { + await dispatch(removeLayerFromLayerList(transientLayerId)); + await dispatch(setTransientLayer(null)); + } + }; +} + +export function setTransientLayer(layerId) { + return { + type: SET_TRANSIENT_LAYER, + transientLayerId: layerId, + }; +} + +export function clearTransientLayerStateAndCloseFlyout() { + return async dispatch => { + await dispatch(updateFlyout(FLYOUT_STATE.NONE)); + await dispatch(setSelectedLayer(null)); + await dispatch(removeTransientLayer()); + }; +} + +export function updateLayerOrder(newLayerOrder) { + return { + type: UPDATE_LAYER_ORDER, + newLayerOrder, + }; +} + +export function mapReady() { + return (dispatch, getState) => { + dispatch({ + type: MAP_READY, + }); + + getWaitingForMapReadyLayerListRaw(getState()).forEach(layerDescriptor => { + dispatch(addLayer(layerDescriptor)); + }); + + dispatch({ + type: CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST, + }); + }; +} + +export function mapDestroyed() { + return { + type: MAP_DESTROYED, + }; +} + +export function mapExtentChanged(newMapConstants) { + return async (dispatch, getState) => { + const state = getState(); + const dataFilters = getDataFilters(state); + const { extent, zoom: newZoom } = newMapConstants; + const { buffer, zoom: currentZoom } = dataFilters; + + if (extent) { + let doesBufferContainExtent = false; + if (buffer) { + const bufferGeometry = turf.bboxPolygon([ + buffer.minLon, + buffer.minLat, + buffer.maxLon, + buffer.maxLat, + ]); + const extentGeometry = turf.bboxPolygon([ + extent.minLon, + extent.minLat, + extent.maxLon, + extent.maxLat, + ]); + + doesBufferContainExtent = turfBooleanContains(bufferGeometry, extentGeometry); + } + + if (!doesBufferContainExtent || currentZoom !== newZoom) { + const scaleFactor = 0.5; // TODO put scale factor in store and fetch with selector + const width = extent.maxLon - extent.minLon; + const height = extent.maxLat - extent.minLat; + dataFilters.buffer = { + minLon: extent.minLon - width * scaleFactor, + minLat: extent.minLat - height * scaleFactor, + maxLon: extent.maxLon + width * scaleFactor, + maxLat: extent.maxLat + height * scaleFactor, + }; + } + } + + dispatch({ + type: MAP_EXTENT_CHANGED, + mapState: { + ...dataFilters, + ...newMapConstants, + }, + }); + const newDataFilters = { ...dataFilters, ...newMapConstants }; + await syncDataForAllLayers(dispatch, getState, newDataFilters); + }; +} + +export function closeOnClickTooltip(tooltipId) { + return (dispatch, getState) => { + dispatch({ + type: SET_OPEN_TOOLTIPS, + openTooltips: getOpenTooltips(getState()).filter(({ id }) => { + return tooltipId !== id; + }), + }); + }; +} + +export function openOnClickTooltip(tooltipState) { + return (dispatch, getState) => { + const openTooltips = getOpenTooltips(getState()).filter(({ features, location, isLocked }) => { + return ( + isLocked && + !_.isEqual(location, tooltipState.location) && + !_.isEqual(features, tooltipState.features) + ); + }); + + openTooltips.push({ + ...tooltipState, + isLocked: true, + id: uuid(), + }); + + dispatch({ + type: SET_OPEN_TOOLTIPS, + openTooltips, + }); + }; +} + +export function closeOnHoverTooltip() { + return (dispatch, getState) => { + if (getOpenTooltips(getState()).length) { + dispatch({ + type: SET_OPEN_TOOLTIPS, + openTooltips: [], + }); + } + }; +} + +export function openOnHoverTooltip(tooltipState) { + return { + type: SET_OPEN_TOOLTIPS, + openTooltips: [ + { + ...tooltipState, + isLocked: false, + id: uuid(), + }, + ], + }; +} + +export function setMouseCoordinates({ lat, lon }) { + let safeLon = lon; + if (lon > 180) { + const overlapWestOfDateLine = lon - 180; + safeLon = -180 + overlapWestOfDateLine; + } else if (lon < -180) { + const overlapEastOfDateLine = Math.abs(lon) - 180; + safeLon = 180 - overlapEastOfDateLine; + } + + return { + type: SET_MOUSE_COORDINATES, + lat, + lon: safeLon, + }; +} + +export function clearMouseCoordinates() { + return { type: CLEAR_MOUSE_COORDINATES }; +} + +export function disableScrollZoom() { + return { type: SET_SCROLL_ZOOM, scrollZoom: false }; +} + +export function fitToLayerExtent(layerId) { + return async function(dispatch, getState) { + const targetLayer = getLayerById(layerId, getState()); + + if (targetLayer) { + const dataFilters = getDataFilters(getState()); + const bounds = await targetLayer.getBounds(dataFilters); + if (bounds) { + await dispatch(setGotoWithBounds(bounds)); + } + } + }; +} + +export function setGotoWithBounds(bounds) { + return { + type: SET_GOTO, + bounds: bounds, + }; +} + +export function setGotoWithCenter({ lat, lon, zoom }) { + return { + type: SET_GOTO, + center: { lat, lon, zoom }, + }; +} + +export function clearGoto() { + return { type: CLEAR_GOTO }; +} + +export function startDataLoad(layerId, dataId, requestToken, meta = {}) { + return (dispatch, getState) => { + const layer = getLayerById(layerId, getState()); + if (layer) { + dispatch(cancelRequest(layer.getPrevRequestToken(dataId))); + } + + const eventHandlers = getEventHandlers(getState()); + if (eventHandlers && eventHandlers.onDataLoad) { + eventHandlers.onDataLoad({ + layerId, + dataId, + }); + } + + dispatch({ + meta, + type: LAYER_DATA_LOAD_STARTED, + layerId, + dataId, + requestToken, + }); + }; +} + +export function updateSourceDataRequest(layerId, newData) { + return dispatch => { + dispatch({ + type: UPDATE_SOURCE_DATA_REQUEST, + dataId: SOURCE_DATA_ID_ORIGIN, + layerId, + newData, + }); + + dispatch(updateStyleMeta(layerId)); + }; +} + +export function endDataLoad(layerId, dataId, requestToken, data, meta) { + return async (dispatch, getState) => { + dispatch(unregisterCancelCallback(requestToken)); + + const features = data && data.features ? data.features : []; + + const eventHandlers = getEventHandlers(getState()); + if (eventHandlers && eventHandlers.onDataLoadEnd) { + const layer = getLayerById(layerId, getState()); + const resultMeta = {}; + if (layer && layer.getType() === LAYER_TYPE.VECTOR) { + resultMeta.featuresCount = features.length; + } + + eventHandlers.onDataLoadEnd({ + layerId, + dataId, + resultMeta, + }); + } + + dispatch(cleanTooltipStateForLayer(layerId, features)); + dispatch({ + type: LAYER_DATA_LOAD_ENDED, + layerId, + dataId, + data, + meta, + requestToken, + }); + + //Clear any data-load errors when there is a succesful data return. + //Co this on end-data-load iso at start-data-load to avoid blipping the error status between true/false. + //This avoids jitter in the warning icon of the TOC when the requests continues to return errors. + dispatch(setLayerDataLoadErrorStatus(layerId, null)); + + dispatch(updateStyleMeta(layerId)); + }; +} + +export function onDataLoadError(layerId, dataId, requestToken, errorMessage) { + return async (dispatch, getState) => { + dispatch(unregisterCancelCallback(requestToken)); + + const eventHandlers = getEventHandlers(getState()); + if (eventHandlers && eventHandlers.onDataLoadError) { + eventHandlers.onDataLoadError({ + layerId, + dataId, + errorMessage, + }); + } + + dispatch(cleanTooltipStateForLayer(layerId)); + dispatch({ + type: LAYER_DATA_LOAD_ERROR, + data: null, + layerId, + dataId, + requestToken, + }); + + dispatch(setLayerDataLoadErrorStatus(layerId, errorMessage)); + }; +} + +export function updateSourceProp(layerId, propName, value, newLayerType) { + return async dispatch => { + dispatch({ + type: UPDATE_SOURCE_PROP, + layerId, + propName, + value, + }); + if (newLayerType) { + dispatch(updateLayerType(layerId, newLayerType)); + } + await dispatch(clearMissingStyleProperties(layerId)); + dispatch(syncDataForLayer(layerId)); + }; +} + +function updateLayerType(layerId, newLayerType) { + return (dispatch, getState) => { + const layer = getLayerById(layerId, getState()); + if (!layer || layer.getType() === newLayerType) { + return; + } + dispatch(clearDataRequests(layer)); + dispatch({ + type: UPDATE_LAYER_PROP, + id: layerId, + propName: 'type', + newValue: newLayerType, + }); + }; +} + +export function syncDataForLayer(layerId) { + return async (dispatch, getState) => { + const targetLayer = getLayerById(layerId, getState()); + if (targetLayer) { + const dataFilters = getDataFilters(getState()); + const loadingFunctions = getLayerLoadingCallbacks(dispatch, getState, layerId); + await targetLayer.syncData({ + ...loadingFunctions, + dataFilters, + }); + } + }; +} + +export function updateLayerLabel(id, newLabel) { + return { + type: UPDATE_LAYER_PROP, + id, + propName: 'label', + newValue: newLabel, + }; +} + +export function updateLayerMinZoom(id, minZoom) { + return { + type: UPDATE_LAYER_PROP, + id, + propName: 'minZoom', + newValue: minZoom, + }; +} + +export function updateLayerMaxZoom(id, maxZoom) { + return { + type: UPDATE_LAYER_PROP, + id, + propName: 'maxZoom', + newValue: maxZoom, + }; +} + +export function updateLayerAlpha(id, alpha) { + return { + type: UPDATE_LAYER_PROP, + id, + propName: 'alpha', + newValue: alpha, + }; +} + +export function setLayerQuery(id, query) { + return dispatch => { + dispatch({ + type: UPDATE_LAYER_PROP, + id, + propName: 'query', + newValue: query, + }); + + dispatch(syncDataForLayer(id)); + }; +} + +export function removeSelectedLayer() { + return (dispatch, getState) => { + const state = getState(); + const layerId = getSelectedLayerId(state); + dispatch(removeLayer(layerId)); + }; +} + +export function removeLayer(layerId) { + return async (dispatch, getState) => { + const state = getState(); + const selectedLayerId = getSelectedLayerId(state); + if (layerId === selectedLayerId) { + dispatch(updateFlyout(FLYOUT_STATE.NONE)); + await dispatch(setSelectedLayer(null)); + } + dispatch(removeLayerFromLayerList(layerId)); + }; +} + +function removeLayerFromLayerList(layerId) { + return (dispatch, getState) => { + const layerGettingRemoved = getLayerById(layerId, getState()); + if (!layerGettingRemoved) { + return; + } + + layerGettingRemoved.getInFlightRequestTokens().forEach(requestToken => { + dispatch(cancelRequest(requestToken)); + }); + dispatch(cleanTooltipStateForLayer(layerId)); + layerGettingRemoved.destroy(); + dispatch({ + type: REMOVE_LAYER, + id: layerId, + }); + }; +} + +export function setQuery({ query, timeFilters, filters = [], refresh = false }) { + function generateQueryTimestamp() { + return new Date().toISOString(); + } + return async (dispatch, getState) => { + const prevQuery = getQuery(getState()); + const prevTriggeredAt = + prevQuery && prevQuery.queryLastTriggeredAt + ? prevQuery.queryLastTriggeredAt + : generateQueryTimestamp(); + + dispatch({ + type: SET_QUERY, + timeFilters, + query: { + ...query, + // ensure query changes to trigger re-fetch when "Refresh" clicked + queryLastTriggeredAt: refresh ? generateQueryTimestamp() : prevTriggeredAt, + }, + filters, + }); + + const dataFilters = getDataFilters(getState()); + await syncDataForAllLayers(dispatch, getState, dataFilters); + }; +} + +export function setRefreshConfig({ isPaused, interval }) { + return { + type: SET_REFRESH_CONFIG, + isPaused, + interval, + }; +} + +export function triggerRefreshTimer() { + return async (dispatch, getState) => { + dispatch({ + type: TRIGGER_REFRESH_TIMER, + }); + + const dataFilters = getDataFilters(getState()); + await syncDataForAllLayers(dispatch, getState, dataFilters); + }; +} + +export function clearMissingStyleProperties(layerId) { + return async (dispatch, getState) => { + const targetLayer = getLayerById(layerId, getState()); + if (!targetLayer) { + return; + } + + const style = targetLayer.getCurrentStyle(); + if (!style) { + return; + } + + const nextFields = await targetLayer.getFields(); //take into account all fields, since labels can be driven by any field (source or join) + const { hasChanges, nextStyleDescriptor } = style.getDescriptorWithMissingStylePropsRemoved( + nextFields + ); + if (hasChanges) { + dispatch(updateLayerStyle(layerId, nextStyleDescriptor)); + } + }; +} + +export function updateLayerStyle(layerId, styleDescriptor) { + return dispatch => { + dispatch({ + type: UPDATE_LAYER_STYLE, + layerId, + style: { + ...styleDescriptor, + }, + }); + + // Ensure updateStyleMeta is triggered + // syncDataForLayer may not trigger endDataLoad if no re-fetch is required + dispatch(updateStyleMeta(layerId)); + + // Style update may require re-fetch, for example ES search may need to retrieve field used for dynamic styling + dispatch(syncDataForLayer(layerId)); + }; +} + +export function updateStyleMeta(layerId) { + return async (dispatch, getState) => { + const layer = getLayerById(layerId, getState()); + if (!layer) { + return; + } + const sourceDataRequest = layer.getSourceDataRequest(); + const style = layer.getCurrentStyle(); + if (!style || !sourceDataRequest) { + return; + } + const styleMeta = await style.pluckStyleMetaFromSourceDataRequest(sourceDataRequest); + dispatch({ + type: SET_LAYER_STYLE_META, + layerId, + styleMeta, + }); + }; +} + +export function updateLayerStyleForSelectedLayer(styleDescriptor) { + return (dispatch, getState) => { + const selectedLayerId = getSelectedLayerId(getState()); + if (!selectedLayerId) { + return; + } + dispatch(updateLayerStyle(selectedLayerId, styleDescriptor)); + }; +} + +export function setJoinsForLayer(layer, joins) { + return async dispatch => { + await dispatch({ + type: SET_JOINS, + layer: layer, + joins: joins, + }); + + await dispatch(clearMissingStyleProperties(layer.getId())); + dispatch(syncDataForLayer(layer.getId())); + }; +} + +export function updateDrawState(drawState) { + return dispatch => { + if (drawState !== null) { + dispatch({ type: SET_OPEN_TOOLTIPS, openTooltips: [] }); // tooltips just get in the way + } + dispatch({ + type: UPDATE_DRAW_STATE, + drawState: drawState, + }); + }; +} + +export function disableInteractive() { + return { type: SET_INTERACTIVE, disableInteractive: true }; +} + +export function disableTooltipControl() { + return { type: DISABLE_TOOLTIP_CONTROL, disableTooltipControl: true }; +} + +export function hideToolbarOverlay() { + return { type: HIDE_TOOLBAR_OVERLAY, hideToolbarOverlay: true }; +} + +export function hideLayerControl() { + return { type: HIDE_LAYER_CONTROL, hideLayerControl: true }; +} +export function hideViewControl() { + return { type: HIDE_VIEW_CONTROL, hideViewControl: true }; +} + +export function setHiddenLayers(hiddenLayerIds) { + return (dispatch, getState) => { + const isMapReady = getMapReady(getState()); + + if (!isMapReady) { + dispatch({ type: SET_WAITING_FOR_READY_HIDDEN_LAYERS, hiddenLayerIds }); + } else { + getLayerListRaw(getState()).forEach(layer => + dispatch(setLayerVisibility(layer.id, !hiddenLayerIds.includes(layer.id))) + ); + } + }; +} diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.test.js b/x-pack/plugins/maps/public/actions/map_actions.test.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/actions/map_actions.test.js rename to x-pack/plugins/maps/public/actions/map_actions.test.js index 7e2a3c827fa88f..c280b8af7ab80d 100644 --- a/x-pack/legacy/plugins/maps/public/actions/map_actions.test.js +++ b/x-pack/plugins/maps/public/actions/map_actions.test.js @@ -5,7 +5,7 @@ */ jest.mock('../selectors/map_selectors', () => ({})); -jest.mock('../../../../../plugins/maps/public/kibana_services', () => ({})); +jest.mock('../kibana_services', () => ({})); import { mapExtentChanged, setMouseCoordinates } from './map_actions'; diff --git a/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts b/x-pack/plugins/maps/public/actions/ui_actions.d.ts similarity index 54% rename from x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts rename to x-pack/plugins/maps/public/actions/ui_actions.d.ts index 233918847de08b..e087dc70256f06 100644 --- a/x-pack/legacy/plugins/maps/public/actions/ui_actions.d.ts +++ b/x-pack/plugins/maps/public/actions/ui_actions.d.ts @@ -6,6 +6,17 @@ import { AnyAction } from 'redux'; +export const UPDATE_FLYOUT: string; +export const CLOSE_SET_VIEW: string; +export const OPEN_SET_VIEW: string; +export const SET_IS_LAYER_TOC_OPEN: string; +export const SET_FULL_SCREEN: string; +export const SET_READ_ONLY: string; +export const SET_OPEN_TOC_DETAILS: string; +export const SHOW_TOC_DETAILS: string; +export const HIDE_TOC_DETAILS: string; +export const UPDATE_INDEXING_STAGE: string; + export function setOpenTOCDetails(layerIds?: string[]): AnyAction; export function setIsLayerTOCOpen(open: boolean): AnyAction; diff --git a/x-pack/plugins/maps/public/actions/ui_actions.js b/x-pack/plugins/maps/public/actions/ui_actions.js index 59ae56c15056a0..77fdf6b0f12d23 100644 --- a/x-pack/plugins/maps/public/actions/ui_actions.js +++ b/x-pack/plugins/maps/public/actions/ui_actions.js @@ -14,3 +14,73 @@ export const SET_OPEN_TOC_DETAILS = 'SET_OPEN_TOC_DETAILS'; export const SHOW_TOC_DETAILS = 'SHOW_TOC_DETAILS'; export const HIDE_TOC_DETAILS = 'HIDE_TOC_DETAILS'; export const UPDATE_INDEXING_STAGE = 'UPDATE_INDEXING_STAGE'; + +export function exitFullScreen() { + return { + type: SET_FULL_SCREEN, + isFullScreen: false, + }; +} + +export function updateFlyout(display) { + return { + type: UPDATE_FLYOUT, + display, + }; +} +export function closeSetView() { + return { + type: CLOSE_SET_VIEW, + }; +} +export function openSetView() { + return { + type: OPEN_SET_VIEW, + }; +} +export function setIsLayerTOCOpen(isLayerTOCOpen) { + return { + type: SET_IS_LAYER_TOC_OPEN, + isLayerTOCOpen, + }; +} +export function enableFullScreen() { + return { + type: SET_FULL_SCREEN, + isFullScreen: true, + }; +} +export function setReadOnly(isReadOnly) { + return { + type: SET_READ_ONLY, + isReadOnly, + }; +} + +export function setOpenTOCDetails(layerIds) { + return { + type: SET_OPEN_TOC_DETAILS, + layerIds, + }; +} + +export function showTOCDetails(layerId) { + return { + type: SHOW_TOC_DETAILS, + layerId, + }; +} + +export function hideTOCDetails(layerId) { + return { + type: HIDE_TOC_DETAILS, + layerId, + }; +} + +export function updateIndexingStage(stage) { + return { + type: UPDATE_INDEXING_STAGE, + stage, + }; +} diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts b/x-pack/plugins/maps/public/angular/get_initial_layers.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/angular/get_initial_layers.d.ts rename to x-pack/plugins/maps/public/angular/get_initial_layers.d.ts diff --git a/x-pack/plugins/maps/public/angular/get_initial_layers.js b/x-pack/plugins/maps/public/angular/get_initial_layers.js new file mode 100644 index 00000000000000..1eb5dac309f28c --- /dev/null +++ b/x-pack/plugins/maps/public/angular/get_initial_layers.js @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import _ from 'lodash'; +// Import each layer type, even those not used, to init in registry + +import '../layers/sources/wms_source'; + +import '../layers/sources/ems_file_source'; + +import '../layers/sources/es_search_source'; + +import '../layers/sources/es_pew_pew_source/es_pew_pew_source'; + +import '../layers/sources/kibana_regionmap_source'; + +import '../layers/sources/es_geo_grid_source'; + +import '../layers/sources/xyz_tms_source'; + +import { KibanaTilemapSource } from '../layers/sources/kibana_tilemap_source'; + +import { EMSTMSSource } from '../layers/sources/ems_tms_source'; + +import { getInjectedVarFunc } from '../kibana_services'; + +import { getKibanaTileMap } from '../meta'; + +export function getInitialLayers(layerListJSON, initialLayers = []) { + if (layerListJSON) { + return JSON.parse(layerListJSON); + } + + const tilemapSourceFromKibana = getKibanaTileMap(); + if (_.get(tilemapSourceFromKibana, 'url')) { + const sourceDescriptor = KibanaTilemapSource.createDescriptor(); + const source = new KibanaTilemapSource(sourceDescriptor); + const layer = source.createDefaultLayer(); + return [layer.toLayerDescriptor(), ...initialLayers]; + } + + const isEmsEnabled = getInjectedVarFunc()('isEmsEnabled', true); + if (isEmsEnabled) { + const descriptor = EMSTMSSource.createDescriptor({ isAutoSelect: true }); + const source = new EMSTMSSource(descriptor); + const layer = source.createDefaultLayer(); + return [layer.toLayerDescriptor(), ...initialLayers]; + } + + return initialLayers; +} diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.test.js b/x-pack/plugins/maps/public/angular/get_initial_layers.test.js similarity index 80% rename from x-pack/legacy/plugins/maps/public/angular/get_initial_layers.test.js rename to x-pack/plugins/maps/public/angular/get_initial_layers.test.js index 8c9185a16ea0e7..f41ed26b2a05d3 100644 --- a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.test.js +++ b/x-pack/plugins/maps/public/angular/get_initial_layers.test.js @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('../../../../../plugins/maps/public/meta', () => { +jest.mock('../meta', () => { return {}; }); -jest.mock('../../../../../plugins/maps/public/kibana_services'); +jest.mock('../kibana_services'); import { getInitialLayers } from './get_initial_layers'; @@ -15,8 +15,7 @@ const layerListNotProvided = undefined; describe('Saved object has layer list', () => { beforeEach(() => { - require('../../../../../plugins/maps/public/kibana_services').getInjectedVarFunc = () => - jest.fn(); + require('../kibana_services').getInjectedVarFunc = () => jest.fn(); }); it('Should get initial layers from saved object', () => { @@ -33,7 +32,7 @@ describe('Saved object has layer list', () => { describe('kibana.yml configured with map.tilemap.url', () => { beforeAll(() => { - require('../../../../../plugins/maps/public/meta').getKibanaTileMap = () => { + require('../meta').getKibanaTileMap = () => { return { url: 'myTileUrl', }; @@ -63,10 +62,10 @@ describe('kibana.yml configured with map.tilemap.url', () => { describe('EMS is enabled', () => { beforeAll(() => { - require('../../../../../plugins/maps/public/meta').getKibanaTileMap = () => { + require('../meta').getKibanaTileMap = () => { return null; }; - require('../../../../../plugins/maps/public/kibana_services').getInjectedVarFunc = () => key => { + require('../kibana_services').getInjectedVarFunc = () => key => { switch (key) { case 'emsTileLayerId': return { @@ -107,11 +106,11 @@ describe('EMS is enabled', () => { describe('EMS is not enabled', () => { beforeAll(() => { - require('../../../../../plugins/maps/public/meta').getKibanaTileMap = () => { + require('../meta').getKibanaTileMap = () => { return null; }; - require('../../../../../plugins/maps/public/kibana_services').getInjectedVarFunc = () => key => { + require('../kibana_services').getInjectedVarFunc = () => key => { switch (key) { case 'isEmsEnabled': return false; diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_query.js b/x-pack/plugins/maps/public/angular/get_initial_query.js similarity index 82% rename from x-pack/legacy/plugins/maps/public/angular/get_initial_query.js rename to x-pack/plugins/maps/public/angular/get_initial_query.js index c50ecb2b05dc07..4f61142413671d 100644 --- a/x-pack/legacy/plugins/maps/public/angular/get_initial_query.js +++ b/x-pack/plugins/maps/public/angular/get_initial_query.js @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getUiSettings } from '../../../../../plugins/maps/public/kibana_services'; +import { getUiSettings } from '../kibana_services'; export function getInitialQuery({ mapStateJSON, appState = {}, userQueryLanguage }) { const settings = getUiSettings(); diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_refresh_config.js b/x-pack/plugins/maps/public/angular/get_initial_refresh_config.js similarity index 84% rename from x-pack/legacy/plugins/maps/public/angular/get_initial_refresh_config.js rename to x-pack/plugins/maps/public/angular/get_initial_refresh_config.js index 8735d45debfc4c..f13e435cd1d5cf 100644 --- a/x-pack/legacy/plugins/maps/public/angular/get_initial_refresh_config.js +++ b/x-pack/plugins/maps/public/angular/get_initial_refresh_config.js @@ -3,8 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getUiSettings } from '../../../../../plugins/maps/public/kibana_services'; + +import { getUiSettings } from '../kibana_services'; export function getInitialRefreshConfig({ mapStateJSON, globalState = {} }) { const uiSettings = getUiSettings(); diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_time_filters.js b/x-pack/plugins/maps/public/angular/get_initial_time_filters.js similarity index 80% rename from x-pack/legacy/plugins/maps/public/angular/get_initial_time_filters.js rename to x-pack/plugins/maps/public/angular/get_initial_time_filters.js index 74fbf603e99f5e..75d9f0e95ccf08 100644 --- a/x-pack/legacy/plugins/maps/public/angular/get_initial_time_filters.js +++ b/x-pack/plugins/maps/public/angular/get_initial_time_filters.js @@ -3,8 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getUiSettings } from '../../../../../plugins/maps/public/kibana_services'; + +import { getUiSettings } from '../kibana_services'; export function getInitialTimeFilters({ mapStateJSON, globalState = {} }) { if (mapStateJSON) { diff --git a/x-pack/legacy/plugins/maps/public/angular/listing_ng_wrapper.html b/x-pack/plugins/maps/public/angular/listing_ng_wrapper.html similarity index 100% rename from x-pack/legacy/plugins/maps/public/angular/listing_ng_wrapper.html rename to x-pack/plugins/maps/public/angular/listing_ng_wrapper.html diff --git a/x-pack/legacy/plugins/maps/public/angular/map.html b/x-pack/plugins/maps/public/angular/map.html similarity index 100% rename from x-pack/legacy/plugins/maps/public/angular/map.html rename to x-pack/plugins/maps/public/angular/map.html diff --git a/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js b/x-pack/plugins/maps/public/angular/services/gis_map_saved_object_loader.js similarity index 79% rename from x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js rename to x-pack/plugins/maps/public/angular/services/gis_map_saved_object_loader.js index 710997a9c0d7f3..2dcec35960b080 100644 --- a/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js +++ b/x-pack/plugins/maps/public/angular/services/gis_map_saved_object_loader.js @@ -6,15 +6,14 @@ import _ from 'lodash'; import { createSavedGisMapClass } from './saved_gis_map'; -import { SavedObjectLoader } from '../../../../../../../src/plugins/saved_objects/public'; +import { SavedObjectLoader } from '../../../../../../src/plugins/saved_objects/public'; import { getCoreChrome, getSavedObjectsClient, getIndexPatternService, getCoreOverlays, getData, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../plugins/maps/public/kibana_services'; +} from '../../kibana_services'; export const getMapsSavedObjectLoader = _.once(function() { const services = { diff --git a/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js b/x-pack/plugins/maps/public/angular/services/saved_gis_map.js similarity index 88% rename from x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js rename to x-pack/plugins/maps/public/angular/services/saved_gis_map.js index 990a0613da681c..1c47e0ab7dc2a4 100644 --- a/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js +++ b/x-pack/plugins/maps/public/angular/services/saved_gis_map.js @@ -5,7 +5,7 @@ */ import _ from 'lodash'; -import { createSavedObjectClass } from '../../../../../../../src/plugins/saved_objects/public'; +import { createSavedObjectClass } from '../../../../../../src/plugins/saved_objects/public'; import { getTimeFilters, getMapZoom, @@ -17,10 +17,10 @@ import { getFilters, } from '../../selectors/map_selectors'; import { getIsLayerTOCOpen, getOpenTOCDetails } from '../../selectors/ui_selectors'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { convertMapExtentToPolygon } from '../../../../../../plugins/maps/public/elasticsearch_geo_utils'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { copyPersistentState } from '../../../../../../plugins/maps/public/reducers/util'; + +import { convertMapExtentToPolygon } from '../../elasticsearch_geo_utils'; + +import { copyPersistentState } from '../../reducers/util'; import { extractReferences, injectReferences } from '../../../common/migrations/references'; import { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants'; diff --git a/x-pack/legacy/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap b/x-pack/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap rename to x-pack/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/components/__snapshots__/layer_toc_actions.test.js.snap b/x-pack/plugins/maps/public/components/__snapshots__/layer_toc_actions.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/__snapshots__/layer_toc_actions.test.js.snap rename to x-pack/plugins/maps/public/components/__snapshots__/layer_toc_actions.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/components/_geometry_filter.scss b/x-pack/plugins/maps/public/components/_geometry_filter.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/_geometry_filter.scss rename to x-pack/plugins/maps/public/components/_geometry_filter.scss diff --git a/x-pack/plugins/maps/public/components/_index.scss b/x-pack/plugins/maps/public/components/_index.scss new file mode 100644 index 00000000000000..161b3fefdb8f9e --- /dev/null +++ b/x-pack/plugins/maps/public/components/_index.scss @@ -0,0 +1,3 @@ +@import 'metric_editors'; +@import './geometry_filter'; +@import 'tooltip_selector'; diff --git a/x-pack/legacy/plugins/maps/public/components/_metric_editors.scss b/x-pack/plugins/maps/public/components/_metric_editors.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/_metric_editors.scss rename to x-pack/plugins/maps/public/components/_metric_editors.scss diff --git a/x-pack/legacy/plugins/maps/public/components/_tooltip_selector.scss b/x-pack/plugins/maps/public/components/_tooltip_selector.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/_tooltip_selector.scss rename to x-pack/plugins/maps/public/components/_tooltip_selector.scss diff --git a/x-pack/legacy/plugins/maps/public/components/distance_filter_form.tsx b/x-pack/plugins/maps/public/components/distance_filter_form.tsx similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/distance_filter_form.tsx rename to x-pack/plugins/maps/public/components/distance_filter_form.tsx diff --git a/x-pack/legacy/plugins/maps/public/components/geo_field_with_index.ts b/x-pack/plugins/maps/public/components/geo_field_with_index.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/geo_field_with_index.ts rename to x-pack/plugins/maps/public/components/geo_field_with_index.ts diff --git a/x-pack/legacy/plugins/maps/public/components/geometry_filter_form.js b/x-pack/plugins/maps/public/components/geometry_filter_form.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/geometry_filter_form.js rename to x-pack/plugins/maps/public/components/geometry_filter_form.js diff --git a/x-pack/legacy/plugins/maps/public/components/geometry_filter_form.test.js b/x-pack/plugins/maps/public/components/geometry_filter_form.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/geometry_filter_form.test.js rename to x-pack/plugins/maps/public/components/geometry_filter_form.test.js diff --git a/x-pack/legacy/plugins/maps/public/components/global_filter_checkbox.js b/x-pack/plugins/maps/public/components/global_filter_checkbox.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/global_filter_checkbox.js rename to x-pack/plugins/maps/public/components/global_filter_checkbox.js diff --git a/x-pack/legacy/plugins/maps/public/components/layer_toc_actions.js b/x-pack/plugins/maps/public/components/layer_toc_actions.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/layer_toc_actions.js rename to x-pack/plugins/maps/public/components/layer_toc_actions.js diff --git a/x-pack/legacy/plugins/maps/public/components/layer_toc_actions.test.js b/x-pack/plugins/maps/public/components/layer_toc_actions.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/layer_toc_actions.test.js rename to x-pack/plugins/maps/public/components/layer_toc_actions.test.js diff --git a/x-pack/legacy/plugins/maps/public/components/map_listing.js b/x-pack/plugins/maps/public/components/map_listing.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/components/map_listing.js rename to x-pack/plugins/maps/public/components/map_listing.js index ef1d524cb91dd2..ee10fe30130f37 100644 --- a/x-pack/legacy/plugins/maps/public/components/map_listing.js +++ b/x-pack/plugins/maps/public/components/map_listing.js @@ -7,8 +7,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import _ from 'lodash'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getToasts } from '../../../../../plugins/maps/public/kibana_services'; + +import { getToasts } from '../kibana_services'; import { EuiTitle, EuiFieldSearch, diff --git a/x-pack/legacy/plugins/maps/public/components/multi_index_geo_field_select.tsx b/x-pack/plugins/maps/public/components/multi_index_geo_field_select.tsx similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/multi_index_geo_field_select.tsx rename to x-pack/plugins/maps/public/components/multi_index_geo_field_select.tsx diff --git a/x-pack/plugins/maps/public/connected_components/_index.scss b/x-pack/plugins/maps/public/connected_components/_index.scss new file mode 100644 index 00000000000000..83042ae1d586cc --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/_index.scss @@ -0,0 +1,6 @@ +@import 'gis_map/gis_map'; +@import 'layer_addpanel/source_select/index'; +@import 'layer_panel/index'; +@import 'widget_overlay/index'; +@import 'toolbar_overlay/index'; +@import 'map/features_tooltip/index'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/_gis_map.scss b/x-pack/plugins/maps/public/connected_components/gis_map/_gis_map.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/gis_map/_gis_map.scss rename to x-pack/plugins/maps/public/connected_components/gis_map/_gis_map.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts b/x-pack/plugins/maps/public/connected_components/gis_map/index.d.ts similarity index 73% rename from x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts rename to x-pack/plugins/maps/public/connected_components/gis_map/index.d.ts index 8689d882971719..92d92dfbd142da 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts +++ b/x-pack/plugins/maps/public/connected_components/gis_map/index.d.ts @@ -6,8 +6,8 @@ import React from 'react'; import { Filter } from 'src/plugins/data/public'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { RenderToolTipContent } from '../../../../../../plugins/maps/public/layers/tooltips/tooltip_property'; + +import { RenderToolTipContent } from '../../layers/tooltips/tooltip_property'; export const GisMap: React.ComponentType<{ addFilters: ((filters: Filter[]) => void) | null; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.js b/x-pack/plugins/maps/public/connected_components/gis_map/index.js similarity index 85% rename from x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.js rename to x-pack/plugins/maps/public/connected_components/gis_map/index.js index 2d8265bae93878..c825fdab75ca74 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.js +++ b/x-pack/plugins/maps/public/connected_components/gis_map/index.js @@ -6,8 +6,8 @@ import { connect } from 'react-redux'; import { GisMap } from './view'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FLYOUT_STATE } from '../../../../../../plugins/maps/public/reducers/ui'; + +import { FLYOUT_STATE } from '../../reducers/ui'; import { exitFullScreen } from '../../actions/ui_actions'; import { getFlyoutDisplay, getIsFullScreen } from '../../selectors/ui_selectors'; import { triggerRefreshTimer, cancelAllInFlightRequests } from '../../actions/map_actions'; @@ -18,8 +18,8 @@ import { getQueryableUniqueIndexPatternIds, isToolbarOverlayHidden, } from '../../selectors/map_selectors'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getCoreChrome } from '../../../../../../plugins/maps/public/kibana_services'; + +import { getCoreChrome } from '../../kibana_services'; function mapStateToProps(state = {}) { const flyoutDisplay = getFlyoutDisplay(state); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/view.js b/x-pack/plugins/maps/public/connected_components/gis_map/view.js similarity index 92% rename from x-pack/legacy/plugins/maps/public/connected_components/gis_map/view.js rename to x-pack/plugins/maps/public/connected_components/gis_map/view.js index 06097ebea1900a..28ad12133d6118 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/view.js +++ b/x-pack/plugins/maps/public/connected_components/gis_map/view.js @@ -7,17 +7,16 @@ import _ from 'lodash'; import React, { Component } from 'react'; import { MBMapContainer } from '../map/mb'; -import { WidgetOverlay } from '../widget_overlay/index'; -import { ToolbarOverlay } from '../toolbar_overlay/index'; -import { LayerPanel } from '../layer_panel/index'; -import { AddLayerPanel } from '../layer_addpanel/index'; +import { WidgetOverlay } from '../widget_overlay'; +import { ToolbarOverlay } from '../toolbar_overlay'; +import { LayerPanel } from '../layer_panel'; +import { AddLayerPanel } from '../layer_addpanel'; import { EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui'; -import { ExitFullScreenButton } from '../../../../../../../src/plugins/kibana_react/public'; +import { ExitFullScreenButton } from '../../../../../../src/plugins/kibana_react/public'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getIndexPatternsFromIds } from '../../../../../../plugins/maps/public/index_pattern_util'; +import { getIndexPatternsFromIds } from '../../index_pattern_util'; import { ES_GEO_FIELD_TYPE } from '../../../common/constants'; -import { indexPatterns as indexPatternsUtils } from '../../../../../../../src/plugins/data/public'; +import { indexPatterns as indexPatternsUtils } from '../../../../../../src/plugins/data/public'; import { i18n } from '@kbn/i18n'; import uuid from 'uuid/v4'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/flyout_footer/index.js b/x-pack/plugins/maps/public/connected_components/layer_addpanel/flyout_footer/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/flyout_footer/index.js rename to x-pack/plugins/maps/public/connected_components/layer_addpanel/flyout_footer/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/flyout_footer/view.js b/x-pack/plugins/maps/public/connected_components/layer_addpanel/flyout_footer/view.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/flyout_footer/view.js rename to x-pack/plugins/maps/public/connected_components/layer_addpanel/flyout_footer/view.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/index.js b/x-pack/plugins/maps/public/connected_components/layer_addpanel/import_editor/index.js similarity index 76% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/index.js rename to x-pack/plugins/maps/public/connected_components/layer_addpanel/import_editor/index.js index e8192795f98aef..bff235a7d27fcd 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/index.js +++ b/x-pack/plugins/maps/public/connected_components/layer_addpanel/import_editor/index.js @@ -6,10 +6,10 @@ import { connect } from 'react-redux'; import { ImportEditor } from './view'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getInspectorAdapters } from '../../../../../../../plugins/maps/public/reducers/non_serializable_instances'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { INDEXING_STAGE } from '../../../../../../../plugins/maps/public/reducers/ui'; + +import { getInspectorAdapters } from '../../../reducers/non_serializable_instances'; + +import { INDEXING_STAGE } from '../../../reducers/ui'; import { updateIndexingStage } from '../../../actions/ui_actions'; import { getIndexingStage } from '../../../selectors/ui_selectors'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js b/x-pack/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js similarity index 89% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js rename to x-pack/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js index cb20d80733c33b..a4fa0d492bf3f5 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js +++ b/x-pack/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js @@ -7,8 +7,8 @@ import React, { Fragment } from 'react'; import { EuiSpacer, EuiPanel, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { uploadLayerWizardConfig } from '../../../../../../../plugins/maps/public/layers/sources/client_file_source'; + +import { uploadLayerWizardConfig } from '../../../layers/sources/client_file_source'; export const ImportEditor = ({ clearSource, isIndexingTriggered, ...props }) => { const editorProperties = getEditorProperties({ isIndexingTriggered, ...props }); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/index.js b/x-pack/plugins/maps/public/connected_components/layer_addpanel/index.js similarity index 85% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/index.js rename to x-pack/plugins/maps/public/connected_components/layer_addpanel/index.js index c4e2fa5169b0fa..24c1f5ced4fe65 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/index.js +++ b/x-pack/plugins/maps/public/connected_components/layer_addpanel/index.js @@ -6,13 +6,13 @@ import { connect } from 'react-redux'; import { AddLayerPanel } from './view'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FLYOUT_STATE, INDEXING_STAGE } from '../../../../../../plugins/maps/public/reducers/ui'; + +import { FLYOUT_STATE, INDEXING_STAGE } from '../../reducers/ui'; import { updateFlyout, updateIndexingStage } from '../../actions/ui_actions'; import { getFlyoutDisplay, getIndexingStage } from '../../selectors/ui_selectors'; import { getMapColors } from '../../selectors/map_selectors'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getInspectorAdapters } from '../../../../../../plugins/maps/public/reducers/non_serializable_instances'; + +import { getInspectorAdapters } from '../../reducers/non_serializable_instances'; import { setTransientLayer, addLayer, diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_editor/index.js b/x-pack/plugins/maps/public/connected_components/layer_addpanel/source_editor/index.js similarity index 75% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_editor/index.js rename to x-pack/plugins/maps/public/connected_components/layer_addpanel/source_editor/index.js index 553e54ee897665..8937f32d3bf05d 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_editor/index.js +++ b/x-pack/plugins/maps/public/connected_components/layer_addpanel/source_editor/index.js @@ -6,8 +6,8 @@ import { connect } from 'react-redux'; import { SourceEditor } from './view'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getInspectorAdapters } from '../../../../../../../plugins/maps/public/reducers/non_serializable_instances'; + +import { getInspectorAdapters } from '../../../reducers/non_serializable_instances'; function mapStateToProps(state = {}) { return { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_editor/view.js b/x-pack/plugins/maps/public/connected_components/layer_addpanel/source_editor/view.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_editor/view.js rename to x-pack/plugins/maps/public/connected_components/layer_addpanel/source_editor/view.js diff --git a/x-pack/plugins/maps/public/connected_components/layer_addpanel/source_select/_index.scss b/x-pack/plugins/maps/public/connected_components/layer_addpanel/source_select/_index.scss new file mode 100644 index 00000000000000..8ae6970315e132 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/layer_addpanel/source_select/_index.scss @@ -0,0 +1 @@ +@import 'source_select'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_source_select.scss b/x-pack/plugins/maps/public/connected_components/layer_addpanel/source_select/_source_select.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_source_select.scss rename to x-pack/plugins/maps/public/connected_components/layer_addpanel/source_select/_source_select.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js b/x-pack/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js similarity index 90% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js rename to x-pack/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js index 67cc17ebaa2241..80b05a0fd015b7 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js +++ b/x-pack/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js @@ -5,8 +5,8 @@ */ import React, { Fragment } from 'react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getLayerWizards } from '../../../../../../../plugins/maps/public/layers/layer_wizard_registry'; + +import { getLayerWizards } from '../../../layers/layer_wizard_registry'; import { EuiTitle, EuiSpacer, EuiCard, EuiIcon } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import _ from 'lodash'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/view.js b/x-pack/plugins/maps/public/connected_components/layer_addpanel/view.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/view.js rename to x-pack/plugins/maps/public/connected_components/layer_addpanel/view.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap b/x-pack/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap rename to x-pack/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap diff --git a/x-pack/plugins/maps/public/connected_components/layer_panel/_index.scss b/x-pack/plugins/maps/public/connected_components/layer_panel/_index.scss new file mode 100644 index 00000000000000..41b4826a02c675 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/_index.scss @@ -0,0 +1,4 @@ +@import 'layer_panel'; +@import 'filter_editor/filter_editor'; +@import 'join_editor/resources/join'; +@import 'style_settings/style_settings'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/_layer_panel.scss b/x-pack/plugins/maps/public/connected_components/layer_panel/_layer_panel.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/_layer_panel.scss rename to x-pack/plugins/maps/public/connected_components/layer_panel/_layer_panel.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/_filter_editor.scss b/x-pack/plugins/maps/public/connected_components/layer_panel/filter_editor/_filter_editor.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/_filter_editor.scss rename to x-pack/plugins/maps/public/connected_components/layer_panel/filter_editor/_filter_editor.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js b/x-pack/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js similarity index 96% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js index 40fdac38493d4b..fba2ec05d0b1d6 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js @@ -20,12 +20,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { - getIndexPatternService, - getUiSettings, - getData, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../../plugins/maps/public/kibana_services'; +import { getIndexPatternService, getUiSettings, getData } from '../../../kibana_services'; import { GlobalFilterCheckbox } from '../../../components/global_filter_checkbox'; export class FilterEditor extends Component { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/index.js b/x-pack/plugins/maps/public/connected_components/layer_panel/filter_editor/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/filter_editor/index.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/filter_editor/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/flyout_footer/index.js b/x-pack/plugins/maps/public/connected_components/layer_panel/flyout_footer/index.js similarity index 89% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/flyout_footer/index.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/flyout_footer/index.js index 287f0019f18ecd..621ce209eb982f 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/flyout_footer/index.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/flyout_footer/index.js @@ -6,8 +6,8 @@ import { connect } from 'react-redux'; import { FlyoutFooter } from './view'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FLYOUT_STATE } from '../../../../../../../plugins/maps/public/reducers/ui'; + +import { FLYOUT_STATE } from '../../../reducers/ui'; import { updateFlyout } from '../../../actions/ui_actions'; import { hasDirtyState } from '../../../selectors/map_selectors'; import { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/flyout_footer/view.js b/x-pack/plugins/maps/public/connected_components/layer_panel/flyout_footer/view.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/flyout_footer/view.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/flyout_footer/view.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/index.js b/x-pack/plugins/maps/public/connected_components/layer_panel/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/index.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/index.js b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/index.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/__snapshots__/metrics_expression.test.js.snap b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/__snapshots__/metrics_expression.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/__snapshots__/metrics_expression.test.js.snap rename to x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/__snapshots__/metrics_expression.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/_join.scss b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/_join.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/_join.scss rename to x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/_join.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join.js b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join.js similarity index 95% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join.js index 9c4e1cfdb5467c..0d26354e2449b2 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join.js @@ -13,9 +13,9 @@ import { MetricsExpression } from './metrics_expression'; import { WhereExpression } from './where_expression'; import { GlobalFilterCheckbox } from '../../../../components/global_filter_checkbox'; -import { indexPatterns } from '../../../../../../../../../src/plugins/data/public'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getIndexPatternService } from '../../../../../../../../plugins/maps/public/kibana_services'; +import { indexPatterns } from '../../../../../../../../src/plugins/data/public'; + +import { getIndexPatternService } from '../../../../kibana_services'; export class Join extends Component { state = { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join_expression.js b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join_expression.js similarity index 93% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join_expression.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join_expression.js index 73600c81d221e2..12ca2f3c514a06 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join_expression.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join_expression.js @@ -16,16 +16,15 @@ import { EuiFormHelpText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { SingleFieldSelect } from '../../../../../../../../plugins/maps/public/components/single_field_select'; + +import { SingleFieldSelect } from '../../../../components/single_field_select'; import { FormattedMessage } from '@kbn/i18n/react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getTermsFields } from '../../../../../../../../plugins/maps/public/index_pattern_util'; + +import { getTermsFields } from '../../../../index_pattern_util'; import { getIndexPatternService, getIndexPatternSelectComponent, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../../../plugins/maps/public/kibana_services'; +} from '../../../../kibana_services'; export class JoinExpression extends Component { state = { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.js b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.js similarity index 95% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.js index c6a79a398f9af2..8c83743ac4c962 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.js @@ -14,8 +14,8 @@ import { EuiFormErrorText, EuiFormHelpText, } from '@elastic/eui'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MetricsEditor } from '../../../../../../../../plugins/maps/public/components/metrics_editor'; + +import { MetricsEditor } from '../../../../components/metrics_editor'; import { FormattedMessage } from '@kbn/i18n/react'; import { AGG_TYPE } from '../../../../../common/constants'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.test.js b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.test.js similarity index 92% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.test.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.test.js index d8bf8622494484..3cd8a3c42879a3 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.test.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('../../../../../../../../plugins/maps/public/components/metric_editor', () => ({ +jest.mock('../../../../components/metric_editor', () => ({ MetricsEditor: () => { return
mockMetricsEditor
; }, diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/where_expression.js b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/where_expression.js similarity index 94% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/where_expression.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/where_expression.js index 54ec0ac46fa3d9..7c9b4f7b7b9a4d 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/where_expression.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/resources/where_expression.js @@ -8,11 +8,7 @@ import React, { Component } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton, EuiPopover, EuiExpression, EuiFormHelpText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { - getUiSettings, - getData, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../../../plugins/maps/public/kibana_services'; +import { getUiSettings, getData } from '../../../../kibana_services'; export class WhereExpression extends Component { state = { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/view.js b/x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/view.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/view.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/join_editor/view.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_errors/__snapshots__/layer_errors.test.js.snap b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_errors/__snapshots__/layer_errors.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_errors/__snapshots__/layer_errors.test.js.snap rename to x-pack/plugins/maps/public/connected_components/layer_panel/layer_errors/__snapshots__/layer_errors.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_errors/index.js b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_errors/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_errors/index.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/layer_errors/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_errors/layer_errors.js b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_errors/layer_errors.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_errors/layer_errors.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/layer_errors/layer_errors.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_errors/layer_errors.test.js b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_errors/layer_errors.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_errors/layer_errors.test.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/layer_errors/layer_errors.test.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js similarity index 94% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js index e8f980bbbf2b40..e2f22c584d3b31 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js @@ -13,7 +13,7 @@ import { updateLayerMinZoom, updateLayerAlpha, } from '../../../actions/map_actions'; -import { MAX_ZOOM } from '../../../../../../../plugins/maps/common/constants'; +import { MAX_ZOOM } from '../../../../common/constants'; function mapStateToProps(state = {}) { const selectedLayer = getSelectedLayer(state); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js similarity index 92% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js index 1d352913e54a36..168c735ab7a6c1 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js @@ -8,11 +8,10 @@ import React, { Fragment } from 'react'; import { EuiTitle, EuiPanel, EuiFormRow, EuiFieldText, EuiSpacer } from '@elastic/eui'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ValidatedRange } from '../../../../../../../plugins/maps/public/components/validated_range'; +import { ValidatedRange } from '../../../components/validated_range'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ValidatedDualRange } from '../../../../../../../../src/plugins/kibana_react/public'; +import { ValidatedDualRange } from '../../../../../../../src/plugins/kibana_react/public'; export function LayerSettings(props) { const onLabelChange = event => { const label = event.target.value; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/style_settings/_style_settings.scss b/x-pack/plugins/maps/public/connected_components/layer_panel/style_settings/_style_settings.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/style_settings/_style_settings.scss rename to x-pack/plugins/maps/public/connected_components/layer_panel/style_settings/_style_settings.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/style_settings/index.js b/x-pack/plugins/maps/public/connected_components/layer_panel/style_settings/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/style_settings/index.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/style_settings/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/style_settings/style_settings.js b/x-pack/plugins/maps/public/connected_components/layer_panel/style_settings/style_settings.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/style_settings/style_settings.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/style_settings/style_settings.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js b/x-pack/plugins/maps/public/connected_components/layer_panel/view.js similarity index 95% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/view.js index 2521318f0b3c9c..f8b7c417e67fd2 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.js +++ b/x-pack/plugins/maps/public/connected_components/layer_panel/view.js @@ -28,10 +28,10 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; -import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getData, getCore } from '../../../../../../plugins/maps/public/kibana_services'; +import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; +import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; + +import { getData, getCore } from '../../kibana_services'; const localStorage = new Storage(window.localStorage); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.test.js b/x-pack/plugins/maps/public/connected_components/layer_panel/view.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.test.js rename to x-pack/plugins/maps/public/connected_components/layer_panel/view.test.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap rename to x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/tooltip_header.test.js.snap b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/tooltip_header.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/tooltip_header.test.js.snap rename to x-pack/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/tooltip_header.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/_index.scss b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/_index.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/_index.scss rename to x-pack/plugins/maps/public/connected_components/map/features_tooltip/_index.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js similarity index 92% rename from x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js rename to x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js index 15824b82965e83..b103fb43af97c7 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js +++ b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js @@ -8,12 +8,12 @@ import React, { Component, Fragment } from 'react'; import { EuiIcon } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { createSpatialFilterWithGeometry } from '../../../../../../../plugins/maps/public/elasticsearch_geo_utils'; + +import { createSpatialFilterWithGeometry } from '../../../elasticsearch_geo_utils'; import { GEO_JSON_TYPE } from '../../../../common/constants'; import { GeometryFilterForm } from '../../../components/geometry_filter_form'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { UrlOverflowService } from '../../../../../../../../src/plugins/kibana_legacy/public'; + +import { UrlOverflowService } from '../../../../../../../src/plugins/kibana_legacy/public'; import rison from 'rison-node'; // over estimated and imprecise value to ensure filter has additional room for any meta keys added when filter is mapped. diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js rename to x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.test.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.test.js rename to x-pack/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.test.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js rename to x-pack/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/tooltip_header.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/tooltip_header.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/tooltip_header.js rename to x-pack/plugins/maps/public/connected_components/map/features_tooltip/tooltip_header.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/tooltip_header.test.js b/x-pack/plugins/maps/public/connected_components/map/features_tooltip/tooltip_header.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/tooltip_header.test.js rename to x-pack/plugins/maps/public/connected_components/map/features_tooltip/tooltip_header.test.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_circle.ts b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_circle.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_circle.ts rename to x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_circle.ts diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js similarity index 96% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js rename to x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js index cc0e665525036f..d20faa39d6492a 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js @@ -15,8 +15,7 @@ import { createSpatialFilterWithGeometry, getBoundingBoxGeometry, roundCoordinates, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../../../plugins/maps/public/elasticsearch_geo_utils'; +} from '../../../../elasticsearch_geo_utils'; import { DrawTooltip } from './draw_tooltip'; const mbDrawModes = MapboxDraw.modes; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_tooltip.js b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_tooltip.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_tooltip.js rename to x-pack/plugins/maps/public/connected_components/map/mb/draw_control/draw_tooltip.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/index.js b/x-pack/plugins/maps/public/connected_components/map/mb/draw_control/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/index.js rename to x-pack/plugins/maps/public/connected_components/map/mb/draw_control/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/index.js b/x-pack/plugins/maps/public/connected_components/map/mb/index.js similarity index 91% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/index.js rename to x-pack/plugins/maps/public/connected_components/map/mb/index.js index 350cb7028abee2..d864b60eb433bf 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/index.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/index.js @@ -24,8 +24,8 @@ import { isTooltipControlDisabled, isViewControlHidden, } from '../../../selectors/map_selectors'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getInspectorAdapters } from '../../../../../../../plugins/maps/public/reducers/non_serializable_instances'; + +import { getInspectorAdapters } from '../../../reducers/non_serializable_instances'; function mapStateToProps(state = {}) { return { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/mb.utils.test.js b/x-pack/plugins/maps/public/connected_components/map/mb/mb.utils.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/mb.utils.test.js rename to x-pack/plugins/maps/public/connected_components/map/mb/mb.utils.test.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tooltip_control.test.js.snap b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tooltip_control.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tooltip_control.test.js.snap rename to x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tooltip_control.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tooltip_popover.test.js.snap b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tooltip_popover.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tooltip_popover.test.js.snap rename to x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tooltip_popover.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/index.js b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/index.js rename to x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js rename to x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.test.js b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.test.js rename to x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.test.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js rename to x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.test.js b/x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.test.js rename to x-pack/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.test.js diff --git a/x-pack/plugins/maps/public/connected_components/map/mb/utils.js b/x-pack/plugins/maps/public/connected_components/map/mb/utils.js index 15aacfbf1f38db..7be2cd9e670843 100644 --- a/x-pack/plugins/maps/public/connected_components/map/mb/utils.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/utils.js @@ -4,8 +4,102 @@ * you may not use this file except in compliance with the Elastic License. */ +import _ from 'lodash'; import { RGBAImage } from './image_utils'; +export function removeOrphanedSourcesAndLayers(mbMap, layerList) { + const mbStyle = mbMap.getStyle(); + + const mbLayerIdsToRemove = []; + mbStyle.layers.forEach(mbLayer => { + const layer = layerList.find(layer => { + return layer.ownsMbLayerId(mbLayer.id); + }); + if (!layer) { + mbLayerIdsToRemove.push(mbLayer.id); + } + }); + mbLayerIdsToRemove.forEach(mbLayerId => mbMap.removeLayer(mbLayerId)); + + const mbSourcesToRemove = []; + for (const mbSourceId in mbStyle.sources) { + if (mbStyle.sources.hasOwnProperty(mbSourceId)) { + const layer = layerList.find(layer => { + return layer.ownsMbSourceId(mbSourceId); + }); + if (!layer) { + mbSourcesToRemove.push(mbSourceId); + } + } + } + mbSourcesToRemove.forEach(mbSourceId => mbMap.removeSource(mbSourceId)); +} + +/** + * This is function assumes only a single layer moved in the layerList, compared to mbMap + * It is optimized to minimize the amount of mbMap.moveLayer calls. + * @param mbMap + * @param layerList + */ +export function syncLayerOrderForSingleLayer(mbMap, layerList) { + if (!layerList || layerList.length === 0) { + return; + } + + const mbLayers = mbMap.getStyle().layers.slice(); + const layerIds = mbLayers.map(mbLayer => { + const layer = layerList.find(layer => layer.ownsMbLayerId(mbLayer.id)); + return layer.getId(); + }); + + const currentLayerOrderLayerIds = _.uniq(layerIds); + + const newLayerOrderLayerIdsUnfiltered = layerList.map(l => l.getId()); + const newLayerOrderLayerIds = newLayerOrderLayerIdsUnfiltered.filter(layerId => + currentLayerOrderLayerIds.includes(layerId) + ); + + let netPos = 0; + let netNeg = 0; + const movementArr = currentLayerOrderLayerIds.reduce((accu, id, idx) => { + const movement = newLayerOrderLayerIds.findIndex(newOId => newOId === id) - idx; + movement > 0 ? netPos++ : movement < 0 && netNeg++; + accu.push({ id, movement }); + return accu; + }, []); + if (netPos === 0 && netNeg === 0) { + return; + } + const movedLayerId = + (netPos >= netNeg && movementArr.find(l => l.movement < 0).id) || + (netPos < netNeg && movementArr.find(l => l.movement > 0).id); + const nextLayerIdx = newLayerOrderLayerIds.findIndex(layerId => layerId === movedLayerId) + 1; + + let nextMbLayerId; + if (nextLayerIdx === newLayerOrderLayerIds.length) { + nextMbLayerId = null; + } else { + const foundLayer = mbLayers.find(({ id: mbLayerId }) => { + const layerId = newLayerOrderLayerIds[nextLayerIdx]; + const layer = layerList.find(layer => layer.getId() === layerId); + return layer.ownsMbLayerId(mbLayerId); + }); + nextMbLayerId = foundLayer.id; + } + + const movedLayer = layerList.find(layer => layer.getId() === movedLayerId); + mbLayers.forEach(({ id: mbLayerId }) => { + if (movedLayer.ownsMbLayerId(mbLayerId)) { + mbMap.moveLayer(mbLayerId, nextMbLayerId); + } + }); +} + +export async function addSpritesheetToMap(json, imgUrl, mbMap) { + const imgData = await loadSpriteSheetImageData(imgUrl); + addSpriteSheetToMapFromImageData(json, imgData, mbMap); +} + function getImageData(img) { const canvas = window.document.createElement('canvas'); const context = canvas.getContext('2d'); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/plugins/maps/public/connected_components/map/mb/view.js similarity index 94% rename from x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js rename to x-pack/plugins/maps/public/connected_components/map/mb/view.js index a36e1d7048e92f..2d95de184f0f4e 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js +++ b/x-pack/plugins/maps/public/connected_components/map/mb/view.js @@ -6,14 +6,14 @@ import _ from 'lodash'; import React from 'react'; -import { ResizeChecker } from '../../../../../../../../src/plugins/kibana_utils/public'; +import { ResizeChecker } from '../../../../../../../src/plugins/kibana_utils/public'; import { syncLayerOrderForSingleLayer, removeOrphanedSourcesAndLayers, addSpritesheetToMap, } from './utils'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getGlyphUrl, isRetina } from '../../../../../../../plugins/maps/public/meta'; + +import { getGlyphUrl, isRetina } from '../../../meta'; import { DECIMAL_DEGREES_PRECISION, MAX_ZOOM, @@ -28,13 +28,9 @@ import sprites1 from '@elastic/maki/dist/sprite@1.png'; import sprites2 from '@elastic/maki/dist/sprite@2.png'; import { DrawControl } from './draw_control'; import { TooltipControl } from './tooltip_control'; -import { - clampToLatBounds, - clampToLonBounds, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../../plugins/maps/public/elasticsearch_geo_utils'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getInjectedVarFunc } from '../../../../../../../plugins/maps/public/kibana_services'; +import { clampToLatBounds, clampToLonBounds } from '../../../elasticsearch_geo_utils'; + +import { getInjectedVarFunc } from '../../../kibana_services'; mapboxgl.workerUrl = mbWorkerUrl; mapboxgl.setRTLTextPlugin(mbRtlPlugin); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/_index.scss b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/_index.scss similarity index 93% rename from x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/_index.scss rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/_index.scss index 01aea403b27f09..2754a3e204263f 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/_index.scss +++ b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/_index.scss @@ -1,4 +1,4 @@ -@import './tools_control/index'; +@import 'tools_control/index'; .mapToolbarOverlay { position: absolute; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/index.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/index.js rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/index.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/index.js rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.js rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.js.snap b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.js.snap rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/_index.scss b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/_index.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/_index.scss rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/_index.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/index.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/index.js rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.test.js b/x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.test.js rename to x-pack/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.test.js diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/_index.scss b/x-pack/plugins/maps/public/connected_components/widget_overlay/_index.scss new file mode 100644 index 00000000000000..5e5086bed2763f --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/_index.scss @@ -0,0 +1,6 @@ +@import 'mixins'; + +@import 'widget_overlay'; +@import 'attribution_control/attribution_control'; +@import 'layer_control/index'; +@import 'view_control/view_control'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/_mixins.scss b/x-pack/plugins/maps/public/connected_components/widget_overlay/_mixins.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/_mixins.scss rename to x-pack/plugins/maps/public/connected_components/widget_overlay/_mixins.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/_widget_overlay.scss b/x-pack/plugins/maps/public/connected_components/widget_overlay/_widget_overlay.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/_widget_overlay.scss rename to x-pack/plugins/maps/public/connected_components/widget_overlay/_widget_overlay.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/__snapshots__/view.test.js.snap b/x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/__snapshots__/view.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/__snapshots__/view.test.js.snap rename to x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/__snapshots__/view.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/_attribution_control.scss b/x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/_attribution_control.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/_attribution_control.scss rename to x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/_attribution_control.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.test.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.test.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.test.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/index.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/index.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/__snapshots__/view.test.js.snap b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/__snapshots__/view.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/__snapshots__/view.test.js.snap rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/__snapshots__/view.test.js.snap diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss new file mode 100644 index 00000000000000..9a3e3a45d6c4e5 --- /dev/null +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss @@ -0,0 +1,2 @@ +@import 'layer_control'; +@import 'layer_toc/toc_entry/toc_entry'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/_layer_control.scss b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/_layer_control.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/_layer_control.scss rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/_layer_control.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/index.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/index.js similarity index 90% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/index.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/index.js index 04de5f71f5bfc4..8780bac59e4b72 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/index.js +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/index.js @@ -6,8 +6,8 @@ import { connect } from 'react-redux'; import { LayerControl } from './view'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FLYOUT_STATE } from '../../../../../../../plugins/maps/public/reducers/ui'; + +import { FLYOUT_STATE } from '../../../reducers/ui'; import { updateFlyout, setIsLayerTOCOpen } from '../../../actions/ui_actions'; import { setSelectedLayer } from '../../../actions/map_actions'; import { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/__snapshots__/view.test.js.snap b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/__snapshots__/view.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/__snapshots__/view.test.js.snap rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/__snapshots__/view.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/index.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/index.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/__snapshots__/view.test.js.snap b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/__snapshots__/view.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/__snapshots__/view.test.js.snap rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/__snapshots__/view.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/_toc_entry.scss b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/_toc_entry.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/_toc_entry.scss rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/_toc_entry.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.js similarity index 93% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.js index 588445d0b49928..ca3c6d325687dd 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.js +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.js @@ -7,8 +7,8 @@ import _ from 'lodash'; import { connect } from 'react-redux'; import { TOCEntry } from './view'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FLYOUT_STATE } from '../../../../../../../../../plugins/maps/public/reducers/ui'; + +import { FLYOUT_STATE } from '../../../../../reducers/ui'; import { updateFlyout, hideTOCDetails, showTOCDetails } from '../../../../../actions/ui_actions'; import { getIsReadOnly, getOpenTOCDetails } from '../../../../../selectors/ui_selectors'; import { diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.test.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.test.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.test.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/view.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/view.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/view.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/view.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/view.test.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/view.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/view.test.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/view.test.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/view.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/view.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/view.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/view.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/view.test.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/view.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/view.test.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/view.test.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/view_control/_view_control.scss b/x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/_view_control.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/view_control/_view_control.scss rename to x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/_view_control.scss diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/view_control/index.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/view_control/index.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/index.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/view_control/view_control.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/view_control.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/view_control/view_control.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/view_control/view_control.js diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/widget_overlay.js b/x-pack/plugins/maps/public/connected_components/widget_overlay/widget_overlay.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/widget_overlay.js rename to x-pack/plugins/maps/public/connected_components/widget_overlay/widget_overlay.js diff --git a/x-pack/plugins/maps/public/elasticsearch_geo_utils.js b/x-pack/plugins/maps/public/elasticsearch_geo_utils.js index 617cf537fd5c31..417c5d84f8916e 100644 --- a/x-pack/plugins/maps/public/elasticsearch_geo_utils.js +++ b/x-pack/plugins/maps/public/elasticsearch_geo_utils.js @@ -64,7 +64,7 @@ function ensureGeometryType(type, expectedTypes) { * @param {string} geoFieldType Geometry field type ["geo_point", "geo_shape"] * @returns {number} */ -export function hitsToGeoJson(hits, flattenHit, geoFieldName, geoFieldType) { +export function hitsToGeoJson(hits, flattenHit, geoFieldName, geoFieldType, epochMillisFields) { const features = []; const tmpGeometriesAccumulator = []; @@ -80,6 +80,16 @@ export function hitsToGeoJson(hits, flattenHit, geoFieldName, geoFieldType) { geoShapeToGeometry(properties[geoFieldName], tmpGeometriesAccumulator); } + // There is a bug in Elasticsearch API where epoch_millis are returned as a string instead of a number + // https://github.com/elastic/elasticsearch/issues/50622 + // Convert these field values to integers. + for (let i = 0; i < epochMillisFields.length; i++) { + const fieldName = epochMillisFields[i]; + if (typeof properties[fieldName] === 'string') { + properties[fieldName] = parseInt(properties[fieldName]); + } + } + // don't include geometry field value in properties delete properties[geoFieldName]; diff --git a/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js b/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js index 5db7556be4639d..fc02e191738432 100644 --- a/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js +++ b/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js @@ -66,7 +66,7 @@ describe('hitsToGeoJson', () => { }, }, ]; - const geojson = hitsToGeoJson(hits, flattenHitMock, geoFieldName, 'geo_point'); + const geojson = hitsToGeoJson(hits, flattenHitMock, geoFieldName, 'geo_point', []); expect(geojson.type).toBe('FeatureCollection'); expect(geojson.features.length).toBe(2); expect(geojson.features[0]).toEqual({ @@ -94,7 +94,7 @@ describe('hitsToGeoJson', () => { _source: {}, }, ]; - const geojson = hitsToGeoJson(hits, flattenHitMock, geoFieldName, 'geo_point'); + const geojson = hitsToGeoJson(hits, flattenHitMock, geoFieldName, 'geo_point', []); expect(geojson.type).toBe('FeatureCollection'); expect(geojson.features.length).toBe(1); }); @@ -111,7 +111,7 @@ describe('hitsToGeoJson', () => { }, }, ]; - const geojson = hitsToGeoJson(hits, flattenHitMock, geoFieldName, 'geo_point'); + const geojson = hitsToGeoJson(hits, flattenHitMock, geoFieldName, 'geo_point', []); expect(geojson.features.length).toBe(1); const feature = geojson.features[0]; expect(feature.properties.myField).toBe(8); @@ -128,7 +128,7 @@ describe('hitsToGeoJson', () => { }, }, ]; - const geojson = hitsToGeoJson(hits, flattenHitMock, geoFieldName, 'geo_point'); + const geojson = hitsToGeoJson(hits, flattenHitMock, geoFieldName, 'geo_point', []); expect(geojson.type).toBe('FeatureCollection'); expect(geojson.features.length).toBe(2); expect(geojson.features[0]).toEqual({ @@ -159,6 +159,23 @@ describe('hitsToGeoJson', () => { }); }); + it('Should convert epoch_millis value from string to integer', () => { + const hits = [ + { + _id: 'doc1', + _index: 'index1', + _source: { + [geoFieldName]: '20,100', + myDateField: '1587156257081', + }, + }, + ]; + const geojson = hitsToGeoJson(hits, flattenHitMock, geoFieldName, 'geo_point', ['myDateField']); + expect(geojson.type).toBe('FeatureCollection'); + expect(geojson.features.length).toBe(1); + expect(geojson.features[0].properties.myDateField).toBe(1587156257081); + }); + describe('dot in geoFieldName', () => { const indexPatternMock = { fields: { @@ -184,7 +201,7 @@ describe('hitsToGeoJson', () => { }, }, ]; - const geojson = hitsToGeoJson(hits, indexPatternFlattenHit, 'my.location', 'geo_point'); + const geojson = hitsToGeoJson(hits, indexPatternFlattenHit, 'my.location', 'geo_point', []); expect(geojson.features[0].geometry).toEqual({ coordinates: [100, 20], type: 'Point', @@ -199,7 +216,7 @@ describe('hitsToGeoJson', () => { }, }, ]; - const geojson = hitsToGeoJson(hits, indexPatternFlattenHit, 'my.location', 'geo_point'); + const geojson = hitsToGeoJson(hits, indexPatternFlattenHit, 'my.location', 'geo_point', []); expect(geojson.features[0].geometry).toEqual({ coordinates: [100, 20], type: 'Point', diff --git a/x-pack/legacy/plugins/maps/public/embeddable/README.md b/x-pack/plugins/maps/public/embeddable/README.md similarity index 100% rename from x-pack/legacy/plugins/maps/public/embeddable/README.md rename to x-pack/plugins/maps/public/embeddable/README.md diff --git a/x-pack/legacy/plugins/maps/public/embeddable/index.ts b/x-pack/plugins/maps/public/embeddable/index.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/embeddable/index.ts rename to x-pack/plugins/maps/public/embeddable/index.ts diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx similarity index 90% rename from x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx rename to x-pack/plugins/maps/public/embeddable/map_embeddable.tsx index b8e4c84ad56a17..dbd48d614e99b4 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable.tsx @@ -9,8 +9,6 @@ import React from 'react'; import { Provider } from 'react-redux'; import { render, unmountComponentAtNode } from 'react-dom'; import 'mapbox-gl/dist/mapbox-gl.css'; - -import { I18nContext } from 'ui/i18n'; import { Subscription } from 'rxjs'; import { Unsubscribe } from 'redux'; import { @@ -18,8 +16,8 @@ import { IContainer, EmbeddableInput, EmbeddableOutput, -} from '../../../../../../src/plugins/embeddable/public'; -import { APPLY_FILTER_TRIGGER } from '../../../../../../src/plugins/ui_actions/public'; +} from '../../../../../src/plugins/embeddable/public'; +import { APPLY_FILTER_TRIGGER } from '../../../../../src/plugins/ui_actions/public'; import { esFilters, IIndexPattern, @@ -27,11 +25,9 @@ import { Filter, Query, RefreshInterval, -} from '../../../../../../src/plugins/data/public'; - +} from '../../../../../src/plugins/data/public'; import { GisMap } from '../connected_components/gis_map'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { createMapStore, MapStore } from '../../../../../plugins/maps/public/reducers/store'; +import { createMapStore, MapStore } from '../reducers/store'; import { setGotoWithCenter, replaceLayerList, @@ -45,21 +41,18 @@ import { hideViewControl, setHiddenLayers, } from '../actions/map_actions'; -import { MapCenterAndZoom } from '../../../../../plugins/maps/common/descriptor_types'; +import { MapCenterAndZoom } from '../../common/descriptor_types'; import { setReadOnly, setIsLayerTOCOpen, setOpenTOCDetails } from '../actions/ui_actions'; import { getIsLayerTOCOpen, getOpenTOCDetails } from '../selectors/ui_selectors'; import { getInspectorAdapters, setEventHandlers, EventHandlers, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; +} from '../reducers/non_serializable_instances'; import { getMapCenter, getMapZoom, getHiddenLayerIds } from '../selectors/map_selectors'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { RenderToolTipContent } from '../../../../../plugins/maps/public/layers/tooltips/tooltip_property'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getUiActions } from '../../../../../plugins/maps/public/kibana_services'; +import { RenderToolTipContent } from '../layers/tooltips/tooltip_property'; +import { getUiActions, getCoreI18n } from '../kibana_services'; interface MapEmbeddableConfig { editUrl?: string; @@ -247,6 +240,8 @@ export class MapEmbeddable extends Embeddable diff --git a/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts new file mode 100644 index 00000000000000..a99fe63c5f3863 --- /dev/null +++ b/x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -0,0 +1,136 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { IIndexPattern } from 'src/plugins/data/public'; +// @ts-ignore +import { getMapsSavedObjectLoader } from '../angular/services/gis_map_saved_object_loader'; +import { MapEmbeddable, MapEmbeddableInput } from './map_embeddable'; +import { getIndexPatternService, getHttp, getMapsCapabilities } from '../kibana_services'; +import { + EmbeddableFactoryDefinition, + IContainer, +} from '../../../../../src/plugins/embeddable/public'; + +import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants'; + +import { createMapStore } from '../reducers/store'; +import { addLayerWithoutDataSync } from '../actions/map_actions'; +import { getQueryableUniqueIndexPatternIds } from '../selectors/map_selectors'; +import { getInitialLayers } from '../angular/get_initial_layers'; +import { mergeInputWithSavedMap } from './merge_input_with_saved_map'; + +export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { + type = MAP_SAVED_OBJECT_TYPE; + savedObjectMetaData = { + name: i18n.translate('xpack.maps.mapSavedObjectLabel', { + defaultMessage: 'Map', + }), + type: MAP_SAVED_OBJECT_TYPE, + getIconForSavedObject: () => APP_ICON, + }; + + async isEditable() { + return getMapsCapabilities().save as boolean; + } + + // Not supported yet for maps types. + canCreateNew() { + return false; + } + + getDisplayName() { + return i18n.translate('xpack.maps.embeddableDisplayName', { + defaultMessage: 'map', + }); + } + + async _getIndexPatterns(layerList: unknown[]): Promise { + // Need to extract layerList from store to get queryable index pattern ids + const store = createMapStore(); + let queryableIndexPatternIds; + try { + layerList.forEach((layerDescriptor: unknown) => { + store.dispatch(addLayerWithoutDataSync(layerDescriptor)); + }); + queryableIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState()); + } catch (error) { + throw new Error( + i18n.translate('xpack.maps.mapEmbeddableFactory.invalidLayerList', { + defaultMessage: 'Unable to load map, malformed layer list', + }) + ); + } + + const promises = queryableIndexPatternIds.map(async indexPatternId => { + try { + return await getIndexPatternService().get(indexPatternId); + } catch (error) { + // Unable to load index pattern, better to not throw error so map embeddable can render + // Error will be surfaced by map embeddable since it too will be unable to locate the index pattern + return null; + } + }); + const indexPatterns = await Promise.all(promises); + return _.compact(indexPatterns) as IIndexPattern[]; + } + + async _fetchSavedMap(savedObjectId: string) { + const savedObjectLoader = getMapsSavedObjectLoader(); + return await savedObjectLoader.get(savedObjectId); + } + + createFromSavedObject = async ( + savedObjectId: string, + input: MapEmbeddableInput, + parent?: IContainer + ) => { + const savedMap = await this._fetchSavedMap(savedObjectId); + const layerList = getInitialLayers(savedMap.layerListJSON); + const indexPatterns = await this._getIndexPatterns(layerList); + + const embeddable = new MapEmbeddable( + { + layerList, + title: savedMap.title, + editUrl: getHttp().basePath.prepend(createMapPath(savedObjectId)), + indexPatterns, + editable: await this.isEditable(), + }, + input, + parent + ); + + try { + embeddable.updateInput(mergeInputWithSavedMap(input, savedMap)); + } catch (error) { + throw new Error( + i18n.translate('xpack.maps.mapEmbeddableFactory.invalidSavedObject', { + defaultMessage: 'Unable to load map, malformed saved object', + }) + ); + } + + return embeddable; + }; + + create = async (input: MapEmbeddableInput, parent?: IContainer) => { + const layerList = getInitialLayers(); + const indexPatterns = await this._getIndexPatterns(layerList); + + return new MapEmbeddable( + { + layerList, + title: input.title ?? '', + indexPatterns, + editable: false, + }, + input, + parent + ); + }; +} diff --git a/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts b/x-pack/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts rename to x-pack/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts diff --git a/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.js b/x-pack/plugins/maps/public/embeddable/merge_input_with_saved_map.js similarity index 89% rename from x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.js rename to x-pack/plugins/maps/public/embeddable/merge_input_with_saved_map.js index 8e3e0a9168e30e..d91c91b3b223c9 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/merge_input_with_saved_map.js +++ b/x-pack/plugins/maps/public/embeddable/merge_input_with_saved_map.js @@ -5,8 +5,8 @@ */ import _ from 'lodash'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { DEFAULT_IS_LAYER_TOC_OPEN } from '../../../../../plugins/maps/public/reducers/ui'; + +import { DEFAULT_IS_LAYER_TOC_OPEN } from '../reducers/ui'; const MAP_EMBEDDABLE_INPUT_KEYS = [ 'hideFilterActions', diff --git a/x-pack/legacy/plugins/maps/public/feature_catalogue_entry.ts b/x-pack/plugins/maps/public/feature_catalogue_entry.ts similarity index 89% rename from x-pack/legacy/plugins/maps/public/feature_catalogue_entry.ts rename to x-pack/plugins/maps/public/feature_catalogue_entry.ts index fdda76b4e12127..6c2579bd3e4e2a 100644 --- a/x-pack/legacy/plugins/maps/public/feature_catalogue_entry.ts +++ b/x-pack/plugins/maps/public/feature_catalogue_entry.ts @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import { APP_ID, APP_ICON } from '../common/constants'; import { getAppTitle } from '../common/i18n_getters'; -import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; +import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; export const featureCatalogueEntry = { id: APP_ID, diff --git a/x-pack/legacy/plugins/maps/public/help_menu_util.js b/x-pack/plugins/maps/public/help_menu_util.js similarity index 81% rename from x-pack/legacy/plugins/maps/public/help_menu_util.js rename to x-pack/plugins/maps/public/help_menu_util.js index 70b9340b562cd1..053caf66883094 100644 --- a/x-pack/legacy/plugins/maps/public/help_menu_util.js +++ b/x-pack/plugins/maps/public/help_menu_util.js @@ -3,8 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getDocLinks, getCoreChrome } from '../../../../plugins/maps/public/kibana_services'; + +import { getDocLinks, getCoreChrome } from './kibana_services'; export function addHelpMenuToAppChrome() { const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = getDocLinks(); diff --git a/x-pack/legacy/plugins/maps/public/icon.svg b/x-pack/plugins/maps/public/icon.svg similarity index 100% rename from x-pack/legacy/plugins/maps/public/icon.svg rename to x-pack/plugins/maps/public/icon.svg diff --git a/x-pack/plugins/maps/public/index.scss b/x-pack/plugins/maps/public/index.scss new file mode 100644 index 00000000000000..8b2f6d3cb61563 --- /dev/null +++ b/x-pack/plugins/maps/public/index.scss @@ -0,0 +1,17 @@ +/* GIS plugin styles */ + +// Import the EUI global scope so we can use EUI constants +@import 'src/legacy/ui/public/styles/_styling_constants'; + +// Prefix all styles with "map" to avoid conflicts. +// Examples +// mapChart +// mapChart__legend +// mapChart__legend--small +// mapChart__legend-isLoading + +@import 'main'; +@import 'mapbox_hacks'; +@import 'connected_components/index'; +@import 'components/index'; +@import 'layers/index'; diff --git a/x-pack/plugins/maps/public/layers/layer.js b/x-pack/plugins/maps/public/layers/layer.js index 19dcbaf1dfcfdc..9362ce2c028e6e 100644 --- a/x-pack/plugins/maps/public/layers/layer.js +++ b/x-pack/plugins/maps/public/layers/layer.js @@ -14,7 +14,7 @@ import { SOURCE_DATA_ID_ORIGIN, } from '../../common/constants'; import uuid from 'uuid/v4'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths + import { copyPersistentState } from '../reducers/util.js'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js index 96679f0e859412..aaa56b30c735a4 100644 --- a/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js @@ -51,7 +51,7 @@ function getDocValueAndSourceFields(indexPattern, fieldNames) { lang: field.lang, }, }; - } else if (field.readFromDocValues) { + } else if (field.type !== ES_GEO_FIELD_TYPE.GEO_SHAPE && field.readFromDocValues) { const docValueField = field.type === 'date' ? { @@ -387,11 +387,21 @@ export class ESSearchSource extends AbstractESSource { }); return properties; }; + const epochMillisFields = searchFilters.fieldNames.filter(fieldName => { + const field = getField(indexPattern, fieldName); + return field.readFromDocValues && field.type === 'date'; + }); let featureCollection; try { const geoField = await this._getGeoField(); - featureCollection = hitsToGeoJson(hits, flattenHit, geoField.name, geoField.type); + featureCollection = hitsToGeoJson( + hits, + flattenHit, + geoField.name, + geoField.type, + epochMillisFields + ); } catch (error) { throw new Error( i18n.translate('xpack.maps.source.esSearch.convertToGeoJsonErrorMsg', { diff --git a/x-pack/plugins/maps/public/layers/sources/es_source/es_source.js b/x-pack/plugins/maps/public/layers/sources/es_source/es_source.js index 3402e367cbd736..9ab87577b77806 100644 --- a/x-pack/plugins/maps/public/layers/sources/es_source/es_source.js +++ b/x-pack/plugins/maps/public/layers/sources/es_source/es_source.js @@ -16,7 +16,7 @@ import { createExtentFilter } from '../../../elasticsearch_geo_utils'; import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import uuid from 'uuid/v4'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths + import { copyPersistentState } from '../../../reducers/util'; import { ES_GEO_FIELD_TYPE } from '../../../../common/constants'; import { DataRequestAbortError } from '../../util/data_request'; diff --git a/x-pack/plugins/maps/public/layers/sources/source.js b/x-pack/plugins/maps/public/layers/sources/source.js index 555b8999d62847..fd93daf249b265 100644 --- a/x-pack/plugins/maps/public/layers/sources/source.js +++ b/x-pack/plugins/maps/public/layers/sources/source.js @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { copyPersistentState } from '../../reducers/util'; import { MIN_ZOOM, MAX_ZOOM } from '../../../common/constants'; diff --git a/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js index ea521f8749d80a..8cef78f9a8f210 100644 --- a/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js +++ b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js @@ -279,6 +279,10 @@ export class DynamicStyleProperty extends AbstractStyleProperty { } getNumericalMbFeatureStateValue(value) { + if (typeof value === 'number') { + return value; + } + const valueAsFloat = parseFloat(value); return isNaN(valueAsFloat) ? null : valueAsFloat; } diff --git a/x-pack/plugins/maps/public/maps_vis_type_alias.js b/x-pack/plugins/maps/public/maps_vis_type_alias.js new file mode 100644 index 00000000000000..85613f4608c6f4 --- /dev/null +++ b/x-pack/plugins/maps/public/maps_vis_type_alias.js @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { APP_ID, APP_ICON, MAP_BASE_URL } from '../common/constants'; +import { getInjectedVarFunc, getVisualizations } from './kibana_services'; + +export function getMapsVisTypeAlias() { + const showMapVisualizationTypes = getInjectedVarFunc()('showMapVisualizationTypes', false); + if (!showMapVisualizationTypes) { + getVisualizations().hideTypes(['region_map', 'tile_map']); + } + + const description = i18n.translate('xpack.maps.visTypeAlias.description', { + defaultMessage: 'Create and style maps with multiple layers and indices.', + }); + + const legacyMapVisualizationWarning = i18n.translate( + 'xpack.maps.visTypeAlias.legacyMapVizWarning', + { + defaultMessage: `Use the Maps app instead of Coordinate Map and Region Map. +The Maps app offers more functionality and is easier to use.`, + } + ); + + return { + aliasUrl: MAP_BASE_URL, + name: APP_ID, + title: i18n.translate('xpack.maps.visTypeAlias.title', { + defaultMessage: 'Maps', + }), + description: showMapVisualizationTypes + ? `${description} ${legacyMapVisualizationWarning}` + : description, + icon: APP_ICON, + stage: 'production', + }; +} diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index d3b9626dc83661..0b076621326ce3 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -33,10 +33,17 @@ import { setVisualizations, // @ts-ignore } from './kibana_services'; +import { featureCatalogueEntry } from './feature_catalogue_entry'; +// @ts-ignore +import { getMapsVisTypeAlias } from './maps_vis_type_alias'; import { registerLayerWizards } from './layers/load_layer_wizards'; +import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; +import { VisualizationsSetup } from '../../../../src/plugins/visualizations/public'; export interface MapsPluginSetupDependencies { inspector: InspectorSetupContract; + home: HomePublicPluginSetup; + visualizations: VisualizationsSetup; } // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface MapsPluginStartDependencies {} @@ -94,8 +101,15 @@ export class MapsPlugin MapsPluginStartDependencies > { public setup(core: CoreSetup, plugins: MapsPluginSetupDependencies) { - plugins.inspector.registerView(MapView); + const { inspector, home, visualizations } = plugins; + bindSetupCoreAndPlugins(core, plugins); + + inspector.registerView(MapView); + home.featureCatalogue.register(featureCatalogueEntry); + visualizations.registerAlias(getMapsVisTypeAlias()); } - public start(core: CoreStart, plugins: any) {} + public start(core: CoreStart, plugins: any) { + bindStartCoreAndPlugins(core, plugins); + } } diff --git a/x-pack/plugins/maps/public/reducers/ui.ts b/x-pack/plugins/maps/public/reducers/ui.ts index 7429545ec0e46e..f577618c74ffef 100644 --- a/x-pack/plugins/maps/public/reducers/ui.ts +++ b/x-pack/plugins/maps/public/reducers/ui.ts @@ -16,7 +16,6 @@ import { SHOW_TOC_DETAILS, HIDE_TOC_DETAILS, UPDATE_INDEXING_STAGE, - // @ts-ignore } from '../actions/ui_actions'; export enum FLYOUT_STATE { diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts b/x-pack/plugins/maps/public/selectors/map_selectors.d.ts similarity index 81% rename from x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts rename to x-pack/plugins/maps/public/selectors/map_selectors.d.ts index 8c99e0adcc14fe..32579d036590e3 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.d.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.d.ts @@ -6,8 +6,8 @@ import { AnyAction } from 'redux'; import { MapCenter } from '../../common/descriptor_types'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MapStoreState } from '../../../../../plugins/maps/public/reducers/store'; + +import { MapStoreState } from '../reducers/store'; export function getHiddenLayerIds(state: MapStoreState): string[]; diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js b/x-pack/plugins/maps/public/selectors/map_selectors.js similarity index 82% rename from x-pack/legacy/plugins/maps/public/selectors/map_selectors.js rename to x-pack/plugins/maps/public/selectors/map_selectors.js index 1e710259355198..c7073efa96cd55 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js +++ b/x-pack/plugins/maps/public/selectors/map_selectors.js @@ -6,31 +6,27 @@ import { createSelector } from 'reselect'; import _ from 'lodash'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TileLayer } from '../../../../../plugins/maps/public/layers/tile_layer'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { VectorTileLayer } from '../../../../../plugins/maps/public/layers/vector_tile_layer'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { VectorLayer } from '../../../../../plugins/maps/public/layers/vector_layer'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { HeatmapLayer } from '../../../../../plugins/maps/public/layers/heatmap_layer'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { BlendedVectorLayer } from '../../../../../plugins/maps/public/layers/blended_vector_layer'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getTimeFilter } from '../../../../../plugins/maps/public/kibana_services'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TiledVectorLayer } from '../../../../../plugins/maps/public/layers/tiled_vector_layer'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getInspectorAdapters } from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; -import { - copyPersistentState, - TRACKED_LAYER_DESCRIPTOR, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../plugins/maps/public/reducers/util'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { InnerJoin } from '../../../../../plugins/maps/public/layers/joins/inner_join'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { getSourceByType } from '../../../../../plugins/maps/public/layers/sources/source_registry'; + +import { TileLayer } from '../layers/tile_layer'; + +import { VectorTileLayer } from '../layers/vector_tile_layer'; + +import { VectorLayer } from '../layers/vector_layer'; + +import { HeatmapLayer } from '../layers/heatmap_layer'; + +import { BlendedVectorLayer } from '../layers/blended_vector_layer'; + +import { getTimeFilter } from '../kibana_services'; + +import { getInspectorAdapters } from '../reducers/non_serializable_instances'; +import { TiledVectorLayer } from '../layers/tiled_vector_layer'; + +import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../reducers/util'; + +import { InnerJoin } from '../layers/joins/inner_join'; + +import { getSourceByType } from '../layers/sources/source_registry'; function createLayerInstance(layerDescriptor, inspectorAdapters) { const source = createSourceInstance(layerDescriptor.sourceDescriptor, inspectorAdapters); diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js b/x-pack/plugins/maps/public/selectors/map_selectors.test.js similarity index 62% rename from x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js rename to x-pack/plugins/maps/public/selectors/map_selectors.test.js index 72cc7486175401..fec16251914ead 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js +++ b/x-pack/plugins/maps/public/selectors/map_selectors.test.js @@ -4,18 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('../../../../../plugins/maps/public/layers/vector_layer', () => {}); -jest.mock('../../../../../plugins/maps/public/layers/tiled_vector_layer', () => {}); -jest.mock('../../../../../plugins/maps/public/layers/blended_vector_layer', () => {}); -jest.mock('../../../../../plugins/maps/public/layers/heatmap_layer', () => {}); -jest.mock('../../../../../plugins/maps/public/layers/vector_tile_layer', () => {}); -jest.mock('../../../../../plugins/maps/public/layers/joins/inner_join', () => {}); -jest.mock('../../../../../plugins/maps/public/reducers/non_serializable_instances', () => ({ +jest.mock('../layers/vector_layer', () => {}); +jest.mock('../layers/tiled_vector_layer', () => {}); +jest.mock('../layers/blended_vector_layer', () => {}); +jest.mock('../layers/heatmap_layer', () => {}); +jest.mock('../layers/vector_tile_layer', () => {}); +jest.mock('../layers/joins/inner_join', () => {}); +jest.mock('../reducers/non_serializable_instances', () => ({ getInspectorAdapters: () => { return {}; }, })); -jest.mock('../../../../../plugins/maps/public/kibana_services', () => ({ +jest.mock('../kibana_services', () => ({ getTimeFilter: () => ({ getTime: () => { return { diff --git a/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.ts b/x-pack/plugins/maps/public/selectors/ui_selectors.ts similarity index 74% rename from x-pack/legacy/plugins/maps/public/selectors/ui_selectors.ts rename to x-pack/plugins/maps/public/selectors/ui_selectors.ts index fdf2a8ea0e4f37..32d4beeb381d7f 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/ui_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/ui_selectors.ts @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MapStoreState } from '../../../../../plugins/maps/public/reducers/store'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { FLYOUT_STATE, INDEXING_STAGE } from '../../../../../plugins/maps/public/reducers/ui'; +import { MapStoreState } from '../reducers/store'; + +import { FLYOUT_STATE, INDEXING_STAGE } from '../reducers/ui'; export const getFlyoutDisplay = ({ ui }: MapStoreState): FLYOUT_STATE => ui.flyoutDisplay; export const getIsSetViewOpen = ({ ui }: MapStoreState): boolean => ui.isSetViewOpen; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts index 3c959b827bb1c7..fb3b2b35199477 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/analytics.ts @@ -52,7 +52,7 @@ export interface ClassificationAnalysis { classification: Classification; } -export interface LoadRegressionExploreDataArg { +export interface LoadExploreDataArg { filterByIsTraining?: boolean; searchQuery: SavedSearchQuery; } @@ -409,11 +409,11 @@ export function getEvalQueryBody({ ignoreDefaultQuery, }: { resultsField: string; - isTraining: boolean; + isTraining?: boolean; searchQuery?: ResultsSearchQuery; ignoreDefaultQuery?: boolean; }) { - let query; + let query: any; const trainingQuery: ResultsSearchQuery = { term: { [`${resultsField}.is_training`]: { value: isTraining } }, @@ -426,19 +426,25 @@ export function getEvalQueryBody({ searchQueryClone.bool.must = []; } - searchQueryClone.bool.must.push(trainingQuery); + if (isTraining !== undefined) { + searchQueryClone.bool.must.push(trainingQuery); + } + query = searchQueryClone; } else if (isQueryStringQuery(searchQueryClone)) { query = { bool: { - must: [searchQueryClone, trainingQuery], + must: [searchQueryClone], }, }; + if (isTraining !== undefined) { + query.bool.must.push(trainingQuery); + } } else { // Not a bool or string query so we need to create it so can add the trainingQuery query = { bool: { - must: [trainingQuery], + must: isTraining !== undefined ? [trainingQuery] : [], }, }; } @@ -456,7 +462,7 @@ interface EvaluateMetrics { } interface LoadEvalDataConfig { - isTraining: boolean; + isTraining?: boolean; index: string; dependentVariable: string; resultsField: string; @@ -535,7 +541,7 @@ interface TrackTotalHitsSearchResponse { interface LoadDocsCountConfig { ignoreDefaultQuery?: boolean; - isTraining: boolean; + isTraining?: boolean; searchQuery: SavedSearchQuery; resultsField: string; destIndex: string; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration_data_grid.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration_data_grid.tsx new file mode 100644 index 00000000000000..424fc002795caa --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration_data_grid.tsx @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Dispatch, FC, SetStateAction, useCallback, useMemo } from 'react'; + +import { i18n } from '@kbn/i18n'; + +import { EuiDataGrid, EuiDataGridPaginationProps, EuiDataGridSorting } from '@elastic/eui'; + +import { euiDataGridStyle, euiDataGridToolbarSettings } from '../../../../common'; + +import { mlFieldFormatService } from '../../../../../services/field_format_service'; + +import { IndexPattern } from '../../../../../../../../../../src/plugins/data/public'; + +const PAGE_SIZE_OPTIONS = [5, 10, 25, 50]; + +type Pagination = Pick; +type TableItem = Record; + +interface ExplorationDataGridProps { + colorRange?: (d: number) => string; + columns: any[]; + indexPattern: IndexPattern; + pagination: Pagination; + resultsField: string; + rowCount: number; + selectedFields: string[]; + setPagination: Dispatch>; + setSelectedFields: Dispatch>; + setSortingColumns: Dispatch>; + sortingColumns: EuiDataGridSorting['columns']; + tableItems: TableItem[]; +} + +export const ClassificationExplorationDataGrid: FC = ({ + columns, + indexPattern, + pagination, + resultsField, + rowCount, + selectedFields, + setPagination, + setSelectedFields, + setSortingColumns, + sortingColumns, + tableItems, +}) => { + const renderCellValue = useMemo(() => { + return ({ rowIndex, columnId }: { rowIndex: number; columnId: string; setCellProps: any }) => { + const adjustedRowIndex = rowIndex - pagination.pageIndex * pagination.pageSize; + + const fullItem = tableItems[adjustedRowIndex]; + + if (fullItem === undefined) { + return null; + } + + let format: any; + + if (indexPattern !== undefined) { + format = mlFieldFormatService.getFieldFormatFromIndexPattern(indexPattern, columnId, ''); + } + + const cellValue = + fullItem.hasOwnProperty(columnId) && fullItem[columnId] !== undefined + ? fullItem[columnId] + : null; + + if (format !== undefined) { + return format.convert(cellValue, 'text'); + } + + if (typeof cellValue === 'string' || cellValue === null) { + return cellValue; + } + + if (typeof cellValue === 'boolean') { + return cellValue ? 'true' : 'false'; + } + + if (typeof cellValue === 'object' && cellValue !== null) { + return JSON.stringify(cellValue); + } + + return cellValue; + }; + }, [resultsField, rowCount, tableItems, pagination.pageIndex, pagination.pageSize]); + + const onChangeItemsPerPage = useCallback( + pageSize => { + setPagination(p => { + const pageIndex = Math.floor((p.pageSize * p.pageIndex) / pageSize); + return { pageIndex, pageSize }; + }); + }, + [setPagination] + ); + + const onChangePage = useCallback(pageIndex => setPagination(p => ({ ...p, pageIndex })), [ + setPagination, + ]); + + const onSort = useCallback(sc => setSortingColumns(sc), [setSortingColumns]); + + return ( + + ); +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx index 91dae49ba5c497..af90547606f82d 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/evaluate_panel.tsx @@ -117,13 +117,7 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) const resultsField = jobConfig.dest.results_field; let requiresKeyword = false; - const loadData = async ({ - isTrainingClause, - ignoreDefaultQuery = true, - }: { - isTrainingClause: { query: string; operator: string }; - ignoreDefaultQuery?: boolean; - }) => { + const loadData = async ({ isTraining }: { isTraining: boolean | undefined }) => { setIsLoading(true); try { @@ -134,19 +128,18 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) } const evalData = await loadEvalData({ - isTraining: false, + isTraining, index, dependentVariable, resultsField, predictionFieldName, searchQuery, - ignoreDefaultQuery, jobType: ANALYSIS_CONFIG_TYPE.CLASSIFICATION, requiresKeyword, }); const docsCountResp = await loadDocsCount({ - isTraining: false, + isTraining, searchQuery, resultsField, destIndex: jobConfig.dest.index, @@ -225,29 +218,46 @@ export const EvaluatePanel: FC = ({ jobConfig, jobStatus, searchQuery }) }, [confusionMatrixData]); useEffect(() => { - const hasIsTrainingClause = - isResultsSearchBoolQuery(searchQuery) && - searchQuery.bool.must.filter( - (clause: any) => clause.match && clause.match[`${resultsField}.is_training`] !== undefined - ); - const isTrainingClause = - hasIsTrainingClause && - hasIsTrainingClause[0] && - hasIsTrainingClause[0].match[`${resultsField}.is_training`]; + let isTraining: boolean | undefined; + const query = + isResultsSearchBoolQuery(searchQuery) && (searchQuery.bool.should || searchQuery.bool.filter); - const noTrainingQuery = isTrainingClause === false || isTrainingClause === undefined; + if (query !== undefined && query !== false) { + for (let i = 0; i < query.length; i++) { + const clause = query[i]; - if (noTrainingQuery) { + if (clause.match && clause.match[`${resultsField}.is_training`] !== undefined) { + isTraining = clause.match[`${resultsField}.is_training`]; + break; + } else if ( + clause.bool && + (clause.bool.should !== undefined || clause.bool.filter !== undefined) + ) { + const innerQuery = clause.bool.should || clause.bool.filter; + if (innerQuery !== undefined) { + for (let j = 0; j < innerQuery.length; j++) { + const innerClause = innerQuery[j]; + if ( + innerClause.match && + innerClause.match[`${resultsField}.is_training`] !== undefined + ) { + isTraining = innerClause.match[`${resultsField}.is_training`]; + break; + } + } + } + } + } + } + if (isTraining === undefined) { setDataSubsetTitle(SUBSET_TITLE.ENTIRE); } else { setDataSubsetTitle( - isTrainingClause && isTrainingClause.query === 'true' - ? SUBSET_TITLE.TRAINING - : SUBSET_TITLE.TESTING + isTraining && isTraining === true ? SUBSET_TITLE.TRAINING : SUBSET_TITLE.TESTING ); } - loadData({ isTrainingClause }); + loadData({ isTraining }); }, [JSON.stringify(searchQuery)]); const renderCellValue = ({ diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/results_table.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/results_table.tsx index 9758dd969b443f..bf63dfe68fe9e5 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/results_table.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/results_table.tsx @@ -4,71 +4,39 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, FC, useEffect, useState } from 'react'; -import moment from 'moment-timezone'; - +import React, { Fragment, FC, useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { - EuiBadge, - EuiButtonIcon, EuiCallOut, - EuiCheckbox, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiPanel, - EuiPopover, - EuiPopoverTitle, EuiProgress, EuiSpacer, EuiText, - EuiToolTip, - Query, } from '@elastic/eui'; -import { Query as QueryType } from '../../../analytics_management/components/analytics_list/common'; -import { ES_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/public'; -import { mlFieldFormatService } from '../../../../../services/field_format_service'; import { IndexPattern } from '../../../../../../../../../../src/plugins/data/public'; - -import { - ColumnType, - mlInMemoryTableBasicFactory, - OnTableChangeArg, - SortingPropType, - SORT_DIRECTION, -} from '../../../../../components/ml_in_memory_table'; - -import { formatHumanReadableDateTimeSeconds } from '../../../../../util/date_utils'; -import { Field } from '../../../../../../../common/types/fields'; -import { SavedSearchQuery } from '../../../../../contexts/ml'; import { BASIC_NUMERICAL_TYPES, EXTENDED_NUMERICAL_TYPES, - isKeywordAndTextType, sortRegressionResultsFields, } from '../../../../common/fields'; import { - toggleSelectedField, - EsDoc, DataFrameAnalyticsConfig, - EsFieldName, MAX_COLUMNS, - getPredictedFieldName, INDEX_STATUS, SEARCH_SIZE, defaultSearchQuery, - getDependentVar, } from '../../../../common'; import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/columns'; import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/analytics_list/common'; -import { useExploreData, TableItem } from './use_explore_data'; +import { useExploreData } from './use_explore_data'; // TableItem import { ExplorationTitle } from './classification_exploration'; - -const PAGE_SIZE_OPTIONS = [5, 10, 25, 50]; - -const MlInMemoryTableBasic = mlInMemoryTableBasicFactory(); +import { ClassificationExplorationDataGrid } from './classification_exploration_data_grid'; +import { ExplorationQueryBar } from '../exploration_query_bar'; const showingDocs = i18n.translate( 'xpack.ml.dataframe.analytics.classificationExploration.documentsShownHelpText', @@ -94,307 +62,65 @@ interface Props { export const ResultsTable: FC = React.memo( ({ indexPattern, jobConfig, jobStatus, setEvaluateSearchQuery }) => { - const [pageIndex, setPageIndex] = useState(0); - const [pageSize, setPageSize] = useState(25); - const [selectedFields, setSelectedFields] = useState([] as Field[]); - const [docFields, setDocFields] = useState([] as Field[]); - const [depVarType, setDepVarType] = useState(undefined); - const [isColumnsPopoverVisible, setColumnsPopoverVisible] = useState(false); - const [searchQuery, setSearchQuery] = useState(defaultSearchQuery); - const [searchError, setSearchError] = useState(undefined); - const [searchString, setSearchString] = useState(undefined); - - const predictedFieldName = getPredictedFieldName( - jobConfig.dest.results_field, - jobConfig.analysis - ); - - const dependentVariable = getDependentVar(jobConfig.analysis); - - function toggleColumnsPopover() { - setColumnsPopoverVisible(!isColumnsPopoverVisible); - } - - function closeColumnsPopover() { - setColumnsPopoverVisible(false); - } - - function toggleColumn(column: EsFieldName) { - if (tableItems.length > 0 && jobConfig !== undefined) { - // spread to a new array otherwise the component wouldn't re-render - setSelectedFields([ - ...toggleSelectedField(selectedFields, column, jobConfig.dest.results_field, depVarType), - ]); - } - } - const needsDestIndexFields = indexPattern && indexPattern.title === jobConfig.source.index[0]; - + const resultsField = jobConfig.dest.results_field; const { errorMessage, - loadExploreData, - sortField, - sortDirection, - status, - tableItems, - } = useExploreData( - jobConfig, - needsDestIndexFields, + fieldTypes, + pagination, + searchQuery, selectedFields, + rowCount, + setPagination, + setSearchQuery, setSelectedFields, - setDocFields, - setDepVarType - ); + setSortingColumns, + sortingColumns, + status, + tableFields, + tableItems, + } = useExploreData(jobConfig, needsDestIndexFields); - const columns: Array> = selectedFields - .sort(({ name: a }, { name: b }) => sortRegressionResultsFields(a, b, jobConfig)) - .map(field => { - const { type } = field; - let format: any; + useEffect(() => { + setEvaluateSearchQuery(searchQuery); + }, [JSON.stringify(searchQuery)]); - if (indexPattern !== undefined) { - format = mlFieldFormatService.getFieldFormatFromIndexPattern(indexPattern, field.id, ''); - } + const columns = tableFields + .sort((a: any, b: any) => sortRegressionResultsFields(a, b, jobConfig)) + .map((field: any) => { + // Built-in values are ['boolean', 'currency', 'datetime', 'numeric', 'json'] + // To fall back to the default string schema it needs to be undefined. + let schema; + let isSortable = true; + const type = fieldTypes[field]; const isNumber = type !== undefined && (BASIC_NUMERICAL_TYPES.has(type) || EXTENDED_NUMERICAL_TYPES.has(type)); - const column: ColumnType = { - field: field.name, - name: field.name, - sortable: true, - truncateText: true, - }; - - const render = (d: any, fullItem: EsDoc) => { - if (format !== undefined) { - d = format.convert(d, 'text'); - return d; - } - - if (Array.isArray(d) && d.every(item => typeof item === 'string')) { - // If the cells data is an array of strings, return as a comma separated list. - // The list will get limited to 5 items with `…` at the end if there's more in the original array. - return `${d.slice(0, 5).join(', ')}${d.length > 5 ? ', …' : ''}`; - } else if (Array.isArray(d)) { - // If the cells data is an array of e.g. objects, display a 'array' badge with a - // tooltip that explains that this type of field is not supported in this table. - return ( - - - {i18n.translate( - 'xpack.ml.dataframe.analytics.classificationExploration.indexArrayBadgeContent', - { - defaultMessage: 'array', - } - )} - - - ); - } - - return d; - }; - if (isNumber) { - column.dataType = 'number'; - column.render = render; - } else if (typeof type !== 'undefined') { - switch (type) { - case ES_FIELD_TYPES.BOOLEAN: - column.dataType = ES_FIELD_TYPES.BOOLEAN; - column.render = d => (d ? 'true' : 'false'); - break; - case ES_FIELD_TYPES.DATE: - column.align = 'right'; - if (format !== undefined) { - column.render = render; - } else { - column.render = (d: any) => { - if (d !== undefined) { - return formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000); - } - return d; - }; - } - break; - default: - column.render = render; - break; - } - } else { - column.render = render; + schema = 'numeric'; } - return column; - }); - - const docFieldsCount = docFields.length; - - useEffect(() => { - if ( - jobConfig !== undefined && - columns.length > 0 && - selectedFields.length > 0 && - sortField !== undefined && - sortDirection !== undefined && - selectedFields.some(field => field.name === sortField) - ) { - let field = sortField; - // If sorting by predictedField use dependentVar type - if (predictedFieldName === sortField) { - field = dependentVariable; + switch (type) { + case 'date': + schema = 'datetime'; + break; + case 'geo_point': + schema = 'json'; + break; + case 'boolean': + schema = 'boolean'; + break; } - const requiresKeyword = isKeywordAndTextType(field); - - loadExploreData({ - field: sortField, - direction: sortDirection, - searchQuery, - requiresKeyword, - }); - } - }, [JSON.stringify(searchQuery)]); - - useEffect(() => { - // By default set sorting to descending on the prediction field (`_prediction`). - // if that's not available sort ascending on the first column. Check if the current sorting field is still available. - if ( - jobConfig !== undefined && - columns.length > 0 && - selectedFields.length > 0 && - !selectedFields.some(field => field.name === sortField) - ) { - const predictedFieldSelected = selectedFields.some( - field => field.name === predictedFieldName - ); - - // CHECK IF keyword suffix is needed (if predicted field is selected we have to check the dependent variable type) - let sortByField = predictedFieldSelected ? dependentVariable : selectedFields[0].name; - - const requiresKeyword = isKeywordAndTextType(sortByField); - - sortByField = predictedFieldSelected ? predictedFieldName : sortByField; - - const direction = predictedFieldSelected ? SORT_DIRECTION.DESC : SORT_DIRECTION.ASC; - loadExploreData({ field: sortByField, direction, searchQuery, requiresKeyword }); - } - }, [ - jobConfig, - columns.length, - selectedFields.length, - sortField, - sortDirection, - tableItems.length, - ]); - - let sorting: SortingPropType = false; - let onTableChange; - - if (columns.length > 0 && sortField !== '' && sortField !== undefined) { - sorting = { - sort: { - field: sortField, - direction: sortDirection, - }, - }; - - onTableChange = ({ - page = { index: 0, size: 10 }, - sort = { field: sortField, direction: sortDirection }, - }: OnTableChangeArg) => { - const { index, size } = page; - setPageIndex(index); - setPageSize(size); - if (sort.field !== sortField || sort.direction !== sortDirection) { - let field = sort.field; - // If sorting by predictedField use depVar for type check - if (predictedFieldName === sort.field) { - field = dependentVariable; - } - - loadExploreData({ - ...sort, - searchQuery, - requiresKeyword: isKeywordAndTextType(field), - }); + if (field === `${resultsField}.feature_importance`) { + isSortable = false; } - }; - } - const pagination = { - initialPageIndex: pageIndex, - initialPageSize: pageSize, - totalItemCount: tableItems.length, - pageSizeOptions: PAGE_SIZE_OPTIONS, - hidePerPageOptions: false, - }; - - const onQueryChange = ({ query, error }: { query: QueryType; error: any }) => { - if (error) { - setSearchError(error.message); - } else { - try { - const esQueryDsl = Query.toESQuery(query); - setSearchQuery(esQueryDsl); - setSearchString(query.text); - setSearchError(undefined); - // set query for use in evaluate panel - setEvaluateSearchQuery(esQueryDsl); - } catch (e) { - setSearchError(e.toString()); - } - } - }; + return { id: field, schema, isSortable }; + }); - const search = { - onChange: onQueryChange, - defaultQuery: searchString, - box: { - incremental: false, - placeholder: i18n.translate( - 'xpack.ml.dataframe.analytics.regressionExploration.searchBoxPlaceholder', - { - defaultMessage: 'E.g. avg>0.5', - } - ), - }, - filters: [ - { - type: 'field_value_toggle_group', - field: `${jobConfig.dest.results_field}.is_training`, - items: [ - { - value: false, - name: i18n.translate( - 'xpack.ml.dataframe.analytics.regressionExploration.isTestingLabel', - { - defaultMessage: 'Testing', - } - ), - }, - { - value: true, - name: i18n.translate( - 'xpack.ml.dataframe.analytics.regressionExploration.isTrainingLabel', - { - defaultMessage: 'Training', - } - ), - }, - ], - }, - ], - }; + const docFieldsCount = tableFields.length; if (jobConfig === undefined) { return null; @@ -426,11 +152,6 @@ export const ResultsTable: FC = React.memo( ); } - const tableError = - status === INDEX_STATUS.ERROR && errorMessage.includes('parsing_exception') - ? errorMessage - : searchError; - return ( = React.memo( {docFieldsCount > MAX_COLUMNS && ( {i18n.translate( - 'xpack.ml.dataframe.analytics.regressionExploration.fieldSelection', + 'xpack.ml.dataframe.analytics.classificationExploration.fieldSelection', { defaultMessage: '{selectedFieldsLength, number} of {docFieldsCount, number} {docFieldsCount, plural, one {field} other {fields}} selected', @@ -466,52 +187,6 @@ export const ResultsTable: FC = React.memo( )}
- - - - } - isOpen={isColumnsPopoverVisible} - closePopover={closeColumnsPopover} - ownFocus - > - - {i18n.translate( - 'xpack.ml.dataframe.analytics.regressionExploration.selectFieldsPopoverTitle', - { - defaultMessage: 'Select fields', - } - )} - -
- {docFields.map(({ name }) => ( - field.name === name)} - onChange={() => toggleColumn(name)} - disabled={ - selectedFields.some(field => field.name === name) && - selectedFields.length === 1 - } - /> - ))} -
-
-
-
@@ -520,28 +195,39 @@ export const ResultsTable: FC = React.memo( )} {(columns.length > 0 || searchQuery !== defaultSearchQuery) && ( - - - - - - - + + + + + + + + + + + + + + + + )} ); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_explore_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_explore_data.ts index 9527a9adb98ced..c8809ca5e471bd 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_explore_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/use_explore_data.ts @@ -3,113 +3,158 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState, Dispatch, SetStateAction } from 'react'; +import { EuiDataGridPaginationProps, EuiDataGridSorting } from '@elastic/eui'; import { SearchResponse } from 'elasticsearch'; import { cloneDeep } from 'lodash'; -import { SortDirection, SORT_DIRECTION } from '../../../../../components/ml_in_memory_table'; +import { SORT_DIRECTION } from '../../../../../components/ml_in_memory_table'; import { ml } from '../../../../../services/ml_api_service'; import { getNestedProperty } from '../../../../../util/object_utils'; import { newJobCapsService } from '../../../../../services/new_job_capabilities_service'; -import { Field } from '../../../../../../../common/types/fields'; +import { isKeywordAndTextType } from '../../../../common/fields'; +import { Dictionary } from '../../../../../../../common/types/common'; import { ES_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/public'; import { defaultSearchQuery, ResultsSearchQuery, isResultsSearchBoolQuery, + LoadExploreDataArg, } from '../../../../common/analytics'; import { getDefaultFieldsFromJobCaps, + getDependentVar, getFlattenedFields, + getPredictedFieldName, DataFrameAnalyticsConfig, EsFieldName, INDEX_STATUS, - SEARCH_SIZE, - SearchQuery, } from '../../../../common'; import { SavedSearchQuery } from '../../../../../contexts/ml'; -interface LoadClassificationExploreDataArg { - direction: SortDirection; - filterByIsTraining?: boolean; - field: string; - searchQuery: SavedSearchQuery; - requiresKeyword?: boolean; - pageIndex?: number; - pageSize?: number; -} - export type TableItem = Record; +type Pagination = Pick; export interface UseExploreDataReturnType { errorMessage: string; - loadExploreData: (arg: LoadClassificationExploreDataArg) => void; - sortField: EsFieldName; - sortDirection: SortDirection; + fieldTypes: { [key: string]: ES_FIELD_TYPES }; + pagination: Pagination; + rowCount: number; + searchQuery: SavedSearchQuery; + selectedFields: EsFieldName[]; + setFilterByIsTraining: Dispatch>; + setPagination: Dispatch>; + setSearchQuery: Dispatch>; + setSelectedFields: Dispatch>; + setSortingColumns: Dispatch>; + sortingColumns: EuiDataGridSorting['columns']; status: INDEX_STATUS; + tableFields: string[]; tableItems: TableItem[]; } +type EsSorting = Dictionary<{ + order: 'asc' | 'desc'; +}>; + +// The types specified in `@types/elasticsearch` are out of date and still have `total: number`. +interface SearchResponse7 extends SearchResponse { + hits: SearchResponse['hits'] & { + total: { + value: number; + relation: string; + }; + }; +} + export const useExploreData = ( - jobConfig: DataFrameAnalyticsConfig | undefined, - needsDestIndexFields: boolean, - selectedFields: Field[], - setSelectedFields: React.Dispatch>, - setDocFields: React.Dispatch>, - setDepVarType: React.Dispatch> + jobConfig: DataFrameAnalyticsConfig, + needsDestIndexFields: boolean ): UseExploreDataReturnType => { const [errorMessage, setErrorMessage] = useState(''); const [status, setStatus] = useState(INDEX_STATUS.UNUSED); + + const [selectedFields, setSelectedFields] = useState([] as EsFieldName[]); + const [tableFields, setTableFields] = useState([]); const [tableItems, setTableItems] = useState([]); - const [sortField, setSortField] = useState(''); - const [sortDirection, setSortDirection] = useState(SORT_DIRECTION.ASC); + const [fieldTypes, setFieldTypes] = useState<{ [key: string]: ES_FIELD_TYPES }>({}); + const [rowCount, setRowCount] = useState(0); + + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 25 }); + const [searchQuery, setSearchQuery] = useState(defaultSearchQuery); + const [filterByIsTraining, setFilterByIsTraining] = useState(undefined); + const [sortingColumns, setSortingColumns] = useState([]); + + const predictedFieldName = getPredictedFieldName( + jobConfig.dest.results_field, + jobConfig.analysis + ); + const dependentVariable = getDependentVar(jobConfig.analysis); const getDefaultSelectedFields = () => { const { fields } = newJobCapsService; - if (selectedFields.length === 0 && jobConfig !== undefined) { - const { - selectedFields: defaultSelected, - docFields, - depVarType, - } = getDefaultFieldsFromJobCaps(fields, jobConfig, needsDestIndexFields); - - setDepVarType(depVarType); - setSelectedFields(defaultSelected); - setDocFields(docFields); + const { selectedFields: defaultSelected, docFields } = getDefaultFieldsFromJobCaps( + fields, + jobConfig, + needsDestIndexFields + ); + + const types: { [key: string]: ES_FIELD_TYPES } = {}; + const allFields: string[] = []; + + docFields.forEach(field => { + types[field.id] = field.type; + allFields.push(field.id); + }); + + setFieldTypes(types); + setSelectedFields(defaultSelected.map(field => field.id)); + setTableFields(allFields); } }; const loadExploreData = async ({ - field, - direction, - searchQuery, - requiresKeyword, - }: LoadClassificationExploreDataArg) => { + filterByIsTraining: isTraining, + searchQuery: incomingQuery, + }: LoadExploreDataArg) => { if (jobConfig !== undefined) { setErrorMessage(''); setStatus(INDEX_STATUS.LOADING); try { const resultsField = jobConfig.dest.results_field; - const searchQueryClone: ResultsSearchQuery = cloneDeep(searchQuery); + const searchQueryClone: ResultsSearchQuery = cloneDeep(incomingQuery); let query: ResultsSearchQuery; + const { pageIndex, pageSize } = pagination; + // If filterByIsTraining is defined - add that in to the final query + const trainingQuery = + isTraining !== undefined + ? { + term: { [`${resultsField}.is_training`]: { value: isTraining } }, + } + : undefined; - if (JSON.stringify(searchQuery) === JSON.stringify(defaultSearchQuery)) { - query = { + if (JSON.stringify(incomingQuery) === JSON.stringify(defaultSearchQuery)) { + const existsQuery = { exists: { field: resultsField, }, }; + + query = { + bool: { + must: [existsQuery], + }, + }; + + if (trainingQuery !== undefined && isResultsSearchBoolQuery(query)) { + query.bool.must.push(trainingQuery); + } } else if (isResultsSearchBoolQuery(searchQueryClone)) { if (searchQueryClone.bool.must === undefined) { searchQueryClone.bool.must = []; @@ -121,33 +166,37 @@ export const useExploreData = ( }, }); + if (trainingQuery !== undefined) { + searchQueryClone.bool.must.push(trainingQuery); + } + query = searchQueryClone; } else { query = searchQueryClone; } - const body: SearchQuery = { - query, - }; - - if (field !== undefined) { - body.sort = [ - { - [`${field}${requiresKeyword ? '.keyword' : ''}`]: { - order: direction, - }, - }, - ]; - } + const sort: EsSorting = sortingColumns + .map(column => { + const { id } = column; + column.id = isKeywordAndTextType(id) ? `${id}.keyword` : id; + return column; + }) + .reduce((s, column) => { + s[column.id] = { order: column.direction }; + return s; + }, {} as EsSorting); - const resp: SearchResponse = await ml.esSearch({ + const resp: SearchResponse7 = await ml.esSearch({ index: jobConfig.dest.index, - size: SEARCH_SIZE, - body, + body: { + query, + from: pageIndex * pageSize, + size: pageSize, + ...(Object.keys(sort).length > 0 ? { sort } : {}), + }, }); - setSortField(field); - setSortDirection(direction); + setRowCount(resp.hits.total.value); const docs = resp.hits.hits; @@ -199,10 +248,45 @@ export const useExploreData = ( }; useEffect(() => { - if (jobConfig !== undefined) { - getDefaultSelectedFields(); - } + getDefaultSelectedFields(); + }, [jobConfig && jobConfig.id]); + + // By default set sorting to descending on the prediction field (`_prediction`). + useEffect(() => { + const sortByField = isKeywordAndTextType(dependentVariable) + ? `${predictedFieldName}.keyword` + : predictedFieldName; + const direction = SORT_DIRECTION.DESC; + + setSortingColumns([{ id: sortByField, direction }]); }, [jobConfig && jobConfig.id]); - return { errorMessage, loadExploreData, sortField, sortDirection, status, tableItems }; + useEffect(() => { + loadExploreData({ filterByIsTraining, searchQuery }); + }, [ + filterByIsTraining, + jobConfig && jobConfig.id, + pagination, + searchQuery, + selectedFields, + sortingColumns, + ]); + + return { + errorMessage, + fieldTypes, + pagination, + searchQuery, + selectedFields, + rowCount, + setFilterByIsTraining, + setPagination, + setSelectedFields, + setSortingColumns, + setSearchQuery, + sortingColumns, + status, + tableItems, + tableFields, + }; }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts index c68fe5b2cbee86..978aafd10de11f 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/use_explore_data.ts @@ -29,7 +29,7 @@ import { Dictionary } from '../../../../../../../common/types/common'; import { isKeywordAndTextType } from '../../../../common/fields'; import { ES_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/public'; import { - LoadRegressionExploreDataArg, + LoadExploreDataArg, defaultSearchQuery, ResultsSearchQuery, isResultsSearchBoolQuery, @@ -120,7 +120,7 @@ export const useExploreData = ( const loadExploreData = async ({ filterByIsTraining: isTraining, searchQuery: incomingQuery, - }: LoadRegressionExploreDataArg) => { + }: LoadExploreDataArg) => { if (jobConfig !== undefined) { setErrorMessage(''); setStatus(INDEX_STATUS.LOADING); diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index 5e505757dd2aa3..42742f6efcb2a5 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -150,7 +150,7 @@ function getTimeseriesexplorerDefaultState() { }; } -const containerPadding = 24; +const containerPadding = 34; export class TimeSeriesExplorer extends React.Component { static propTypes = { diff --git a/x-pack/plugins/reporting/server/config/schema.ts b/x-pack/plugins/reporting/server/config/schema.ts index 67d70c1513c15d..402fddcb5e0146 100644 --- a/x-pack/plugins/reporting/server/config/schema.ts +++ b/x-pack/plugins/reporting/server/config/schema.ts @@ -114,6 +114,7 @@ const CaptureSchema = schema.object({ const CsvSchema = schema.object({ checkForFormulas: schema.boolean({ defaultValue: true }), + escapeFormulaValues: schema.boolean({ defaultValue: false }), enablePanelActionDownload: schema.boolean({ defaultValue: true }), maxSizeBytes: schema.number({ defaultValue: 1024 * 1024 * 10, // 10MB diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts index c207d075331b6e..cfba40fc225a24 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/rules/validate.ts @@ -9,8 +9,9 @@ import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import * as t from 'io-ts'; +import { formatErrors } from '../../../../utils/build_validation/format_errors'; +import { exactCheck } from '../../../../utils/build_validation/exact_check'; import { PartialAlert, FindResult } from '../../../../../../alerting/server'; -import { formatErrors } from '../schemas/response/utils'; import { isAlertType, IRuleSavedAttributesSavedObjectAttributes, @@ -19,7 +20,6 @@ import { import { OutputRuleAlertRest } from '../../types'; import { createBulkErrorObject, BulkError } from '../utils'; import { rulesSchema, RulesSchema } from '../schemas/response/rules_schema'; -import { exactCheck } from '../schemas/response/exact_check'; import { transformFindAlerts, transform, transformAlertToRule } from './utils'; import { findRulesSchema } from '../schemas/response/find_rules_schema'; import { RuleActions } from '../../rule_actions/types'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/__mocks__/utils.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/__mocks__/utils.ts index 21f18f9db55fba..fef6bcf42e49f0 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/__mocks__/utils.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/__mocks__/utils.ts @@ -4,32 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as t from 'io-ts'; -import { fold } from 'fp-ts/lib/Either'; import { RulesSchema } from '../rules_schema'; import { RulesBulkSchema } from '../rules_bulk_schema'; import { ErrorSchema } from '../error_schema'; import { FindRulesSchema } from '../find_rules_schema'; -import { formatErrors } from '../utils'; -import { pipe } from 'fp-ts/lib/pipeable'; - -interface Message { - errors: t.Errors; - schema: T | {}; -} - -const onLeft = (errors: t.Errors): Message => { - return { schema: {}, errors }; -}; - -const onRight = (schema: T): Message => { - return { - schema, - errors: [], - }; -}; - -export const foldLeftRight = fold(onLeft, onRight); export const ANCHOR_DATE = '2020-02-20T03:57:54.037Z'; @@ -127,18 +105,3 @@ export const getFindResponseSingle = (): FindRulesSchema => ({ total: 1, data: [getBaseResponsePayload()], }); - -/** - * Convenience utility to keep the error message handling within tests to be - * very concise. - * @param validation The validation to get the errors from - */ -export const getPaths = (validation: t.Validation): string[] => { - return pipe( - validation, - fold( - errors => formatErrors(errors), - () => ['no errors'] - ) - ); -}; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts index 0eda2a7a13d96f..fbd2382e2826d4 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/check_type_dependents.test.ts @@ -15,17 +15,13 @@ import { addQueryFields, addMlFields, } from './check_type_dependents'; -import { - foldLeftRight, - getBaseResponsePayload, - getPaths, - getMlRuleResponsePayload, -} from './__mocks__/utils'; +import { getBaseResponsePayload, getMlRuleResponsePayload } from './__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; -import { exactCheck } from './exact_check'; import { RulesSchema } from './rules_schema'; import { TypeAndTimelineOnly } from './type_timeline_only_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../utils/build_validation/exact_check'; describe('check_type_dependents', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts index 11d8b85f259206..6e159a792edb66 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/error_schema.test.ts @@ -7,10 +7,11 @@ import { left } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck } from './exact_check'; -import { foldLeftRight, getErrorPayload, getPaths } from './__mocks__/utils'; +import { getErrorPayload } from './__mocks__/utils'; import { errorSchema, ErrorSchema } from './error_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; +import { exactCheck } from '../../../../../utils/build_validation/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('error_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts index f5c1970ee8c55c..68b67db595d76d 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/find_rules_schema.test.ts @@ -5,17 +5,13 @@ */ import { findRulesSchema, FindRulesSchema } from './find_rules_schema'; -import { exactCheck } from './exact_check'; import { pipe } from 'fp-ts/lib/pipeable'; -import { - foldLeftRight, - getFindResponseSingle, - getBaseResponsePayload, - getPaths, -} from './__mocks__/utils'; +import { getFindResponseSingle, getBaseResponsePayload } from './__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; import { RulesSchema } from './rules_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; +import { getPaths, foldLeftRight } from '../../../../../utils/build_validation/__mocks__/utils'; +import { exactCheck } from '../../../../../utils/build_validation/exact_check'; describe('find_rules_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts index ce4bbf420a634b..b0b863ebbbc0b2 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/import_rules_schema.test.ts @@ -4,13 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { exactCheck } from './exact_check'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from './__mocks__/utils'; -import { left } from 'fp-ts/lib/Either'; +import { left, Either } from 'fp-ts/lib/Either'; import { ImportRulesSchema, importRulesSchema } from './import_rules_schema'; import { ErrorSchema } from './error_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; +import { exactCheck } from '../../../../../utils/build_validation/exact_check'; +import { getPaths, foldLeftRight } from '../../../../../utils/build_validation/__mocks__/utils'; +import { Errors } from 'io-ts'; describe('import_rules_schema', () => { beforeAll(() => { @@ -79,13 +80,31 @@ describe('import_rules_schema', () => { }); test('it should NOT validate a success that is not a boolean', () => { + type UnsafeCastForTest = Either< + Errors, + { + success: string; + success_count: number; + errors: Array< + { + id?: string | undefined; + rule_id?: string | undefined; + } & { + error: { + status_code: number; + message: string; + }; + } + >; + } + >; const payload: Omit & { success: string } = { success: 'hello', success_count: 0, errors: [], }; const decoded = importRulesSchema.decode(payload); - const checked = exactCheck(payload, decoded); + const checked = exactCheck(payload, decoded as UnsafeCastForTest); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual(['Invalid value "hello" supplied to "success"']); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts index 46667826416e1f..827167c63fd58e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_schema.test.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { exactCheck } from './exact_check'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from './__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; import { PrePackagedRulesSchema, prePackagedRulesSchema } from './prepackaged_rules_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; +import { exactCheck } from '../../../../../utils/build_validation/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('prepackaged_rules_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts index 1c270ff402f75e..a864667583c0a8 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/prepackaged_rules_status_schema.test.ts @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { exactCheck } from './exact_check'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from './__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; import { PrePackagedRulesStatusSchema, prePackagedRulesStatusSchema, } from './prepackaged_rules_status_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; +import { exactCheck } from '../../../../../utils/build_validation/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('prepackaged_rules_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts index 8dc97d727c4d1d..9a7cf5e2c2871c 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_bulk_schema.test.ts @@ -7,17 +7,13 @@ import { left } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck } from './exact_check'; -import { - foldLeftRight, - getBaseResponsePayload, - getErrorPayload, - getPaths, -} from './__mocks__/utils'; +import { getBaseResponsePayload, getErrorPayload } from './__mocks__/utils'; import { RulesBulkSchema, rulesBulkSchema } from './rules_bulk_schema'; import { RulesSchema } from './rules_schema'; import { ErrorSchema } from './error_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; +import { exactCheck } from '../../../../../utils/build_validation/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('prepackaged_rule_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts index 4bfc51c1a66aab..82a6682e6461f9 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/rules_schema.test.ts @@ -7,10 +7,11 @@ import { left } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck } from './exact_check'; import { rulesSchema, RulesSchema, removeList } from './rules_schema'; -import { foldLeftRight, getBaseResponsePayload, getPaths } from './__mocks__/utils'; +import { getBaseResponsePayload } from './__mocks__/utils'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; +import { exactCheck } from '../../../../../utils/build_validation/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; export const ANCHOR_DATE = '2020-02-20T03:57:54.037Z'; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts index 68a3c8b3038239..85fb124464487a 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/type_timeline_only_schema.test.ts @@ -7,10 +7,10 @@ import { left } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; -import { exactCheck } from './exact_check'; -import { foldLeftRight, getPaths } from './__mocks__/utils'; import { TypeAndTimelineOnly, typeAndTimelineOnlySchema } from './type_timeline_only_schema'; import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; +import { exactCheck } from '../../../../../utils/build_validation/exact_check'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('prepackaged_rule_schema', () => { beforeAll(() => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts index fbafaf7f52ecb6..ff62ea4443f3f6 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/iso_date_string.test.ts @@ -6,8 +6,8 @@ import { IsoDateString } from './iso_date_string'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from '../response/__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('ios_date_string', () => { test('it should validate a iso string', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts index e8a9c7b0886a19..2a97c8a4a143e8 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts @@ -6,8 +6,8 @@ import { ListsDefaultArray } from './lists_default_array'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from '../response/__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('lists_default_array', () => { test('it should validate an empty array', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts index bc17303f242038..d6f21681df88fe 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/positive_integer_greater_than_zero.test.ts @@ -6,8 +6,8 @@ import { PositiveIntegerGreaterThanZero } from './positive_integer_greater_than_zero'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from '../response/__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('positive_integer_greater_than_zero', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts index cee451279663a7..26441745a7f29e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/postive_integer.test.ts @@ -6,8 +6,8 @@ import { PositiveInteger } from './positive_integer'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from '../response/__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('positive_integer_greater_than_zero', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts index 3ae8415b4f170f..76f722274ce23a 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/references_default_array.test.ts @@ -6,8 +6,8 @@ import { ReferencesDefaultArray } from './references_default_array'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from '../response/__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('references_default_array', () => { test('it should validate an empty array', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts index ab3f80944489fe..76e3445358dd9b 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/risk_score.test.ts @@ -6,8 +6,8 @@ import { RiskScore } from './risk_score'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from '../response/__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('risk_score', () => { test('it should validate a positive number', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts index 342e6f2db2e16d..7b68dbcef2d7ed 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/types/uuid.test.ts @@ -6,8 +6,8 @@ import { UUID } from './uuid'; import { pipe } from 'fp-ts/lib/pipeable'; -import { foldLeftRight, getPaths } from '../response/__mocks__/utils'; import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../../../../../utils/build_validation/__mocks__/utils'; describe('uuid', () => { test('it should validate a uuid', () => { diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts index 8a5da8e8597216..731fffcac1bb0e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -86,7 +86,7 @@ export const sampleDocNoSortId = (someUuid: string = sampleIdGuid): SignalSource _id: someUuid, _source: { someKey: 'someValue', - '@timestamp': 'someTimeStamp', + '@timestamp': '2020-04-20T21:27:45+0000', }, }); @@ -97,7 +97,7 @@ export const sampleDocNoSortIdNoVersion = (someUuid: string = sampleIdGuid): Sig _id: someUuid, _source: { someKey: 'someValue', - '@timestamp': 'someTimeStamp', + '@timestamp': '2020-04-20T21:27:45+0000', }, }); @@ -109,7 +109,7 @@ export const sampleDocWithSortId = (someUuid: string = sampleIdGuid): SignalSour _id: someUuid, _source: { someKey: 'someValue', - '@timestamp': 'someTimeStamp', + '@timestamp': '2020-04-20T21:27:45+0000', }, sort: ['1234567891111'], }); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts index bbd01cfaafc624..df9d282b71e5e1 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts @@ -59,7 +59,7 @@ describe('buildBulkBody', () => { depth: 1, }, ], - original_time: 'someTimeStamp', + original_time: '2020-04-20T21:27:45+0000', status: 'open', rule: { actions: [], @@ -185,7 +185,7 @@ describe('buildBulkBody', () => { depth: 1, }, ], - original_time: 'someTimeStamp', + original_time: '2020-04-20T21:27:45+0000', status: 'open', rule: { actions: [], @@ -309,7 +309,7 @@ describe('buildBulkBody', () => { depth: 1, }, ], - original_time: 'someTimeStamp', + original_time: '2020-04-20T21:27:45+0000', status: 'open', rule: { actions: [], @@ -426,7 +426,7 @@ describe('buildBulkBody', () => { depth: 1, }, ], - original_time: 'someTimeStamp', + original_time: '2020-04-20T21:27:45+0000', status: 'open', rule: { actions: [], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts index 0a50c33fbbfe4d..f3f4ab60e4db6c 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/build_signal.test.ts @@ -41,7 +41,7 @@ describe('buildSignal', () => { depth: 1, }, ], - original_time: 'someTimeStamp', + original_time: '2020-04-20T21:27:45+0000', status: 'open', rule: { created_by: 'elastic', @@ -101,7 +101,7 @@ describe('buildSignal', () => { depth: 1, }, ], - original_time: 'someTimeStamp', + original_time: '2020-04-20T21:27:45+0000', original_event: { action: 'socket_opened', dataset: 'socket', @@ -173,7 +173,7 @@ describe('buildSignal', () => { depth: 1, }, ], - original_time: 'someTimeStamp', + original_time: '2020-04-20T21:27:45+0000', original_event: { action: 'socket_opened', dataset: 'socket', diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index cec011ae8c4455..2cb23b05f6a9b6 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -30,7 +30,7 @@ describe('searchAfterAndBulkCreate', () => { test('if successful with empty search results', async () => { const sampleParams = sampleRuleAlertParams(); - const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ someResult: sampleEmptyDocSearchResults(), ruleParams: sampleParams, services: mockService, @@ -55,6 +55,7 @@ describe('searchAfterAndBulkCreate', () => { expect(mockService.callCluster).toHaveBeenCalledTimes(0); expect(success).toEqual(true); expect(createdSignalsCount).toEqual(0); + expect(lastLookBackDate).toBeNull(); }); test('if successful iteration of while loop with maxDocs', async () => { @@ -105,7 +106,7 @@ describe('searchAfterAndBulkCreate', () => { }, ], }); - const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(3, 1, someGuids.slice(6, 9)), ruleParams: sampleParams, services: mockService, @@ -130,13 +131,14 @@ describe('searchAfterAndBulkCreate', () => { expect(mockService.callCluster).toHaveBeenCalledTimes(5); expect(success).toEqual(true); expect(createdSignalsCount).toEqual(3); + expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); test('if unsuccessful first bulk create', async () => { const someGuids = Array.from({ length: 4 }).map(x => uuid.v4()); const sampleParams = sampleRuleAlertParams(10); mockService.callCluster.mockResolvedValue(sampleBulkCreateDuplicateResult); - const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(4, 1, someGuids), ruleParams: sampleParams, services: mockService, @@ -161,6 +163,7 @@ describe('searchAfterAndBulkCreate', () => { expect(mockLogger.error).toHaveBeenCalled(); expect(success).toEqual(false); expect(createdSignalsCount).toEqual(1); + expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); test('if unsuccessful iteration of searchAfterAndBulkCreate due to empty sort ids', async () => { @@ -179,7 +182,7 @@ describe('searchAfterAndBulkCreate', () => { }, ], }); - const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ someResult: sampleDocSearchResultsNoSortId(), ruleParams: sampleParams, services: mockService, @@ -204,6 +207,7 @@ describe('searchAfterAndBulkCreate', () => { expect(mockLogger.error).toHaveBeenCalled(); expect(success).toEqual(false); expect(createdSignalsCount).toEqual(1); + expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); test('if unsuccessful iteration of searchAfterAndBulkCreate due to empty sort ids and 0 total hits', async () => { @@ -222,7 +226,7 @@ describe('searchAfterAndBulkCreate', () => { }, ], }); - const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ someResult: sampleDocSearchResultsNoSortIdNoHits(), ruleParams: sampleParams, services: mockService, @@ -246,6 +250,7 @@ describe('searchAfterAndBulkCreate', () => { }); expect(success).toEqual(true); expect(createdSignalsCount).toEqual(1); + expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); test('if successful iteration of while loop with maxDocs and search after returns results with no sort ids', async () => { @@ -267,7 +272,7 @@ describe('searchAfterAndBulkCreate', () => { ], }) .mockResolvedValueOnce(sampleDocSearchResultsNoSortId()); - const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(4, 1, someGuids), ruleParams: sampleParams, services: mockService, @@ -291,6 +296,7 @@ describe('searchAfterAndBulkCreate', () => { }); expect(success).toEqual(true); expect(createdSignalsCount).toEqual(1); + expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); test('if successful iteration of while loop with maxDocs and search after returns empty results with no sort ids', async () => { @@ -312,7 +318,7 @@ describe('searchAfterAndBulkCreate', () => { ], }) .mockResolvedValueOnce(sampleEmptyDocSearchResults()); - const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(4, 1, someGuids), ruleParams: sampleParams, services: mockService, @@ -336,6 +342,7 @@ describe('searchAfterAndBulkCreate', () => { }); expect(success).toEqual(true); expect(createdSignalsCount).toEqual(1); + expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); test('if returns false when singleSearchAfter throws an exception', async () => { @@ -359,7 +366,7 @@ describe('searchAfterAndBulkCreate', () => { .mockImplementation(() => { throw Error('Fake Error'); }); - const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(4, 1, someGuids), ruleParams: sampleParams, services: mockService, @@ -383,5 +390,6 @@ describe('searchAfterAndBulkCreate', () => { }); expect(success).toEqual(false); expect(createdSignalsCount).toEqual(1); + expect(lastLookBackDate).toEqual(new Date('2020-04-20T21:27:45+0000')); }); }); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts index e287e33295c896..acf3e9bfb055c9 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -98,13 +98,15 @@ export const searchAfterAndBulkCreate = async ({ tags, throttle, }); - toReturn.lastLookBackDate = - someResult.hits.hits.length > 0 - ? new Date(someResult.hits.hits[someResult.hits.hits.length - 1]?._source['@timestamp']) - : null; - if (createdItemsCount) { + + if (createdItemsCount > 0) { toReturn.createdSignalsCount = createdItemsCount; + toReturn.lastLookBackDate = + someResult.hits.hits.length > 0 + ? new Date(someResult.hits.hits[someResult.hits.hits.length - 1]?._source['@timestamp']) + : null; } + if (bulkCreateDuration) { toReturn.bulkCreateTimes.push(bulkCreateDuration); } diff --git a/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts b/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts index 51cc0f449b17a2..6f3cc6e708fce1 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts +++ b/x-pack/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts @@ -300,7 +300,7 @@ describe('singleBulkCreate', () => { _id: 'e1e08ddc-5e37-49ff-a258-5393aa44435a', _source: { someKey: 'someValue', - '@timestamp': 'someTimeStamp', + '@timestamp': '2020-04-20T21:27:45+0000', signal: { parent: { rule: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', @@ -334,7 +334,7 @@ describe('singleBulkCreate', () => { test('filter duplicate rules will return back search responses if they do not have a signal and will NOT filter the source out', () => { const ancestors = sampleDocWithAncestors(); - ancestors.hits.hits[0]._source = { '@timestamp': 'some timestamp' }; + ancestors.hits.hits[0]._source = { '@timestamp': '2020-04-20T21:27:45+0000' }; const filtered = filterDuplicateRules('04128c15-0d1b-4716-a4c5-46997ac7f3bd', ancestors); expect(filtered).toEqual([ { @@ -343,7 +343,7 @@ describe('singleBulkCreate', () => { _score: 100, _version: 1, _id: 'e1e08ddc-5e37-49ff-a258-5393aa44435a', - _source: { '@timestamp': 'some timestamp' }, + _source: { '@timestamp': '2020-04-20T21:27:45+0000' }, }, ]); }); diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/export_timelines_route.test.ts b/x-pack/plugins/siem/server/lib/timeline/routes/export_timelines_route.test.ts index 47ca25e16bd50d..2bccb7c393837b 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/export_timelines_route.test.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/export_timelines_route.test.ts @@ -85,7 +85,7 @@ describe('export timelines', () => { const result = server.validate(request); expect(result.badRequest.mock.calls[0][0]).toEqual( - 'Invalid value undefined supplied to : { ids: Array }/ids: Array' + 'Invalid value "undefined" supplied to "ids"' ); }); @@ -98,11 +98,7 @@ describe('export timelines', () => { const result = server.validate(request); expect(result.badRequest.mock.calls[1][0]).toEqual( - [ - 'Invalid value undefined supplied to : { file_name: string, exclude_export_details: ("true" | "false") }/file_name: string', - 'Invalid value undefined supplied to : { file_name: string, exclude_export_details: ("true" | "false") }/exclude_export_details: ("true" | "false")/0: "true"', - 'Invalid value undefined supplied to : { file_name: string, exclude_export_details: ("true" | "false") }/exclude_export_details: ("true" | "false")/1: "false"', - ].join('\n') + 'Invalid value "undefined" supplied to "file_name",Invalid value "undefined" supplied to "exclude_export_details",Invalid value "undefined" supplied to "exclude_export_details"' ); }); }); diff --git a/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.test.ts b/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.test.ts index 3931bf0e5bea51..9f41943cfa27f7 100644 --- a/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.test.ts +++ b/x-pack/plugins/siem/server/lib/timeline/routes/import_timelines_route.test.ts @@ -332,10 +332,7 @@ describe('import timelines', () => { const result = server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - [ - 'Invalid value undefined supplied to : { file: (ReadableRt & { hapi: { filename: string } }) }/file: (ReadableRt & { hapi: { filename: string } })/0: ReadableRt', - 'Invalid value undefined supplied to : { file: (ReadableRt & { hapi: { filename: string } }) }/file: (ReadableRt & { hapi: { filename: string } })/1: { hapi: { filename: string } }', - ].join('\n') + 'Invalid value "undefined" supplied to "file",Invalid value "undefined" supplied to "file"' ); }); }); diff --git a/x-pack/plugins/siem/server/utils/build_validation/__mocks__/utils.ts b/x-pack/plugins/siem/server/utils/build_validation/__mocks__/utils.ts new file mode 100644 index 00000000000000..578972dda5aeff --- /dev/null +++ b/x-pack/plugins/siem/server/utils/build_validation/__mocks__/utils.ts @@ -0,0 +1,43 @@ +/* + * 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 * as t from 'io-ts'; +import { fold } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { formatErrors } from '../format_errors'; + +interface Message { + errors: t.Errors; + schema: T | {}; +} + +const onLeft = (errors: t.Errors): Message => { + return { schema: {}, errors }; +}; + +const onRight = (schema: T): Message => { + return { + schema, + errors: [], + }; +}; + +export const foldLeftRight = fold(onLeft, onRight); + +/** + * Convenience utility to keep the error message handling within tests to be + * very concise. + * @param validation The validation to get the errors from + */ +export const getPaths = (validation: t.Validation): string[] => { + return pipe( + validation, + fold( + errors => formatErrors(errors), + () => ['no errors'] + ) + ); +}; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/exact_check.test.ts b/x-pack/plugins/siem/server/utils/build_validation/exact_check.test.ts similarity index 94% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/exact_check.test.ts rename to x-pack/plugins/siem/server/utils/build_validation/exact_check.test.ts index cae4365d06856f..1e70deaeed4386 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/exact_check.test.ts +++ b/x-pack/plugins/siem/server/utils/build_validation/exact_check.test.ts @@ -5,22 +5,13 @@ */ import * as t from 'io-ts'; -import { left, right } from 'fp-ts/lib/Either'; +import { left, right, Either } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import { foldLeftRight, getPaths } from './__mocks__/utils'; import { exactCheck, findDifferencesRecursive } from './exact_check'; -import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; describe('exact_check', () => { - beforeAll(() => { - setFeatureFlagsForTestsOnly(); - }); - - afterAll(() => { - unSetFeatureFlagsForTestsOnly(); - }); - test('it returns an error if given extra object properties', () => { const someType = t.exact( t.type({ @@ -36,14 +27,22 @@ describe('exact_check', () => { }); test('it returns an error if the data type is not as expected', () => { + type UnsafeCastForTest = Either< + t.Errors, + { + a: number; + } + >; + const someType = t.exact( t.type({ a: t.string, }) ); + const payload = { a: 1 }; const decoded = someType.decode(payload); - const checked = exactCheck(payload, decoded); + const checked = exactCheck(payload, decoded as UnsafeCastForTest); const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual(['Invalid value "1" supplied to "a"']); expect(message.schema).toEqual({}); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/exact_check.ts b/x-pack/plugins/siem/server/utils/build_validation/exact_check.ts similarity index 59% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/exact_check.ts rename to x-pack/plugins/siem/server/utils/build_validation/exact_check.ts index 6fa04729501892..9484765f9973df 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/exact_check.ts +++ b/x-pack/plugins/siem/server/utils/build_validation/exact_check.ts @@ -25,10 +25,7 @@ import { isObject, get } from 'lodash/fp'; * @param decoded The decoded either which has either an existing error or the * decoded object which could have additional keys stripped from it. */ -export const exactCheck = ( - original: object, - decoded: Either -): Either => { +export const exactCheck = (original: T, decoded: Either): Either => { const onLeft = (errors: t.Errors): Either => left(errors); const onRight = (decodedValue: T): Either => { const differences = findDifferencesRecursive(original, decodedValue); @@ -47,7 +44,7 @@ export const exactCheck = ( return pipe(decoded, fold(onLeft, onRight)); }; -export const findDifferencesRecursive = (original: object, decodedValue: T): string[] => { +export const findDifferencesRecursive = (original: T, decodedValue: T): string[] => { if (decodedValue == null) { try { // It is null and painful when the original contains an object or an array @@ -56,29 +53,33 @@ export const findDifferencesRecursive = (original: object, decodedValue: T): } catch (err) { return ['circular reference']; } + } else if (typeof original !== 'object' || original == null) { + // We are not an object or null so do not report differences + return []; + } else { + const decodedKeys = Object.keys(decodedValue); + const differences = Object.keys(original).flatMap(originalKey => { + const foundKey = decodedKeys.some(key => key === originalKey); + const topLevelKey = foundKey ? [] : [originalKey]; + // I use lodash to cheat and get an any (not going to lie ;-)) + const valueObjectOrArrayOriginal = get(originalKey, original); + const valueObjectOrArrayDecoded = get(originalKey, decodedValue); + if (isObject(valueObjectOrArrayOriginal)) { + return [ + ...topLevelKey, + ...findDifferencesRecursive(valueObjectOrArrayOriginal, valueObjectOrArrayDecoded), + ]; + } else if (Array.isArray(valueObjectOrArrayOriginal)) { + return [ + ...topLevelKey, + ...valueObjectOrArrayOriginal.flatMap((arrayElement, index) => + findDifferencesRecursive(arrayElement, get(index, valueObjectOrArrayDecoded)) + ), + ]; + } else { + return topLevelKey; + } + }); + return differences; } - const decodedKeys = Object.keys(decodedValue); - const differences = Object.keys(original).flatMap(originalKey => { - const foundKey = decodedKeys.some(key => key === originalKey); - const topLevelKey = foundKey ? [] : [originalKey]; - // I use lodash to cheat and get an any (not going to lie ;-)) - const valueObjectOrArrayOriginal = get(originalKey, original); - const valueObjectOrArrayDecoded = get(originalKey, decodedValue); - if (isObject(valueObjectOrArrayOriginal)) { - return [ - ...topLevelKey, - ...findDifferencesRecursive(valueObjectOrArrayOriginal, valueObjectOrArrayDecoded), - ]; - } else if (Array.isArray(valueObjectOrArrayOriginal)) { - return [ - ...topLevelKey, - ...valueObjectOrArrayOriginal.flatMap((arrayElement, index) => - findDifferencesRecursive(arrayElement, get(index, valueObjectOrArrayDecoded)) - ), - ]; - } else { - return topLevelKey; - } - }); - return differences; }; diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/utils.test.ts b/x-pack/plugins/siem/server/utils/build_validation/format_errors.test.ts similarity index 94% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/utils.test.ts rename to x-pack/plugins/siem/server/utils/build_validation/format_errors.test.ts index c1eb32be4895c2..f9dd9e76a1d9c6 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/utils.test.ts +++ b/x-pack/plugins/siem/server/utils/build_validation/format_errors.test.ts @@ -5,18 +5,9 @@ */ import * as t from 'io-ts'; -import { formatErrors } from './utils'; -import { setFeatureFlagsForTestsOnly, unSetFeatureFlagsForTestsOnly } from '../../../feature_flags'; +import { formatErrors } from './format_errors'; describe('utils', () => { - beforeAll(() => { - setFeatureFlagsForTestsOnly(); - }); - - afterAll(() => { - unSetFeatureFlagsForTestsOnly(); - }); - test('returns an empty error message string if there are no errors', () => { const errors: t.Errors = []; const output = formatErrors(errors); diff --git a/x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/utils.ts b/x-pack/plugins/siem/server/utils/build_validation/format_errors.ts similarity index 100% rename from x-pack/plugins/siem/server/lib/detection_engine/routes/schemas/response/utils.ts rename to x-pack/plugins/siem/server/utils/build_validation/format_errors.ts diff --git a/x-pack/plugins/siem/server/utils/build_validation/route_validation.test.ts b/x-pack/plugins/siem/server/utils/build_validation/route_validation.test.ts index d17a8457ff81be..866dbe9480ca59 100644 --- a/x-pack/plugins/siem/server/utils/build_validation/route_validation.test.ts +++ b/x-pack/plugins/siem/server/utils/build_validation/route_validation.test.ts @@ -9,9 +9,33 @@ import * as rt from 'io-ts'; import { RouteValidationResultFactory } from '../../../../../../src/core/server/http'; describe('buildRouteValidation', () => { - const schema = rt.type({ - ids: rt.array(rt.string), - }); + const schema = rt.exact( + rt.type({ + ids: rt.array(rt.string), + }) + ); + type Schema = rt.TypeOf; + + /** + * If your schema is using exact all the way down then the validation will + * catch any additional keys that should not be present within the validation + * when the route_validation uses the exact check. + */ + const deepSchema = rt.exact( + rt.type({ + topLevel: rt.exact( + rt.type({ + secondLevel: rt.exact( + rt.type({ + thirdLevel: rt.string, + }) + ), + }) + ), + }) + ); + type DeepSchema = rt.TypeOf; + const validationResult: RouteValidationResultFactory = { ok: jest.fn().mockImplementation(validatedInput => validatedInput), badRequest: jest.fn().mockImplementation(e => e), @@ -22,18 +46,41 @@ describe('buildRouteValidation', () => { }); test('return validation error', () => { - const input = { id: 'someId' }; + const input: Omit & { id: string } = { id: 'someId' }; const result = buildRouteValidation(schema)(input, validationResult); - expect(result).toEqual( - 'Invalid value undefined supplied to : { ids: Array }/ids: Array' - ); + expect(result).toEqual('Invalid value "undefined" supplied to "ids"'); }); test('return validated input', () => { - const input = { ids: ['someId'] }; + const input: Schema = { ids: ['someId'] }; const result = buildRouteValidation(schema)(input, validationResult); expect(result).toEqual(input); }); + + test('returns validation error if given extra keys on input for an array', () => { + const input: Schema & { somethingExtra: string } = { + ids: ['someId'], + somethingExtra: 'hello', + }; + const result = buildRouteValidation(schema)(input, validationResult); + expect(result).toEqual('invalid keys "somethingExtra"'); + }); + + test('return validation input for a deep 3rd level object', () => { + const input: DeepSchema = { topLevel: { secondLevel: { thirdLevel: 'hello' } } }; + const result = buildRouteValidation(deepSchema)(input, validationResult); + expect(result).toEqual(input); + }); + + test('return validation error for a deep 3rd level object that has an extra key value of "somethingElse"', () => { + const input: DeepSchema & { + topLevel: { secondLevel: { thirdLevel: string; somethingElse: string } }; + } = { + topLevel: { secondLevel: { thirdLevel: 'hello', somethingElse: 'extraKey' } }, + }; + const result = buildRouteValidation(deepSchema)(input, validationResult); + expect(result).toEqual('invalid keys "somethingElse"'); + }); }); diff --git a/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts b/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts index bfcd0998fe6900..30b95dcfa94ee3 100644 --- a/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts +++ b/x-pack/plugins/siem/server/utils/build_validation/route_validation.ts @@ -7,12 +7,13 @@ import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import * as rt from 'io-ts'; -import { failure } from 'io-ts/lib/PathReporter'; import { RouteValidationFunction, RouteValidationResultFactory, RouteValidationError, } from '../../../../../../src/core/server'; +import { exactCheck } from './exact_check'; +import { formatErrors } from './format_errors'; type RequestValidationResult = | { @@ -32,8 +33,9 @@ export const buildRouteValidation = >( ) => pipe( schema.decode(inputValue), + decoded => exactCheck(inputValue, decoded), fold>( - (errors: rt.Errors) => validationResult.badRequest(failure(errors).join('\n')), + (errors: rt.Errors) => validationResult.badRequest(formatErrors(errors).join()), (validatedInput: A) => validationResult.ok(validatedInput) ) ); diff --git a/x-pack/plugins/snapshot_restore/common/types/index.ts b/x-pack/plugins/snapshot_restore/common/types/index.ts index 5cb3839fa9e01d..d52584ca737a2a 100644 --- a/x-pack/plugins/snapshot_restore/common/types/index.ts +++ b/x-pack/plugins/snapshot_restore/common/types/index.ts @@ -8,4 +8,3 @@ export * from './repository'; export * from './snapshot'; export * from './restore'; export * from './policy'; -export * from './privileges'; diff --git a/x-pack/plugins/snapshot_restore/common/types/privileges.ts b/x-pack/plugins/snapshot_restore/common/types/privileges.ts deleted file mode 100644 index bf710b8225599e..00000000000000 --- a/x-pack/plugins/snapshot_restore/common/types/privileges.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export interface MissingPrivileges { - [key: string]: string[] | undefined; -} - -export interface Privileges { - hasAllPrivileges: boolean; - missingPrivileges: MissingPrivileges; -} diff --git a/x-pack/plugins/snapshot_restore/public/application/app.tsx b/x-pack/plugins/snapshot_restore/public/application/app.tsx index 77ef697814b2c4..350d8aec711edf 100644 --- a/x-pack/plugins/snapshot_restore/public/application/app.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/app.tsx @@ -4,13 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React from 'react'; import { Redirect, Route, Switch } from 'react-router-dom'; import { EuiPageContent } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { APP_REQUIRED_CLUSTER_PRIVILEGES } from '../../common/constants'; -import { SectionLoading, SectionError } from './components'; +import { APP_REQUIRED_CLUSTER_PRIVILEGES } from '../../common'; +import { + useAuthorizationContext, + SectionError, + WithPrivileges, + NotAuthorizedSection, +} from '../shared_imports'; +import { SectionLoading } from './components'; import { BASE_PATH, DEFAULT_SECTION, Section } from './constants'; import { RepositoryAdd, @@ -21,11 +27,10 @@ import { PolicyEdit, } from './sections'; import { useConfig } from './app_context'; -import { AuthorizationContext, WithPrivileges, NotAuthorizedSection } from './lib/authorization'; export const App: React.FunctionComponent = () => { const { slm_ui: slmUi } = useConfig(); - const { apiError } = useContext(AuthorizationContext); + const { apiError } = useAuthorizationContext(); const sections: Section[] = ['repositories', 'snapshots', 'restore_status']; diff --git a/x-pack/plugins/snapshot_restore/public/application/app_providers.tsx b/x-pack/plugins/snapshot_restore/public/application/app_providers.tsx index e2732c0051337a..3ca25b7d32dba7 100644 --- a/x-pack/plugins/snapshot_restore/public/application/app_providers.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/app_providers.tsx @@ -5,8 +5,8 @@ */ import React from 'react'; -import { API_BASE_PATH } from '../../common/constants'; -import { AuthorizationProvider } from './lib/authorization'; +import { API_BASE_PATH } from '../../common'; +import { AuthorizationProvider } from '../shared_imports'; import { AppContextProvider, AppDependencies } from './app_context'; interface Props { @@ -18,10 +18,11 @@ export const AppProviders = ({ appDependencies, children }: Props) => { const { core } = appDependencies; const { i18n: { Context: I18nContext }, + http, } = core; return ( - + {children} diff --git a/x-pack/plugins/snapshot_restore/public/application/components/index.ts b/x-pack/plugins/snapshot_restore/public/application/components/index.ts index a7038ebd715785..f5bb8923898702 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/index.ts +++ b/x-pack/plugins/snapshot_restore/public/application/components/index.ts @@ -10,7 +10,6 @@ export { RepositoryDeleteProvider } from './repository_delete_provider'; export { RepositoryForm } from './repository_form'; export { RepositoryVerificationBadge } from './repository_verification_badge'; export { RepositoryTypeLogo } from './repository_type_logo'; -export { SectionError, Error } from './section_error'; export { SectionLoading } from './section_loading'; export { SnapshotDeleteProvider } from './snapshot_delete_provider'; export { RestoreSnapshotForm } from './restore_snapshot_form'; diff --git a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx index f2d4e2bd74598f..105f0601e3dfbb 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/policy_form/steps/step_logistics.tsx @@ -21,13 +21,13 @@ import { } from '@elastic/eui'; import { Repository } from '../../../../../common/types'; -import { CronEditor } from '../../../../shared_imports'; +import { CronEditor, SectionError } from '../../../../shared_imports'; import { useServices } from '../../../app_context'; import { DEFAULT_POLICY_SCHEDULE, DEFAULT_POLICY_FREQUENCY } from '../../../constants'; import { useLoadRepositories } from '../../../services/http'; import { linkToAddRepository } from '../../../services/navigation'; import { documentationLinksService } from '../../../services/documentation'; -import { SectionLoading, SectionError } from '../../'; +import { SectionLoading } from '../../'; import { StepProps } from './'; export const PolicyStepLogistics: React.FunctionComponent = ({ diff --git a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/step_one.tsx b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/step_one.tsx index 3b4c9d595b9f28..34bc06343a780a 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/step_one.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/step_one.tsx @@ -23,13 +23,14 @@ import { } from '@elastic/eui'; import { Repository, RepositoryType, EmptyRepository } from '../../../../common/types'; -import { REPOSITORY_TYPES } from '../../../../common/constants'; +import { REPOSITORY_TYPES } from '../../../../common'; +import { SectionError, Error } from '../../../shared_imports'; import { documentationLinksService } from '../../services/documentation'; import { useLoadRepositoryTypes } from '../../services/http'; import { textService } from '../../services/text'; import { RepositoryValidation } from '../../services/validation'; -import { SectionError, SectionLoading, RepositoryTypeLogo, Error } from '../'; +import { SectionLoading, RepositoryTypeLogo } from '../'; interface Props { repository: Repository | EmptyRepository; diff --git a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/index.tsx b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/index.tsx index 75295a1205cef9..1e54868397a6da 100644 --- a/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/index.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/components/repository_form/type_settings/index.tsx @@ -6,11 +6,11 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { REPOSITORY_TYPES } from '../../../../../common/constants'; +import { REPOSITORY_TYPES } from '../../../../../common'; import { Repository, RepositoryType, EmptyRepository } from '../../../../../common/types'; +import { SectionError } from '../../../../shared_imports'; import { useServices } from '../../../app_context'; import { RepositorySettingsValidation } from '../../../services/validation'; -import { SectionError } from '../../index'; import { AzureSettings } from './azure_settings'; import { FSSettings } from './fs_settings'; diff --git a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/authorization_provider.tsx b/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/authorization_provider.tsx deleted file mode 100644 index d32fe29cc1dfa4..00000000000000 --- a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/authorization_provider.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { createContext } from 'react'; -import { useRequest } from '../../../services/http/use_request'; -import { Privileges } from '../../../../../common/types'; -import { Error } from '../../../components/section_error'; - -interface Authorization { - isLoading: boolean; - apiError: Error | null; - privileges: Privileges; -} - -const initialValue: Authorization = { - isLoading: true, - apiError: null, - privileges: { - hasAllPrivileges: true, - missingPrivileges: {}, - }, -}; - -export const AuthorizationContext = createContext(initialValue); - -interface Props { - privilegesEndpoint: string; - children: React.ReactNode; -} - -export const AuthorizationProvider = ({ privilegesEndpoint, children }: Props) => { - const { isLoading, error, data: privilegesData } = useRequest({ - path: privilegesEndpoint, - method: 'get', - }); - - const value = { - isLoading, - privileges: isLoading ? { hasAllPrivileges: true, missingPrivileges: {} } : privilegesData, - apiError: error ? error : null, - } as Authorization; - - return {children}; -}; diff --git a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/index.ts b/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/index.ts deleted file mode 100644 index ac77aa52686602..00000000000000 --- a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { AuthorizationProvider, AuthorizationContext } from './authorization_provider'; - -export { WithPrivileges } from './with_privileges'; - -export { NotAuthorizedSection } from './not_authorized_section'; diff --git a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/not_authorized_section.tsx b/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/not_authorized_section.tsx deleted file mode 100644 index 3fc13245708e85..00000000000000 --- a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/not_authorized_section.tsx +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { EuiEmptyPrompt } from '@elastic/eui'; - -interface Props { - title: React.ReactNode; - message: React.ReactNode | string; -} - -export const NotAuthorizedSection = ({ title, message }: Props) => ( - {title}} body={

{message}

} /> -); diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx index f3110199ee17c6..03d381c9f3aa3d 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_details/policy_details.tsx @@ -26,6 +26,7 @@ import { import { SlmPolicy } from '../../../../../../common/types'; import { useServices } from '../../../../app_context'; +import { SectionError, Error } from '../../../../../shared_imports'; import { UIM_POLICY_DETAIL_PANEL_SUMMARY_TAB, UIM_POLICY_DETAIL_PANEL_HISTORY_TAB, @@ -34,11 +35,9 @@ import { useLoadPolicy } from '../../../../services/http'; import { linkToEditPolicy, linkToSnapshot } from '../../../../services/navigation'; import { - SectionError, SectionLoading, PolicyExecuteProvider, PolicyDeleteProvider, - Error, } from '../../../../components'; import { TabSummary, TabHistory } from './tabs'; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_list.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_list.tsx index 0122e25e5e1657..51297038b0f3fa 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_list.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_list.tsx @@ -9,13 +9,19 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { RouteComponentProps } from 'react-router-dom'; import { EuiEmptyPrompt, EuiButton, EuiCallOut, EuiSpacer } from '@elastic/eui'; +import { + SectionError, + Error, + WithPrivileges, + NotAuthorizedSection, +} from '../../../../shared_imports'; + import { SlmPolicy } from '../../../../../common/types'; -import { APP_SLM_CLUSTER_PRIVILEGES } from '../../../../../common/constants'; -import { SectionError, SectionLoading, Error } from '../../../components'; +import { APP_SLM_CLUSTER_PRIVILEGES } from '../../../../../common'; +import { SectionLoading } from '../../../components'; import { BASE_PATH, UIM_POLICY_LIST_LOAD } from '../../../constants'; import { useLoadPolicies, useLoadRetentionSettings } from '../../../services/http'; import { linkToAddPolicy, linkToPolicy } from '../../../services/navigation'; -import { WithPrivileges, NotAuthorizedSection } from '../../../lib/authorization'; import { useServices } from '../../../app_context'; import { PolicyDetails } from './policy_details'; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_table/policy_table.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_table/policy_table.tsx index 7f9c5c5af7705c..ba28bcddf53470 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_table/policy_table.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/policy_list/policy_table/policy_table.tsx @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import { SlmPolicy } from '../../../../../../common/types'; +import { Error } from '../../../../../shared_imports'; import { UIM_POLICY_SHOW_DETAILS_CLICK } from '../../../../constants'; import { useServices } from '../../../../app_context'; import { @@ -28,7 +29,6 @@ import { PolicyExecuteProvider, PolicyDeleteProvider, } from '../../../../components'; -import { Error } from '../../../../components/section_error'; import { linkToAddPolicy, linkToEditPolicy } from '../../../../services/navigation'; import { SendRequestResponse } from '../../../../../shared_imports'; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/repository_details.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/repository_details.tsx index d293f194f647a0..9932f146640760 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/repository_details.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_details/repository_details.tsx @@ -25,6 +25,8 @@ import { import 'brace/theme/textmate'; +import { SectionError, Error } from '../../../../../shared_imports'; + import { useServices } from '../../../../app_context'; import { documentationLinksService } from '../../../../services/documentation'; import { @@ -35,7 +37,8 @@ import { import { textService } from '../../../../services/text'; import { linkToSnapshots, linkToEditRepository } from '../../../../services/navigation'; -import { REPOSITORY_TYPES } from '../../../../../../common/constants'; +import { REPOSITORY_TYPES } from '../../../../../../common'; + import { Repository, RepositoryVerification, @@ -43,10 +46,8 @@ import { } from '../../../../../../common/types'; import { RepositoryDeleteProvider, - SectionError, SectionLoading, RepositoryVerificationBadge, - Error, } from '../../../../components'; import { TypeDetails } from './type_details'; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_list.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_list.tsx index 6fa12537e9d6f8..2256fa5991decf 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_list.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_list.tsx @@ -10,7 +10,8 @@ import { RouteComponentProps } from 'react-router-dom'; import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; import { Repository } from '../../../../../common/types'; -import { SectionError, SectionLoading, Error } from '../../../components'; +import { SectionError, Error } from '../../../../shared_imports'; +import { SectionLoading } from '../../../components'; import { BASE_PATH, UIM_REPOSITORY_LIST_LOAD } from '../../../constants'; import { useServices } from '../../../app_context'; import { useLoadRepositories } from '../../../services/http'; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_table/repository_table.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_table/repository_table.tsx index 7c0438f6b837fa..bf2643d78bca16 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_table/repository_table.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/repository_list/repository_table/repository_table.tsx @@ -15,15 +15,14 @@ import { EuiIconTip, } from '@elastic/eui'; -import { REPOSITORY_TYPES } from '../../../../../../common/constants'; +import { REPOSITORY_TYPES } from '../../../../../../common'; import { Repository, RepositoryType } from '../../../../../../common/types'; -import { Error } from '../../../../components/section_error'; +import { Error, SendRequestResponse } from '../../../../../shared_imports'; import { RepositoryDeleteProvider } from '../../../../components'; import { UIM_REPOSITORY_SHOW_DETAILS_CLICK } from '../../../../constants'; import { useServices } from '../../../../app_context'; import { textService } from '../../../../services/text'; import { linkToEditRepository, linkToAddRepository } from '../../../../services/navigation'; -import { SendRequestResponse } from '../../../../../shared_imports'; interface Props { repositories: Repository[]; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/restore_list/restore_list.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/restore_list/restore_list.tsx index da9ce3b124a11a..0e3d9363d05359 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/restore_list/restore_list.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/restore_list/restore_list.tsx @@ -18,14 +18,19 @@ import { EuiLoadingSpinner, EuiLink, } from '@elastic/eui'; -import { APP_RESTORE_INDEX_PRIVILEGES } from '../../../../../common/constants'; -import { SectionError, SectionLoading, Error } from '../../../components'; +import { APP_RESTORE_INDEX_PRIVILEGES } from '../../../../../common'; +import { + WithPrivileges, + NotAuthorizedSection, + SectionError, + Error, +} from '../../../../shared_imports'; +import { SectionLoading } from '../../../components'; import { UIM_RESTORE_LIST_LOAD } from '../../../constants'; import { useLoadRestores } from '../../../services/http'; import { linkToSnapshots } from '../../../services/navigation'; import { useServices } from '../../../app_context'; import { RestoreTable } from './restore_table'; -import { WithPrivileges, NotAuthorizedSection } from '../../../lib/authorization'; const ONE_SECOND_MS = 1000; const TEN_SECONDS_MS = 10 * 1000; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_details/snapshot_details.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_details/snapshot_details.tsx index d16545debe1ecf..1943762a3c36e9 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_details/snapshot_details.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_details/snapshot_details.tsx @@ -23,12 +23,8 @@ import React, { Fragment, useState, useEffect } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { SnapshotDetails as ISnapshotDetails } from '../../../../../../common/types'; -import { - SectionError, - SectionLoading, - SnapshotDeleteProvider, - Error, -} from '../../../../components'; +import { SectionError, Error } from '../../../../../shared_imports'; +import { SectionLoading, SnapshotDeleteProvider } from '../../../../components'; import { useServices } from '../../../../app_context'; import { UIM_SNAPSHOT_DETAIL_PANEL_SUMMARY_TAB, diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx index fe99ccb6f596c8..30e4c771644bc0 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx @@ -10,10 +10,10 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { RouteComponentProps } from 'react-router-dom'; import { EuiButton, EuiCallOut, EuiLink, EuiEmptyPrompt, EuiSpacer, EuiIcon } from '@elastic/eui'; -import { APP_SLM_CLUSTER_PRIVILEGES } from '../../../../../common/constants'; -import { SectionError, SectionLoading, Error } from '../../../components'; +import { APP_SLM_CLUSTER_PRIVILEGES } from '../../../../../common'; +import { WithPrivileges, SectionError, Error } from '../../../../shared_imports'; +import { SectionLoading } from '../../../components'; import { BASE_PATH, UIM_SNAPSHOT_LIST_LOAD } from '../../../constants'; -import { WithPrivileges } from '../../../lib/authorization'; import { documentationLinksService } from '../../../services/documentation'; import { useLoadSnapshots } from '../../../services/http'; import { diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_table/snapshot_table.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_table/snapshot_table.tsx index ad64dcc7adcfee..427c241970007e 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_table/snapshot_table.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_table/snapshot_table.tsx @@ -17,10 +17,10 @@ import { } from '@elastic/eui'; import { SnapshotDetails } from '../../../../../../common/types'; +import { Error } from '../../../../../shared_imports'; import { SNAPSHOT_STATE, UIM_SNAPSHOT_SHOW_DETAILS_CLICK } from '../../../../constants'; import { useServices } from '../../../../app_context'; import { linkToRepository, linkToRestoreSnapshot } from '../../../../services/navigation'; -import { Error } from '../../../../components/section_error'; import { DataPlaceholder, FormattedDateTime, SnapshotDeleteProvider } from '../../../../components'; import { SendRequestResponse } from '../../../../../shared_imports'; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/policy_add/policy_add.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/policy_add/policy_add.tsx index 4eb0f54978d094..6d1a432be7f9f0 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/policy_add/policy_add.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/policy_add/policy_add.tsx @@ -9,9 +9,11 @@ import { RouteComponentProps } from 'react-router-dom'; import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui'; import { SlmPolicyPayload } from '../../../../common/types'; -import { TIME_UNITS } from '../../../../common/constants'; +import { TIME_UNITS } from '../../../../common'; -import { PolicyForm, SectionError, SectionLoading, Error } from '../../components'; +import { SectionError, Error } from '../../../shared_imports'; + +import { PolicyForm, SectionLoading } from '../../components'; import { BASE_PATH, DEFAULT_POLICY_SCHEDULE } from '../../constants'; import { breadcrumbService, docTitleService } from '../../services/navigation'; import { addPolicy, useLoadIndices } from '../../services/http'; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx index 9ca7eba5c4eebb..0f1473fc054925 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/policy_edit/policy_edit.tsx @@ -9,8 +9,9 @@ import { RouteComponentProps } from 'react-router-dom'; import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle, EuiCallOut } from '@elastic/eui'; import { SlmPolicyPayload } from '../../../../common/types'; +import { SectionError, Error } from '../../../shared_imports'; import { TIME_UNITS } from '../../../../common/constants'; -import { SectionError, SectionLoading, PolicyForm, Error } from '../../components'; +import { SectionLoading, PolicyForm } from '../../components'; import { BASE_PATH } from '../../constants'; import { useServices } from '../../app_context'; import { breadcrumbService, docTitleService } from '../../services/navigation'; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/repository_add/repository_add.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/repository_add/repository_add.tsx index 126e04bc7dc1d7..08bfde833c3688 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/repository_add/repository_add.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/repository_add/repository_add.tsx @@ -12,7 +12,9 @@ import { RouteComponentProps } from 'react-router-dom'; import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui'; import { Repository, EmptyRepository } from '../../../../common/types'; -import { RepositoryForm, SectionError } from '../../components'; +import { SectionError } from '../../../shared_imports'; + +import { RepositoryForm } from '../../components'; import { BASE_PATH, Section } from '../../constants'; import { breadcrumbService, docTitleService } from '../../services/navigation'; import { addRepository } from '../../services/http'; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/repository_edit/repository_edit.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/repository_edit/repository_edit.tsx index aa29b8b9f05518..95f8b9b8bde7d0 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/repository_edit/repository_edit.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/repository_edit/repository_edit.tsx @@ -10,7 +10,8 @@ import { RouteComponentProps } from 'react-router-dom'; import { EuiCallOut, EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui'; import { Repository, EmptyRepository } from '../../../../common/types'; -import { RepositoryForm, SectionError, SectionLoading, Error } from '../../components'; +import { SectionError, Error } from '../../../shared_imports'; +import { RepositoryForm, SectionLoading } from '../../components'; import { BASE_PATH, Section } from '../../constants'; import { useServices } from '../../app_context'; import { breadcrumbService, docTitleService } from '../../services/navigation'; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/restore_snapshot/restore_snapshot.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/restore_snapshot/restore_snapshot.tsx index 252fd07a85f800..9eabed8341ee02 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/restore_snapshot/restore_snapshot.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/restore_snapshot/restore_snapshot.tsx @@ -9,8 +9,9 @@ import { RouteComponentProps } from 'react-router-dom'; import { EuiPageBody, EuiPageContent, EuiSpacer, EuiTitle } from '@elastic/eui'; import { SnapshotDetails, RestoreSettings } from '../../../../common/types'; +import { SectionError, Error } from '../../../shared_imports'; import { BASE_PATH } from '../../constants'; -import { SectionError, SectionLoading, RestoreSnapshotForm, Error } from '../../components'; +import { SectionLoading, RestoreSnapshotForm } from '../../components'; import { useServices } from '../../app_context'; import { breadcrumbService, docTitleService } from '../../services/navigation'; import { useLoadSnapshot, executeRestore } from '../../services/http'; diff --git a/x-pack/plugins/snapshot_restore/public/application/services/http/use_request.ts b/x-pack/plugins/snapshot_restore/public/application/services/http/use_request.ts index 200d601fd2ce98..27a565ccb74bc9 100644 --- a/x-pack/plugins/snapshot_restore/public/application/services/http/use_request.ts +++ b/x-pack/plugins/snapshot_restore/public/application/services/http/use_request.ts @@ -9,11 +9,10 @@ import { UseRequestConfig, sendRequest as _sendRequest, useRequest as _useRequest, + Error as CustomError, } from '../../../shared_imports'; -import { Error as CustomError } from '../../components/section_error'; - -import { httpService } from './index'; +import { httpService } from '.'; export const sendRequest = (config: SendRequestConfig) => { return _sendRequest(httpService.httpClient, config); diff --git a/x-pack/plugins/snapshot_restore/public/shared_imports.ts b/x-pack/plugins/snapshot_restore/public/shared_imports.ts index 7e7ef09d0c09dc..e0024ea8e0c126 100644 --- a/x-pack/plugins/snapshot_restore/public/shared_imports.ts +++ b/x-pack/plugins/snapshot_restore/public/shared_imports.ts @@ -12,4 +12,10 @@ export { useRequest, CronEditor, DAY, + SectionError, + Error, + WithPrivileges, + useAuthorizationContext, + NotAuthorizedSection, + AuthorizationProvider, } from '../../../../src/plugins/es_ui_shared/public'; diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/app.ts b/x-pack/plugins/snapshot_restore/server/routes/api/app.ts index 5d334fddc144bc..bda64fdb665714 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/app.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/app.ts @@ -3,12 +3,13 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Privileges } from '../../../common/types'; +import { Privileges } from '../../../../../../src/plugins/es_ui_shared/public'; + import { APP_REQUIRED_CLUSTER_PRIVILEGES, APP_RESTORE_INDEX_PRIVILEGES, APP_SLM_CLUSTER_PRIVILEGES, -} from '../../../common/constants'; +} from '../../../common'; import { RouteDependencies } from '../../types'; import { addBasePath } from '../helpers'; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 4e1217ac9e7b50..8a606e230dc36f 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2244,7 +2244,6 @@ "kbn.management.editIndexPattern.scripted.table.nameHeader": "名前", "kbn.management.editIndexPattern.scripted.table.scriptDescription": "フィールドのスクリプトです", "kbn.management.editIndexPattern.scripted.table.scriptHeader": "スクリプト", - "kbn.management.editIndexPattern.scripted.unknownModeErrorMessage": "不明なフィールド設定モード {mode}", "kbn.management.editIndexPattern.scriptedHeader": "スクリプトフィールド", "kbn.management.editIndexPattern.scriptedLabel": "ビジュアライゼーションにスクリプトフィールドを使用し、ドキュメントに表示させることができます。但し、スクリプトフィールドは検索できません。", "kbn.management.editIndexPattern.setDefaultAria": "デフォルトのインデックスに設定", @@ -8788,8 +8787,6 @@ "xpack.logstash.idFormatErrorMessage": "パイプライン ID は文字またはアンダーラインで始まる必要があり、文字、アンダーライン、ハイフン、数字のみ使用できます", "xpack.logstash.insufficientUserPermissionsDescription": "Logstash パイプラインの管理に必要なユーザーパーミッションがありません", "xpack.logstash.kibanaManagementPipelinesTitle": "Kibana の管理で作成されたパイプラインだけがここに表示されます", - "xpack.logstash.managementSection.createPipelineTitle": "パイプラインの作成", - "xpack.logstash.managementSection.editPipelineTitle": "パイプラインの編集", "xpack.logstash.managementSection.enableSecurityDescription": "Logstash パイプライン管理機能を使用するには、セキュリティを有効にする必要があります。elasticsearch.yml で xpack.security.enabled: true に設定してください。", "xpack.logstash.managementSection.licenseDoesNotSupportDescription": "ご使用の {licenseType} ライセンスは Logstash パイプライン管理をサポートしていません。ライセンスをアップグレードしてください。", "xpack.logstash.managementSection.notPossibleToManagePipelinesMessage": "現在ライセンス情報が利用できないため Logstash パイプラインを使用できません。", @@ -9478,8 +9475,6 @@ "xpack.ml.dataframe.analytics.classificationExploration.evaluateJobIdTitle": "分類ジョブID {jobId}の評価", "xpack.ml.dataframe.analytics.classificationExploration.firstDocumentsShownHelpText": "予測がある最初の{searchSize}のドキュメントを示す", "xpack.ml.dataframe.analytics.classificationExploration.generalizationDocsCount": "{docsCount, plural, one {# doc} other {# docs}}が評価されました", - "xpack.ml.dataframe.analytics.classificationExploration.indexArrayBadgeContent": "配列", - "xpack.ml.dataframe.analytics.classificationExploration.indexArrayToolTipContent": "この配列ベースの列の完全なコンテンツは表示できません。", "xpack.ml.dataframe.analytics.classificationExploration.jobCapsFetchError": "結果を取得できません。インデックスのフィールドデータの読み込み中にエラーが発生しました。", "xpack.ml.dataframe.analytics.classificationExploration.jobConfigurationFetchError": "結果を取得できません。ジョブ構成データの読み込み中にエラーが発生しました。", "xpack.ml.dataframe.analytics.classificationExploration.jobConfigurationNoResultsMessage": "結果が見つかりませんでした。", @@ -9574,8 +9569,6 @@ "xpack.ml.dataframe.analytics.regressionExploration.generalizationDocsCount": "{docsCount, plural, one {# doc} other {# docs}}が評価されました", "xpack.ml.dataframe.analytics.regressionExploration.generalizationErrorTitle": "一般化エラー", "xpack.ml.dataframe.analytics.regressionExploration.indexError": "インデックスデータの読み込み中にエラーが発生しました。", - "xpack.ml.dataframe.analytics.regressionExploration.isTestingLabel": "テスト", - "xpack.ml.dataframe.analytics.regressionExploration.isTrainingLabel": "トレーニング", "xpack.ml.dataframe.analytics.regressionExploration.jobCapsFetchError": "結果を取得できません。インデックスのフィールドデータの読み込み中にエラーが発生しました。", "xpack.ml.dataframe.analytics.regressionExploration.jobConfigurationFetchError": "結果を取得できません。ジョブ構成データの読み込み中にエラーが発生しました。", "xpack.ml.dataframe.analytics.regressionExploration.meanSquaredErrorText": "平均二乗エラー", @@ -9587,9 +9580,6 @@ "xpack.ml.dataframe.analytics.regressionExploration.queryParsingErrorMessage": "クエリをパースできません。", "xpack.ml.dataframe.analytics.regressionExploration.rSquaredText": "R の二乗", "xpack.ml.dataframe.analytics.regressionExploration.rSquaredTooltipContent": "適合度を表します。モデルによる観察された結果の複製の効果を測定します。", - "xpack.ml.dataframe.analytics.regressionExploration.searchBoxPlaceholder": "例: 平均>0.5", - "xpack.ml.dataframe.analytics.regressionExploration.selectColumnsAriaLabel": "列を選択", - "xpack.ml.dataframe.analytics.regressionExploration.selectFieldsPopoverTitle": "フィールドを選択", "xpack.ml.dataframe.analytics.regressionExploration.tableJobIdTitle": "回帰ジョブID {jobId}のデスティネーションインデックス", "xpack.ml.dataframe.analytics.regressionExploration.trainingDocsCount": "{docsCount, plural, one {# doc} other {# docs}}が評価されました", "xpack.ml.dataframe.analytics.regressionExploration.trainingErrorTitle": "トレーニングエラー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index bfdcc8b865313f..faee95b8172b72 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2245,7 +2245,6 @@ "kbn.management.editIndexPattern.scripted.table.nameHeader": "名称", "kbn.management.editIndexPattern.scripted.table.scriptDescription": "字段的脚本", "kbn.management.editIndexPattern.scripted.table.scriptHeader": "脚本", - "kbn.management.editIndexPattern.scripted.unknownModeErrorMessage": "未知 fieldSettings 模式 {mode}", "kbn.management.editIndexPattern.scriptedHeader": "脚本字段", "kbn.management.editIndexPattern.scriptedLabel": "可以在可视化中使用脚本字段,并在您的文档中显示它们。但是,您不能搜索脚本字段。", "kbn.management.editIndexPattern.setDefaultAria": "设置为默认索引", @@ -8791,8 +8790,6 @@ "xpack.logstash.idFormatErrorMessage": "管道 ID 必须以字母或下划线开头,并只能包含字母、下划线、短划线和数字", "xpack.logstash.insufficientUserPermissionsDescription": "管理 Logstash 管道的用户权限不足", "xpack.logstash.kibanaManagementPipelinesTitle": "仅在 Kibana“管理”中创建的管道显示在此处", - "xpack.logstash.managementSection.createPipelineTitle": "创建管道", - "xpack.logstash.managementSection.editPipelineTitle": "编辑管道", "xpack.logstash.managementSection.enableSecurityDescription": "必须启用 Security,才能使用 Logstash 管道管理功能。请在 elasticsearch.yml 中设置 xpack.security.enabled: true。", "xpack.logstash.managementSection.licenseDoesNotSupportDescription": "您的{licenseType}许可不支持 Logstash 管道管理功能。请升级您的许可。", "xpack.logstash.managementSection.notPossibleToManagePipelinesMessage": "您不能管理 Logstash 管道,因为许可信息当前不可用。", @@ -9481,8 +9478,6 @@ "xpack.ml.dataframe.analytics.classificationExploration.evaluateJobIdTitle": "分类作业 ID {jobId} 的评估", "xpack.ml.dataframe.analytics.classificationExploration.firstDocumentsShownHelpText": "正在显示有相关预测存在的前 {searchSize} 个文档", "xpack.ml.dataframe.analytics.classificationExploration.generalizationDocsCount": "{docsCount, plural, one {# 个文档} other {# 个文档}}已评估", - "xpack.ml.dataframe.analytics.classificationExploration.indexArrayBadgeContent": "数组", - "xpack.ml.dataframe.analytics.classificationExploration.indexArrayToolTipContent": "此基于数组的列的完整内容无法显示。", "xpack.ml.dataframe.analytics.classificationExploration.jobCapsFetchError": "无法提取结果。加载索引的字段数据时发生错误。", "xpack.ml.dataframe.analytics.classificationExploration.jobConfigurationFetchError": "无法提取结果。加载作业配置数据时发生错误。", "xpack.ml.dataframe.analytics.classificationExploration.jobConfigurationNoResultsMessage": "未找到结果。", @@ -9577,8 +9572,6 @@ "xpack.ml.dataframe.analytics.regressionExploration.generalizationDocsCount": "{docsCount, plural, one {# 个文档} other {# 个文档}}已评估", "xpack.ml.dataframe.analytics.regressionExploration.generalizationErrorTitle": "泛化误差", "xpack.ml.dataframe.analytics.regressionExploration.indexError": "加载索引数据时出错。", - "xpack.ml.dataframe.analytics.regressionExploration.isTestingLabel": "测试", - "xpack.ml.dataframe.analytics.regressionExploration.isTrainingLabel": "培训", "xpack.ml.dataframe.analytics.regressionExploration.jobCapsFetchError": "无法提取结果。加载索引的字段数据时发生错误。", "xpack.ml.dataframe.analytics.regressionExploration.jobConfigurationFetchError": "无法提取结果。加载作业配置数据时发生错误。", "xpack.ml.dataframe.analytics.regressionExploration.meanSquaredErrorText": "均方误差", @@ -9590,9 +9583,6 @@ "xpack.ml.dataframe.analytics.regressionExploration.queryParsingErrorMessage": "无法解析查询。", "xpack.ml.dataframe.analytics.regressionExploration.rSquaredText": "R 平方", "xpack.ml.dataframe.analytics.regressionExploration.rSquaredTooltipContent": "表示拟合优度。度量模型复制被观察结果的优良性。", - "xpack.ml.dataframe.analytics.regressionExploration.searchBoxPlaceholder": "例如 avg>0.5", - "xpack.ml.dataframe.analytics.regressionExploration.selectColumnsAriaLabel": "选择列", - "xpack.ml.dataframe.analytics.regressionExploration.selectFieldsPopoverTitle": "选择字段", "xpack.ml.dataframe.analytics.regressionExploration.tableJobIdTitle": "回归作业 ID {jobId} 的目标索引", "xpack.ml.dataframe.analytics.regressionExploration.trainingDocsCount": "{docsCount, plural, one {# 个文档} other {# 个文档}}已评估", "xpack.ml.dataframe.analytics.regressionExploration.trainingErrorTitle": "训练误差", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx index 7da97b9fe34360..1c9e87310107fc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.test.tsx @@ -90,7 +90,7 @@ describe('pagerduty action params validation', () => { summary: '2323', source: 'source', severity: 'critical', - timestamp: '234654564654', + timestamp: new Date().toISOString(), component: 'test', group: 'group', class: 'test class', @@ -99,6 +99,7 @@ describe('pagerduty action params validation', () => { expect(actionTypeModel.validateParams(actionParams)).toEqual({ errors: { summary: [], + timestamp: [], }, }); }); @@ -156,7 +157,7 @@ describe('PagerDutyParamsFields renders', () => { summary: '2323', source: 'source', severity: SeverityActionOptions.CRITICAL, - timestamp: '234654564654', + timestamp: new Date().toISOString(), component: 'test', group: 'group', class: 'test class', @@ -164,7 +165,7 @@ describe('PagerDutyParamsFields renders', () => { const wrapper = mountWithIntl( {}} index={0} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.tsx index d99362c6183565..15f91ae1d46094 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty.tsx @@ -14,6 +14,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import moment from 'moment'; import { ActionTypeModel, ActionConnectorFieldsProps, @@ -23,6 +24,7 @@ import { import { PagerDutyActionParams, PagerDutyActionConnector } from './types'; import pagerDutySvg from './pagerduty.svg'; import { AddMessageVariables } from '../add_message_variables'; +import { hasMustacheTokens } from '../../lib/has_mustache_tokens'; export function getActionType(): ActionTypeModel { return { @@ -62,6 +64,7 @@ export function getActionType(): ActionTypeModel { const validationResult = { errors: {} }; const errors = { summary: new Array(), + timestamp: new Array(), }; validationResult.errors = errors; if (!actionParams.summary?.length) { @@ -74,6 +77,24 @@ export function getActionType(): ActionTypeModel { ) ); } + if (actionParams.timestamp && !hasMustacheTokens(actionParams.timestamp)) { + if (isNaN(Date.parse(actionParams.timestamp))) { + const { nowShortFormat, nowLongFormat } = getValidTimestampExamples(); + errors.timestamp.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.error.invalidTimestamp', + { + defaultMessage: + 'Timestamp must be a valid date, such as {nowShortFormat} or {nowLongFormat}.', + values: { + nowShortFormat, + nowLongFormat, + }, + } + ) + ); + } + } return validationResult; }, actionConnectorFields: PagerDutyActionConnectorFields, @@ -334,6 +355,8 @@ const PagerDutyParamsFields: React.FunctionComponent 0 && timestamp !== undefined} label={i18n.translate( 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.timestampTextFieldLabel', { @@ -355,11 +378,14 @@ const PagerDutyParamsFields: React.FunctionComponent 0 && timestamp !== undefined} onChange={(e: React.ChangeEvent) => { editAction('timestamp', e.target.value, index); }} onBlur={() => { - if (!timestamp) { + if (timestamp?.trim()) { + editAction('timestamp', timestamp.trim(), index); + } else { editAction('timestamp', '', index); } }} @@ -534,3 +560,11 @@ const PagerDutyParamsFields: React.FunctionComponent ); }; + +function getValidTimestampExamples() { + const now = moment(); + return { + nowShortFormat: now.format('YYYY-MM-DD'), + nowLongFormat: now.format('YYYY-MM-DD h:mm:ss'), + }; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/has_mustache_tokens.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/has_mustache_tokens.test.ts new file mode 100644 index 00000000000000..db4f9fa7991706 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/has_mustache_tokens.test.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import uuid from 'uuid'; + +import { hasMustacheTokens } from './has_mustache_tokens'; + +describe('hasMustacheTokens', () => { + test('returns false for empty string', () => { + expect(hasMustacheTokens('')).toBe(false); + }); + + test('returns false for string without tokens', () => { + expect(hasMustacheTokens(`some random string ${uuid.v4()}`)).toBe(false); + }); + + test('returns true when a template token is present', () => { + expect(hasMustacheTokens('{{context.timestamp}}')).toBe(true); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/has_mustache_tokens.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/has_mustache_tokens.ts new file mode 100644 index 00000000000000..4dcd8113d51fca --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/has_mustache_tokens.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. + */ + +export function hasMustacheTokens(str: string): boolean { + return null !== str.match(/{{.*}}/); +} diff --git a/x-pack/test/api_integration/apis/logstash/pipeline/delete.ts b/x-pack/test/api_integration/apis/logstash/pipeline/delete.ts index cdbf5a3e6a1fea..2463dbe4500b5f 100644 --- a/x-pack/test/api_integration/apis/logstash/pipeline/delete.ts +++ b/x-pack/test/api_integration/apis/logstash/pipeline/delete.ts @@ -20,7 +20,6 @@ export default function({ getService }: FtrProviderContext) { .send({ id: 'fast_generator', description: 'foobar baz', - username: 'seger', pipeline: 'input { generator {} }\n\n output { stdout {} }', }) .expect(204); diff --git a/x-pack/test/api_integration/apis/logstash/pipeline/save.ts b/x-pack/test/api_integration/apis/logstash/pipeline/save.ts index 2ca9fbe7d68e00..ca0cfb19b9454d 100644 --- a/x-pack/test/api_integration/apis/logstash/pipeline/save.ts +++ b/x-pack/test/api_integration/apis/logstash/pipeline/save.ts @@ -28,7 +28,6 @@ export default function({ getService }: FtrProviderContext) { .send({ id: 'fast_generator', description: 'foobar baz', - username: 'seger', pipeline: 'input { generator {} }\n\n output { stdout {} }', }) .expect(204); diff --git a/x-pack/test/reporting/functional/reporting.js b/x-pack/test/reporting/functional/reporting.js index 6107363986a409..c1a2ae634662ca 100644 --- a/x-pack/test/reporting/functional/reporting.js +++ b/x-pack/test/reporting/functional/reporting.js @@ -24,6 +24,7 @@ export default function({ getService, getPageObjects }) { const browser = getService('browser'); const log = getService('log'); const config = getService('config'); + const filterBar = getService('filterBar'); const PageObjects = getPageObjects([ 'reporting', 'common', @@ -161,7 +162,27 @@ export default function({ getService, getPageObjects }) { expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null); }); + it('becomes available/not available when a saved search is created, changed and saved again', async () => { + // create new search, csv export is not available + await PageObjects.discover.clickNewSearchButton(); + await PageObjects.reporting.openCsvReportingPanel(); + expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be('true'); + // save search, csv export is available + await PageObjects.discover.saveSearch('my search - expectEnabledGenerateReportButton 2'); + await PageObjects.reporting.openCsvReportingPanel(); + expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null); + // add filter, csv export is not available + await filterBar.addFilter('currency', 'is', 'EUR'); + await PageObjects.reporting.openCsvReportingPanel(); + expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be('true'); + // save search again, csv export is available + await PageObjects.discover.saveSearch('my search - expectEnabledGenerateReportButton 2'); + await PageObjects.reporting.openCsvReportingPanel(); + expect(await PageObjects.reporting.isGenerateReportButtonDisabled()).to.be(null); + }); + it('generates a report with data', async () => { + await PageObjects.discover.clickNewSearchButton(); await PageObjects.reporting.setTimepickerInDataRange(); await PageObjects.discover.saveSearch('my search - with data - expectReportCanBeCreated'); await PageObjects.reporting.openCsvReportingPanel();