From 7b57caf62beb33d06959ac6368a956a9e5f492aa Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 20 Apr 2020 09:32:03 +0300 Subject: [PATCH 01/34] [i18n] add localization section in CONTRIBUTING file (#63668) * add localization section * upadte TOC * Update CONTRIBUTING.md --- CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5c745f1611cce..e4a9d87bc56fc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,6 +22,7 @@ A high level overview of our contributing guidelines. - [Setting Up SSL](#setting-up-ssl) - [Linting](#linting) - [Internationalization](#internationalization) + - [Localization](#localization) - [Testing and Building](#testing-and-building) - [Debugging server code](#debugging-server-code) - [Instrumenting with Elastic APM](#instrumenting-with-elastic-apm) @@ -408,6 +409,11 @@ ReactDOM.render( There are a number of tools created to support internationalization in Kibana that would allow one to validate internationalized labels, extract them to a `JSON` file or integrate translations back to Kibana. To know more, please read corresponding [readme](src/dev/i18n/README.md) file. +### Localization + +We cannot support accepting contributions to the translations from any source other than the translators we have engaged to do the work. +We are still to develop a proper process to accept any contributed translations. We certainly appreciate that people care enough about the localization effort to want to help improve the quality. We aim to build out a more comprehensive localization process for the future and will notify you once contributions can be supported, but for the time being, we are not able to incorporate suggestions. + ### Testing and Building To ensure that your changes will not break other functionality, please run the test suite and build process before submitting your Pull Request. From 7c7fbc70cd081bfe9b5dbdbe90e6773c1e3772d5 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 20 Apr 2020 11:12:17 +0200 Subject: [PATCH 02/34] [Uptime] Refactor folder structure (#63442) * update structure * update connected structure * update connected structure * update code structure * update types * update imports * update folder * update trans * fixed snapshot * updated code * refacto monitor list container * update types Co-authored-by: Elastic Machine --- .../common/constants/chart_format_limits.ts | 2 +- .../common/constants/context_defaults.ts | 2 +- .../plugins/uptime/common/graphql/types.ts | 0 .../uptime/common/runtime_types/common.ts | 1 - .../plugins/uptime/common/types/index.ts | 4 + .../plugins/uptime/public/apps/plugin.ts | 14 +- .../__snapshots__/location_link.test.tsx.snap | 0 .../uptime_date_picker.test.tsx.snap | 0 .../__tests__/location_link.test.tsx | 0 .../__tests__/uptime_date_picker.test.tsx | 0 .../chart_empty_state.test.tsx.snap | 0 .../__snapshots__/chart_wrapper.test.tsx.snap | 0 .../__snapshots__/donut_chart.test.tsx.snap | 0 .../donut_chart_legend.test.tsx.snap | 0 .../donut_chart_legend_row.test.tsx.snap | 0 .../duration_charts.test.tsx.snap | 1 - .../monitor_bar_series.test.tsx.snap | 0 .../ping_histogram.test.tsx.snap | 0 .../__tests__/chart_empty_state.test.tsx | 0 .../charts/__tests__/chart_wrapper.test.tsx | 2 +- .../charts/__tests__/donut_chart.test.tsx | 0 .../__tests__/donut_chart_legend.test.tsx | 0 .../__tests__/donut_chart_legend_row.test.tsx | 0 .../charts/__tests__/duration_charts.test.tsx | 1 - .../charts/__tests__/get_tick_format.test.ts | 0 .../__tests__/monitor_bar_series.test.tsx | 0 .../charts/__tests__/ping_histogram.test.tsx | 0 .../charts/annotation_tooltip.tsx | 0 .../charts/chart_empty_state.tsx | 0 .../charts/chart_wrapper/chart_wrapper.tsx | 0 .../charts/chart_wrapper/index.ts | 0 .../charts/donut_chart.tsx | 0 .../charts/donut_chart_legend.tsx | 0 .../charts/donut_chart_legend_row.tsx | 0 .../common/charts/duration_chart.tsx | 122 ++++++++++++++ .../charts/duration_line_bar_list.tsx | 0 .../charts/duration_line_series_list.tsx | 2 +- .../charts/get_tick_format.ts | 0 .../{functional => common}/charts/index.ts | 0 .../charts/monitor_bar_series.tsx | 0 .../charts/ping_histogram.tsx | 0 .../responsive_wrapper.test.tsx.snap | 0 .../__tests__/responsive_wrapper.test.tsx | 0 .../{ => common}/higher_order/index.ts | 0 .../higher_order/responsive_wrapper.tsx | 0 .../location_link.tsx | 0 .../uptime_date_picker.tsx | 2 +- .../connected/charts/ping_histogram.tsx | 82 --------- .../connected/charts/snapshot_container.tsx | 93 ----------- .../public/components/connected/index.ts | 19 --- .../monitor/status_bar_container.tsx | 71 -------- .../monitor/status_details_container.tsx | 63 ------- .../functional/charts/duration_chart.tsx | 155 ------------------ .../public/components/functional/index.ts | 20 --- .../monitor_status_bar/index.ts | 8 - .../components/functional/search_schema.ts | 17 -- .../monitor_charts.test.tsx.snap | 0 .../__tests__/monitor_charts.test.tsx | 0 .../uptime/public/components/monitor/index.ts | 12 ++ .../__snapshots__/location_map.test.tsx.snap | 0 .../location_missing.test.tsx.snap | 0 .../location_status_tags.test.tsx.snap | 0 .../__tests__/location_map.test.tsx | 0 .../__tests__/location_missing.test.tsx | 0 .../__tests__/location_status_tags.test.tsx | 3 +- .../embeddables/__tests__/__mocks__/mock.ts | 0 .../embeddables/__tests__/map_config.test.ts | 0 .../location_map/embeddables/embedded_map.tsx | 0 .../embeddables/low_poly_layer.json | 0 .../location_map/embeddables/map_config.ts | 2 +- .../location_map/embeddables/translations.ts | 0 .../location_map/index.tsx | 0 .../location_map/location_map.tsx | 0 .../location_map/location_missing.tsx | 2 +- .../location_map/location_status_tags.tsx | 0 .../confirm_delete.test.tsx.snap | 0 .../__snapshots__/license_info.test.tsx.snap | 0 .../__snapshots__/ml_flyout.test.tsx.snap | 0 .../ml_integerations.test.tsx.snap | 0 .../__snapshots__/ml_job_link.test.tsx.snap | 0 .../__snapshots__/ml_manage_job.test.tsx.snap | 0 .../ml/__tests__/confirm_delete.test.tsx | 0 .../ml/__tests__/license_info.test.tsx | 0 .../ml/__tests__/ml_flyout.test.tsx | 0 .../ml/__tests__/ml_integerations.test.tsx | 0 .../ml/__tests__/ml_job_link.test.tsx | 0 .../ml/__tests__/ml_manage_job.test.tsx | 0 .../ml/confirm_delete.tsx | 0 .../components/monitor/ml}/index.ts | 2 +- .../ml/license_info.tsx | 0 .../ml/manage_ml_job.tsx | 2 +- .../ml/ml_flyout.tsx | 0 .../ml/ml_flyout_container.tsx | 2 +- .../ml/ml_integeration.tsx | 2 +- .../ml/ml_job_link.tsx | 0 .../ml/translations.tsx | 7 - .../monitor_charts.tsx | 7 +- .../monitor/monitor_duration/index.ts | 8 + .../monitor_duration/monitor_duration.tsx | 66 ++++++++ .../monitor_duration_container.tsx} | 11 +- .../monitor_ssl_certificate.test.tsx.snap | 0 .../monitor_status.bar.test.tsx.snap | 0 .../status_by_location.test.tsx.snap | 0 .../__test__/monitor_ssl_certificate.test.tsx | 0 .../__test__/monitor_status.bar.test.tsx | 0 .../__test__/status_by_location.test.tsx | 2 +- .../monitor_status_details/index.ts | 5 +- .../monitor_status_bar/index.ts | 9 + .../monitor_status_bar/ssl_certificate.tsx} | 0 .../monitor_status_bar/status_bar.tsx} | 4 +- .../status_bar_container.tsx | 40 +++++ .../monitor_status_bar/status_by_location.tsx | 0 .../monitor_status_bar/translations.ts | 1 + .../status_details.tsx} | 2 +- .../status_details_container.tsx | 34 ++++ .../monitor_status_details/translations.ts | 1 + .../pings => monitor/ping_histogram}/index.ts | 2 +- .../ping_histogram_container.tsx | 48 ++++++ .../__snapshots__/doc_link_body.test.tsx.snap | 0 .../__snapshots__/expanded_row.test.tsx.snap | 0 .../__snapshots__/ping_list.test.tsx.snap | 0 .../__tests__/doc_link_body.test.tsx | 0 .../ping_list/__tests__/expanded_row.test.tsx | 0 .../ping_list/__tests__/ping_list.test.tsx | 2 +- .../ping_list/doc_link_body.tsx | 0 .../ping_list/expanded_row.tsx | 0 .../ping_list/index.tsx | 1 + .../ping_list/location_name.tsx | 0 .../ping_list/ping_list.tsx | 6 +- .../ping_list/ping_list_container.tsx} | 2 +- .../parsing_error_callout.test.tsx.snap} | 0 .../__snapshots__/snapshot.test.tsx.snap | 0 .../snapshot_heading.test.tsx.snap | 0 .../__tests__/parsing_error_callout.test.tsx} | 6 +- .../__tests__/snapshot.test.tsx | 2 +- .../__tests__/snapshot_heading.test.tsx | 2 +- .../__tests__/alert_monitor_status.test.tsx | 4 +- .../alerts/alert_monitor_status.tsx | 2 +- .../alert_monitor_status.tsx | 4 +- .../alerts/alerts_containers}/index.ts | 0 .../toggle_alert_flyout_button.tsx | 4 +- .../uptime_alerts_flyout_wrapper.tsx | 6 +- .../{functional => overview}/alerts/index.ts | 1 + .../alerts/toggle_alert_flyout_button.tsx | 0 .../alerts/uptime_alerts_context_provider.tsx | 0 .../alerts/uptime_alerts_flyout_wrapper.tsx | 0 .../data_or_index_missing.test.tsx.snap | 0 .../__snapshots__/empty_state.test.tsx.snap | 0 .../__tests__/data_or_index_missing.test.tsx | 0 .../__tests__/empty_state.test.tsx | 0 .../empty_state/data_or_index_missing.tsx | 0 .../empty_state/empty_state.tsx | 0 .../empty_state/empty_state_container.tsx} | 2 +- .../empty_state/empty_state_error.tsx | 0 .../empty_state/empty_state_loading.tsx | 0 .../components/overview/empty_state/index.ts | 8 + .../filter_popover.test.tsx.snap | 0 .../filter_status_button.test.tsx.snap | 0 .../parse_filter_map.test.ts.snap | 0 .../__tests__/filter_popover.test.tsx | 0 .../__tests__/filter_status_button.test.tsx | 2 +- .../__tests__/parse_filter_map.test.ts | 0 .../__tests__/toggle_selected_item.test.ts | 0 .../filter_group/filter_group.tsx | 0 .../filter_group/filter_group_container.tsx | 4 +- .../filter_group/filter_popover.tsx | 0 .../filter_group/filter_status_button.tsx | 0 .../filter_group/index.ts | 1 + .../filter_group/parse_filter_map.ts | 0 .../filter_group/toggle_selected_item.ts | 0 .../filter_group/uptime_filter_button.tsx | 0 .../public/components/overview/index.ts | 14 ++ .../components/overview/kuery_bar/index.ts | 8 + .../kuery_bar/kuery_bar.tsx | 0 .../kuery_bar}/kuery_bar_container.tsx | 2 +- .../kuery_bar/typeahead/click_outside.js | 0 .../kuery_bar/typeahead/index.d.ts | 2 +- .../kuery_bar/typeahead/index.js | 0 .../kuery_bar/typeahead/suggestion.js | 0 .../kuery_bar/typeahead/suggestions.js | 0 .../__snapshots__/monitor_list.test.tsx.snap | 0 .../monitor_list_status_column.test.tsx.snap | 0 .../monitor_page_link.test.tsx.snap | 0 .../__tests__/monitor_list.test.tsx | 0 .../monitor_list_page_size_select.test.tsx | 0 .../monitor_list_status_column.test.tsx | 0 .../__tests__/monitor_page_link.test.tsx | 0 .../__tests__/parse_timestamp.test.ts | 0 .../monitor_list/index.ts | 2 + .../monitor_list/monitor_list.tsx | 6 +- .../monitor_list/monitor_list_container.tsx} | 5 +- .../integration_group.test.tsx.snap | 0 .../integration_link.test.tsx.snap | 0 .../monitor_list_drawer.test.tsx.snap | 0 .../monitor_status_list.test.tsx.snap | 0 .../monitor_status_row.test.tsx.snap | 0 .../most_recent_error.test.tsx.snap | 0 .../monitor_list_drawer/__tests__/data.json | 0 .../__tests__/integration_group.test.tsx | 2 +- .../__tests__/integration_link.test.tsx | 2 +- .../__tests__/monitor_list_drawer.test.tsx | 0 .../__tests__/monitor_status_list.test.tsx | 0 .../__tests__/monitor_status_row.test.tsx | 0 .../__tests__/most_recent_error.test.tsx | 0 .../actions_popover/actions_popover.tsx} | 10 +- .../actions_popover_container.tsx} | 13 +- .../actions_popover}/integration_group.tsx | 6 +- .../actions_popover}/integration_link.tsx | 0 .../monitor_list/monitor_list_drawer/index.ts | 4 +- .../list_drawer_container.tsx | 14 +- .../monitor_list_drawer.tsx | 4 +- .../monitor_status_list.tsx | 2 +- .../monitor_status_row.tsx | 0 .../monitor_list_drawer/most_recent_error.tsx | 0 .../monitor_list_page_size_select.tsx | 0 .../monitor_list_status_column.tsx | 0 .../monitor_list/monitor_page_link.tsx | 0 .../monitor_list/overview_page_link.tsx | 0 .../monitor_list/parse_timestamp.ts | 0 .../monitor_list/translations.ts | 0 .../monitor_list/types.ts | 7 - .../pages => overview}/overview_container.tsx | 8 +- .../parsing_error_callout.tsx} | 6 +- .../components/overview/snapshot/index.ts} | 6 +- .../snapshot}/snapshot.tsx | 6 +- .../overview/snapshot/snapshot_container.tsx | 34 ++++ .../snapshot}/snapshot_heading.tsx | 0 .../{functional => overview}/status_panel.tsx | 3 +- .../contexts/uptime_settings_context.tsx | 2 +- .../hooks/__tests__/use_breadcrumbs.test.tsx | 2 +- .../uptime/public/lib/alert_types/index.ts | 2 +- .../public/lib/alert_types/monitor_status.tsx | 2 +- .../lib/helper/charts/get_chart_date_label.ts | 2 +- .../lib/helper/charts/get_label_format.ts | 2 +- .../public/lib/helper/convert_measurements.ts | 2 +- .../__tests__/parse_absolute_date.test.ts | 2 +- .../parse_autorefresh_interval.test.ts | 24 --- .../legacy/plugins/uptime/public/lib/lib.ts | 3 - .../plugins/uptime/public/pages/monitor.tsx | 5 +- .../plugins/uptime/public/pages/overview.tsx | 7 +- .../uptime/public/pages/page_header.tsx | 4 +- .../legacy/plugins/uptime/public/routes.tsx | 2 +- .../uptime/public/state/api/monitor.ts | 2 +- .../public/state/api/monitor_duration.ts | 2 +- .../public/state/api/overview_filters.ts | 4 +- .../plugins/uptime/public/state/api/ping.ts | 2 +- .../uptime/public/state/api/snapshot.ts | 2 +- .../public/state/reducers/monitor_status.ts | 4 +- .../uptime/public/state/reducers/ui.ts | 2 +- .../uptime/public/state/selectors/index.ts | 10 ++ .../plugins/uptime/public/uptime_app.tsx | 8 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 253 files changed, 560 insertions(+), 719 deletions(-) create mode 100644 x-pack/legacy/plugins/uptime/common/graphql/types.ts rename x-pack/legacy/plugins/uptime/public/components/{functional/monitor_list/monitor_list_drawer => common}/__tests__/__snapshots__/location_link.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional/monitor_list/monitor_list_drawer => common}/__tests__/location_link.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/__tests__/uptime_date_picker.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/__snapshots__/chart_empty_state.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/__snapshots__/chart_wrapper.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/__snapshots__/donut_chart_legend.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/__snapshots__/duration_charts.test.tsx.snap (99%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/__snapshots__/monitor_bar_series.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/__snapshots__/ping_histogram.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/chart_empty_state.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/chart_wrapper.test.tsx (97%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/donut_chart.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/donut_chart_legend.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/donut_chart_legend_row.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/duration_charts.test.tsx (98%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/get_tick_format.test.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/monitor_bar_series.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/__tests__/ping_histogram.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/annotation_tooltip.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/chart_empty_state.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/chart_wrapper/chart_wrapper.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/chart_wrapper/index.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/donut_chart.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/donut_chart_legend.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/donut_chart_legend_row.tsx (100%) create mode 100644 x-pack/legacy/plugins/uptime/public/components/common/charts/duration_chart.tsx rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/duration_line_bar_list.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/duration_line_series_list.tsx (97%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/get_tick_format.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/index.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/monitor_bar_series.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/charts/ping_histogram.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{ => common}/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{ => common}/higher_order/__tests__/responsive_wrapper.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{ => common}/higher_order/index.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{ => common}/higher_order/responsive_wrapper.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional/monitor_list/monitor_list_drawer => common}/location_link.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => common}/uptime_date_picker.tsx (100%) delete mode 100644 x-pack/legacy/plugins/uptime/public/components/connected/charts/ping_histogram.tsx delete mode 100644 x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx delete mode 100644 x-pack/legacy/plugins/uptime/public/components/connected/index.ts delete mode 100644 x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx delete mode 100644 x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx delete mode 100644 x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx delete mode 100644 x-pack/legacy/plugins/uptime/public/components/functional/index.ts delete mode 100644 x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/index.ts delete mode 100644 x-pack/legacy/plugins/uptime/public/components/functional/search_schema.ts rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/__tests__/__snapshots__/monitor_charts.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/__tests__/monitor_charts.test.tsx (100%) create mode 100644 x-pack/legacy/plugins/uptime/public/components/monitor/index.ts rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/__tests__/__snapshots__/location_map.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/__tests__/location_map.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/__tests__/location_missing.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/__tests__/location_status_tags.test.tsx (97%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/embeddables/__tests__/__mocks__/mock.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/embeddables/__tests__/map_config.test.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/embeddables/embedded_map.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/embeddables/low_poly_layer.json (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/embeddables/map_config.ts (98%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/embeddables/translations.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/index.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/location_map.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/location_missing.tsx (97%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/location_map/location_status_tags.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/__snapshots__/license_info.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/__snapshots__/ml_job_link.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/confirm_delete.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/license_info.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/ml_flyout.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/ml_integerations.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/ml_job_link.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/__tests__/ml_manage_job.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/confirm_delete.tsx (100%) rename x-pack/legacy/plugins/uptime/{common/domain_types => public/components/monitor/ml}/index.ts (81%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/license_info.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/manage_ml_job.tsx (97%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/ml_flyout.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/ml_flyout_container.tsx (98%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/ml_integeration.tsx (98%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/ml_job_link.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{monitor_details => monitor}/ml/translations.tsx (95%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/monitor_charts.tsx (68%) create mode 100644 x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/index.ts create mode 100644 x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration.tsx rename x-pack/legacy/plugins/uptime/public/components/{connected/charts/monitor_duration.tsx => monitor/monitor_duration/monitor_duration_container.tsx} (91%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/monitor_status_details/__test__/__snapshots__/monitor_ssl_certificate.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/monitor_status_details/__test__/__snapshots__/monitor_status.bar.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/monitor_status_details/__test__/__snapshots__/status_by_location.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/monitor_status_details/__test__/monitor_ssl_certificate.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/monitor_status_details/__test__/monitor_status.bar.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/monitor_status_details/__test__/status_by_location.test.tsx (98%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/monitor_status_details/index.ts (64%) create mode 100644 x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/index.ts rename x-pack/legacy/plugins/uptime/public/components/{functional/monitor_status_details/monitor_status_bar/monitor_ssl_certificate.tsx => monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx} (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional/monitor_status_details/monitor_status_bar/monitor_status_bar.tsx => monitor/monitor_status_details/monitor_status_bar/status_bar.tsx} (95%) create mode 100644 x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar_container.tsx rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/monitor_status_details/monitor_status_bar/status_by_location.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/monitor_status_details/monitor_status_bar/translations.ts (99%) rename x-pack/legacy/plugins/uptime/public/components/{functional/monitor_status_details/monitor_status_details.tsx => monitor/monitor_status_details/status_details.tsx} (97%) create mode 100644 x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/status_details_container.tsx rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/monitor_status_details/translations.ts (99%) rename x-pack/legacy/plugins/uptime/public/components/{connected/pings => monitor/ping_histogram}/index.ts (80%) create mode 100644 x-pack/legacy/plugins/uptime/public/components/monitor/ping_histogram/ping_histogram_container.tsx rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/ping_list/__tests__/__snapshots__/doc_link_body.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/ping_list/__tests__/doc_link_body.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/ping_list/__tests__/expanded_row.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/ping_list/__tests__/ping_list.test.tsx (99%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/ping_list/doc_link_body.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/ping_list/expanded_row.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/ping_list/index.tsx (85%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/ping_list/location_name.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => monitor}/ping_list/ping_list.tsx (98%) rename x-pack/legacy/plugins/uptime/public/components/{connected/pings/ping_list.tsx => monitor/ping_list/ping_list_container.tsx} (96%) rename x-pack/legacy/plugins/uptime/public/components/{functional/__tests__/__snapshots__/overview_page_parsing_error_callout.test.tsx.snap => overview/__tests__/__snapshots__/parsing_error_callout.test.tsx.snap} (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/__tests__/__snapshots__/snapshot.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/__tests__/__snapshots__/snapshot_heading.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional/__tests__/overview_page_parsing_error_callout.test.tsx => overview/__tests__/parsing_error_callout.test.tsx} (76%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/__tests__/snapshot.test.tsx (92%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/__tests__/snapshot_heading.test.tsx (93%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/alerts/__tests__/alert_monitor_status.test.tsx (98%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/alerts/alert_monitor_status.tsx (99%) rename x-pack/legacy/plugins/uptime/public/components/{connected/alerts => overview/alerts/alerts_containers}/alert_monitor_status.tsx (86%) rename x-pack/legacy/plugins/uptime/public/components/{connected/alerts => overview/alerts/alerts_containers}/index.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{connected/alerts => overview/alerts/alerts_containers}/toggle_alert_flyout_button.tsx (80%) rename x-pack/legacy/plugins/uptime/public/components/{connected/alerts => overview/alerts/alerts_containers}/uptime_alerts_flyout_wrapper.tsx (83%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/alerts/index.ts (93%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/alerts/toggle_alert_flyout_button.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/alerts/uptime_alerts_context_provider.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/alerts/uptime_alerts_flyout_wrapper.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/empty_state/__tests__/__snapshots__/data_or_index_missing.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/empty_state/__tests__/data_or_index_missing.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/empty_state/__tests__/empty_state.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/empty_state/data_or_index_missing.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/empty_state/empty_state.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{connected/empty_state/empty_state.tsx => overview/empty_state/empty_state_container.tsx} (95%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/empty_state/empty_state_error.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/empty_state/empty_state_loading.tsx (100%) create mode 100644 x-pack/legacy/plugins/uptime/public/components/overview/empty_state/index.ts rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/__tests__/__snapshots__/parse_filter_map.test.ts.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/__tests__/filter_popover.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/__tests__/filter_status_button.test.tsx (93%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/__tests__/parse_filter_map.test.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/__tests__/toggle_selected_item.test.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/filter_group.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{connected => overview}/filter_group/filter_group_container.tsx (94%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/filter_popover.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/filter_status_button.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/index.ts (84%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/parse_filter_map.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/toggle_selected_item.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/filter_group/uptime_filter_button.tsx (100%) create mode 100644 x-pack/legacy/plugins/uptime/public/components/overview/index.ts create mode 100644 x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/index.ts rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/kuery_bar/kuery_bar.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{connected/kuerybar => overview/kuery_bar}/kuery_bar_container.tsx (90%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/kuery_bar/typeahead/click_outside.js (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/kuery_bar/typeahead/index.d.ts (95%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/kuery_bar/typeahead/index.js (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/kuery_bar/typeahead/suggestion.js (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/kuery_bar/typeahead/suggestions.js (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/__tests__/__snapshots__/monitor_list_status_column.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/__tests__/monitor_list.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/__tests__/monitor_list_page_size_select.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/__tests__/monitor_list_status_column.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/__tests__/monitor_page_link.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/__tests__/parse_timestamp.test.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/index.ts (69%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list.tsx (97%) rename x-pack/legacy/plugins/uptime/public/components/{connected/monitor/monitor_list.tsx => overview/monitor_list/monitor_list_container.tsx} (93%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_link.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/data.json (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/integration_group.test.tsx (94%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx (93%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional/monitor_list/monitor_list_drawer/monitor_list_actions_popover.tsx => overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx} (89%) rename x-pack/legacy/plugins/uptime/public/components/{connected/monitor/drawer_popover_container.tsx => overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx} (63%) rename x-pack/legacy/plugins/uptime/public/components/{functional/monitor_list/monitor_list_drawer => overview/monitor_list/monitor_list_drawer/actions_popover}/integration_group.tsx (98%) rename x-pack/legacy/plugins/uptime/public/components/{functional/monitor_list/monitor_list_drawer => overview/monitor_list/monitor_list_drawer/actions_popover}/integration_link.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/index.ts (63%) rename x-pack/legacy/plugins/uptime/public/components/{connected/monitor => overview/monitor_list/monitor_list_drawer}/list_drawer_container.tsx (72%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx (93%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/monitor_status_list.tsx (97%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/monitor_status_row.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_drawer/most_recent_error.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_page_size_select.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_list_status_column.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/monitor_page_link.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/overview_page_link.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/parse_timestamp.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/translations.ts (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/monitor_list/types.ts (83%) rename x-pack/legacy/plugins/uptime/public/components/{connected/pages => overview}/overview_container.tsx (74%) rename x-pack/legacy/plugins/uptime/public/components/{functional/overview_page_parsing_error_callout.tsx => overview/parsing_error_callout.tsx} (89%) rename x-pack/legacy/plugins/uptime/{common/domain_types/monitors.ts => public/components/overview/snapshot/index.ts} (71%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview/snapshot}/snapshot.tsx (85%) create mode 100644 x-pack/legacy/plugins/uptime/public/components/overview/snapshot/snapshot_container.tsx rename x-pack/legacy/plugins/uptime/public/components/{functional => overview/snapshot}/snapshot_heading.tsx (100%) rename x-pack/legacy/plugins/uptime/public/components/{functional => overview}/status_panel.tsx (87%) delete mode 100644 x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_autorefresh_interval.test.ts diff --git a/x-pack/legacy/plugins/uptime/common/constants/chart_format_limits.ts b/x-pack/legacy/plugins/uptime/common/constants/chart_format_limits.ts index f291450ab2a7a..3bd204a003c9d 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/chart_format_limits.ts +++ b/x-pack/legacy/plugins/uptime/common/constants/chart_format_limits.ts @@ -11,7 +11,7 @@ const WEEK = DAY * 7; const MONTH = WEEK * 4; /** - * These contsants are used by the charting code to determine + * These constants are used by the charting code to determine * what label should be applied to chart axes so as to help users * understand the timeseries data they're being shown. */ diff --git a/x-pack/legacy/plugins/uptime/common/constants/context_defaults.ts b/x-pack/legacy/plugins/uptime/common/constants/context_defaults.ts index c6b79afd9043b..0c493326add72 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/context_defaults.ts +++ b/x-pack/legacy/plugins/uptime/common/constants/context_defaults.ts @@ -11,7 +11,7 @@ import { CursorDirection, SortOrder } from '../runtime_types'; */ export const CONTEXT_DEFAULTS = { /** - * The application cannot assume a basepath. + * The application cannot assume a basePath. */ BASE_PATH: '', diff --git a/x-pack/legacy/plugins/uptime/common/graphql/types.ts b/x-pack/legacy/plugins/uptime/common/graphql/types.ts new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts b/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts index 9018f4acaa320..e07c46fa01cfe 100644 --- a/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts +++ b/x-pack/legacy/plugins/uptime/common/runtime_types/common.ts @@ -33,7 +33,6 @@ export const DateRangeType = t.type({ }); export type Summary = t.TypeOf; -export type CheckGeo = t.TypeOf; export type Location = t.TypeOf; export type StatesIndexStatus = t.TypeOf; export type DateRange = t.TypeOf; diff --git a/x-pack/legacy/plugins/uptime/common/types/index.ts b/x-pack/legacy/plugins/uptime/common/types/index.ts index fcbb92caf26d5..a32eabd49a3e5 100644 --- a/x-pack/legacy/plugins/uptime/common/types/index.ts +++ b/x-pack/legacy/plugins/uptime/common/types/index.ts @@ -35,3 +35,7 @@ export interface MonitorDurationResult { /** The average values for the monitor duration. */ locationDurationLines: LocationDurationLine[]; } + +export interface MonitorIdParam { + monitorId: string; +} diff --git a/x-pack/legacy/plugins/uptime/public/apps/plugin.ts b/x-pack/legacy/plugins/uptime/public/apps/plugin.ts index eec49418910f8..e73598c44c9f0 100644 --- a/x-pack/legacy/plugins/uptime/public/apps/plugin.ts +++ b/x-pack/legacy/plugins/uptime/public/apps/plugin.ts @@ -4,23 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - LegacyCoreStart, - LegacyCoreSetup, - PluginInitializerContext, - AppMountParameters, -} from 'src/core/public'; -import { PluginsStart, PluginsSetup } from 'ui/new_platform/new_platform'; +import { LegacyCoreSetup, PluginInitializerContext, AppMountParameters } from 'src/core/public'; +import { PluginsSetup } from 'ui/new_platform/new_platform'; import { FeatureCatalogueCategory } from '../../../../../../src/plugins/home/public'; import { UMFrontendLibs } from '../lib/lib'; import { PLUGIN } from '../../common/constants'; import { getKibanaFrameworkAdapter } from '../lib/adapters/framework/new_platform_adapter'; -export interface StartObject { - core: LegacyCoreStart; - plugins: PluginsStart; -} - export interface SetupObject { core: LegacyCoreSetup; plugins: PluginsSetup; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/location_link.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/common/__tests__/__snapshots__/location_link.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/location_link.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/common/__tests__/__snapshots__/location_link.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/common/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/common/__tests__/__snapshots__/uptime_date_picker.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/location_link.test.tsx b/x-pack/legacy/plugins/uptime/public/components/common/__tests__/location_link.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/location_link.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/__tests__/location_link.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/uptime_date_picker.test.tsx b/x-pack/legacy/plugins/uptime/public/components/common/__tests__/uptime_date_picker.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/uptime_date_picker.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/__tests__/uptime_date_picker.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/chart_empty_state.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_empty_state.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/chart_empty_state.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_empty_state.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/chart_wrapper.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_wrapper.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/chart_wrapper.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/chart_wrapper.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/donut_chart_legend.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/donut_chart_legend.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/donut_chart_legend_row.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/duration_charts.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/duration_charts.test.tsx.snap similarity index 99% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/duration_charts.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/duration_charts.test.tsx.snap index 6c38f3e338cfd..96918ab68f716 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/__snapshots__/duration_charts.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/__snapshots__/duration_charts.test.tsx.snap @@ -53,7 +53,6 @@ exports[`MonitorCharts component renders the component without errors 1`] = ` > { const component = shallowWithRouter( diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/get_tick_format.test.ts b/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/get_tick_format.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/get_tick_format.test.ts rename to x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/get_tick_format.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/monitor_bar_series.test.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/monitor_bar_series.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/monitor_bar_series.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/monitor_bar_series.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/ping_histogram.test.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/ping_histogram.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/__tests__/ping_histogram.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/__tests__/ping_histogram.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/annotation_tooltip.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/annotation_tooltip.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/annotation_tooltip.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/annotation_tooltip.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/chart_empty_state.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/chart_empty_state.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/chart_empty_state.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/chart_empty_state.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/chart_wrapper/chart_wrapper.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/chart_wrapper/chart_wrapper.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/chart_wrapper/chart_wrapper.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/chart_wrapper/chart_wrapper.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/chart_wrapper/index.ts b/x-pack/legacy/plugins/uptime/public/components/common/charts/chart_wrapper/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/chart_wrapper/index.ts rename to x-pack/legacy/plugins/uptime/public/components/common/charts/chart_wrapper/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/donut_chart.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/donut_chart.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/donut_chart_legend.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/donut_chart_legend.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart_legend.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/donut_chart_legend_row.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/donut_chart_legend_row.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/donut_chart_legend_row.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/common/charts/duration_chart.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/duration_chart.tsx new file mode 100644 index 0000000000000..c82b2a1cf9fe2 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/common/charts/duration_chart.tsx @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Axis, Chart, Position, timeFormatter, Settings, SeriesIdentifier } from '@elastic/charts'; +import { getChartDateLabel } from '../../../lib/helper'; +import { LocationDurationLine } from '../../../../common/types'; +import { DurationLineSeriesList } from './duration_line_series_list'; +import { ChartWrapper } from './chart_wrapper'; +import { useUrlParams } from '../../../hooks'; +import { getTickFormat } from './get_tick_format'; +import { ChartEmptyState } from './chart_empty_state'; +import { DurationAnomaliesBar } from './duration_line_bar_list'; +import { AnomalyRecords } from '../../../state/actions'; + +interface DurationChartProps { + /** + * Timeseries data that is used to express an average line series + * on the duration chart. One entry per location + */ + locationDurationLines: LocationDurationLine[]; + + /** + * To represent the loading spinner on chart + */ + loading: boolean; + + anomalies: AnomalyRecords | null; +} + +/** + * This chart is intended to visualize monitor duration performance over time to + * the users in a helpful way. Its x-axis is based on a timeseries, the y-axis is in + * milliseconds. + * @param props The props required for this component to render properly + */ +export const DurationChartComponent = ({ + locationDurationLines, + anomalies, + loading, +}: DurationChartProps) => { + const hasLines = locationDurationLines.length > 0; + const [getUrlParams, updateUrlParams] = useUrlParams(); + const { absoluteDateRangeStart: min, absoluteDateRangeEnd: max } = getUrlParams(); + + const [hiddenLegends, setHiddenLegends] = useState([]); + + const onBrushEnd = (minX: number, maxX: number) => { + updateUrlParams({ + dateRangeStart: moment(minX).toISOString(), + dateRangeEnd: moment(maxX).toISOString(), + }); + }; + + const legendToggleVisibility = (legendItem: SeriesIdentifier | null) => { + if (legendItem) { + setHiddenLegends(prevState => { + if (prevState.includes(legendItem.specId)) { + return [...prevState.filter(item => item !== legendItem.specId)]; + } else { + return [...prevState, legendItem.specId]; + } + }); + } + }; + + return ( + + {hasLines ? ( + + + + getTickFormat(d)} + title={i18n.translate('xpack.uptime.monitorCharts.durationChart.leftAxis.title', { + defaultMessage: 'Duration ms', + })} + /> + + + + ) : ( + up }} + /> + } + title={i18n.translate('xpack.uptime.durationChart.emptyPrompt.title', { + defaultMessage: 'No duration data available', + })} + /> + )} + + ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_line_bar_list.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/duration_line_bar_list.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_line_bar_list.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/duration_line_bar_list.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_line_series_list.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/duration_line_series_list.tsx similarity index 97% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_line_series_list.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/duration_line_series_list.tsx index 912bc5bb0501b..4223e918393b6 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_line_series_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/common/charts/duration_line_series_list.tsx @@ -21,7 +21,7 @@ export const DurationLineSeriesList = ({ lines }: Props) => ( // this id is used for the line chart representing the average duration length data={line.map(({ x, y }) => [x, microsToMillis(y || null)])} id={`loc-avg-${name}`} - key={`locline-${name}`} + key={`loc-line-${name}`} name={name} xAccessor={0} xScaleType="time" diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/get_tick_format.ts b/x-pack/legacy/plugins/uptime/public/components/common/charts/get_tick_format.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/get_tick_format.ts rename to x-pack/legacy/plugins/uptime/public/components/common/charts/get_tick_format.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/index.ts b/x-pack/legacy/plugins/uptime/public/components/common/charts/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/index.ts rename to x-pack/legacy/plugins/uptime/public/components/common/charts/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/monitor_bar_series.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/monitor_bar_series.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/monitor_bar_series.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/monitor_bar_series.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/ping_histogram.tsx b/x-pack/legacy/plugins/uptime/public/components/common/charts/ping_histogram.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/charts/ping_histogram.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/charts/ping_histogram.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/common/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/common/higher_order/__tests__/__snapshots__/responsive_wrapper.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/higher_order/__tests__/responsive_wrapper.test.tsx b/x-pack/legacy/plugins/uptime/public/components/common/higher_order/__tests__/responsive_wrapper.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/higher_order/__tests__/responsive_wrapper.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/higher_order/__tests__/responsive_wrapper.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/higher_order/index.ts b/x-pack/legacy/plugins/uptime/public/components/common/higher_order/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/higher_order/index.ts rename to x-pack/legacy/plugins/uptime/public/components/common/higher_order/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/higher_order/responsive_wrapper.tsx b/x-pack/legacy/plugins/uptime/public/components/common/higher_order/responsive_wrapper.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/higher_order/responsive_wrapper.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/higher_order/responsive_wrapper.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/location_link.tsx b/x-pack/legacy/plugins/uptime/public/components/common/location_link.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/location_link.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/location_link.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/uptime_date_picker.tsx b/x-pack/legacy/plugins/uptime/public/components/common/uptime_date_picker.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/uptime_date_picker.tsx rename to x-pack/legacy/plugins/uptime/public/components/common/uptime_date_picker.tsx index 7d2123af8ff9c..4254004dba4e0 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/uptime_date_picker.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/common/uptime_date_picker.tsx @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiSuperDatePicker } from '@elastic/eui'; import React, { useContext } from 'react'; +import { EuiSuperDatePicker } from '@elastic/eui'; import { useUrlParams } from '../../hooks'; import { CLIENT_DEFAULTS } from '../../../common/constants'; import { UptimeRefreshContext, UptimeSettingsContext } from '../../contexts'; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/charts/ping_histogram.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/charts/ping_histogram.tsx deleted file mode 100644 index cf35dbf4e5206..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/connected/charts/ping_histogram.tsx +++ /dev/null @@ -1,82 +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, { useEffect } from 'react'; -import { connect } from 'react-redux'; -import { AppState } from '../../../state'; -import { - PingHistogramComponent, - PingHistogramComponentProps, -} from '../../functional/charts/ping_histogram'; -import { getPingHistogram } from '../../../state/actions'; -import { selectPingHistogram } from '../../../state/selectors'; -import { withResponsiveWrapper, ResponsiveWrapperProps } from '../../higher_order'; -import { GetPingHistogramParams, HistogramResult } from '../../../../common/runtime_types'; -import { useGetUrlParams } from '../../../hooks'; - -type Props = ResponsiveWrapperProps & - Pick & - DispatchProps & { lastRefresh: number; monitorId?: string; esKuery?: string }; - -const PingHistogramContainer: React.FC = ({ - data, - loadData, - monitorId, - lastRefresh, - height, - loading, - esKuery, -}) => { - const { - absoluteDateRangeStart, - absoluteDateRangeEnd, - dateRangeStart: dateStart, - dateRangeEnd: dateEnd, - statusFilter, - } = useGetUrlParams(); - - useEffect(() => { - loadData({ monitorId, dateStart, dateEnd, statusFilter, filters: esKuery }); - }, [loadData, dateStart, dateEnd, monitorId, statusFilter, lastRefresh, esKuery]); - return ( - - ); -}; - -interface StateProps { - data: HistogramResult | null; - loading: boolean; - lastRefresh: number; - esKuery: string; -} - -interface DispatchProps { - loadData: typeof getPingHistogram; -} - -const mapStateToProps = (state: AppState): StateProps => ({ ...selectPingHistogram(state) }); - -const mapDispatchToProps = (dispatch: any): DispatchProps => ({ - loadData: (params: GetPingHistogramParams) => { - return dispatch(getPingHistogram(params)); - }, -}); - -export const PingHistogram = connect< - StateProps, - DispatchProps, - Pick, - AppState ->( - mapStateToProps, - mapDispatchToProps -)(withResponsiveWrapper(PingHistogramContainer)); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx deleted file mode 100644 index 39ead242527f8..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/connected/charts/snapshot_container.tsx +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useEffect } from 'react'; -import { connect } from 'react-redux'; -import { useGetUrlParams } from '../../../hooks'; -import { AppState } from '../../../state'; -import { getSnapshotCountAction } from '../../../state/actions'; -import { SnapshotComponent } from '../../functional/snapshot'; -import { Snapshot as SnapshotType } from '../../../../common/runtime_types'; -import { SnapShotQueryParams } from '../../../state/api'; - -/** - * Props expected from parent components. - */ -interface OwnProps { - /** - * Height is needed, since by default charts takes height of 100% - */ - height?: string; -} - -/** - * Props given by the Redux store based on action input. - */ -interface StoreProps { - count: SnapshotType; - lastRefresh: number; - loading: boolean; - esKuery: string; -} - -/** - * Contains functions that will dispatch actions used - * for this component's life cycle - */ -interface DispatchProps { - loadSnapshotCount: typeof getSnapshotCountAction; -} - -/** - * Props used to render the Snapshot component. - */ -type Props = OwnProps & StoreProps & DispatchProps; - -export const Container: React.FC = ({ - count, - height, - lastRefresh, - loading, - esKuery, - loadSnapshotCount, -}: Props) => { - const { dateRangeStart, dateRangeEnd, statusFilter } = useGetUrlParams(); - - useEffect(() => { - loadSnapshotCount({ dateRangeStart, dateRangeEnd, filters: esKuery, statusFilter }); - }, [dateRangeStart, dateRangeEnd, esKuery, lastRefresh, loadSnapshotCount, statusFilter]); - return ; -}; - -/** - * Provides state to connected component. - * @param state the root app state - */ -const mapStateToProps = ({ - snapshot: { count, loading }, - ui: { lastRefresh, esKuery }, -}: AppState): StoreProps => ({ - count, - lastRefresh, - loading, - esKuery, -}); - -/** - * Used for fetching snapshot counts. - * @param dispatch redux-provided action dispatcher - */ -const mapDispatchToProps = (dispatch: any) => ({ - loadSnapshotCount: (params: SnapShotQueryParams): DispatchProps => { - return dispatch(getSnapshotCountAction(params)); - }, -}); - -export const Snapshot = connect( - // @ts-ignore connect is expecting null | undefined for some reason - mapStateToProps, - mapDispatchToProps -)(Container); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/index.ts b/x-pack/legacy/plugins/uptime/public/components/connected/index.ts deleted file mode 100644 index 94e2529a46a00..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/connected/index.ts +++ /dev/null @@ -1,19 +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 { AlertMonitorStatus, ToggleAlertFlyoutButton, UptimeAlertsFlyoutWrapper } from './alerts'; -export { PingHistogram } from './charts/ping_histogram'; -export { Snapshot } from './charts/snapshot_container'; -export { KueryBar } from './kuerybar/kuery_bar_container'; -export { FilterGroup } from './filter_group/filter_group_container'; -export { MonitorStatusDetails } from './monitor/status_details_container'; -export { MonitorStatusBar } from './monitor/status_bar_container'; -export { MonitorList } from './monitor/monitor_list'; -export { MonitorListDrawer } from './monitor/list_drawer_container'; -export { MonitorListActionsPopover } from './monitor/drawer_popover_container'; -export { PingList, PingListProps } from './pings'; -export { DurationChart } from './charts/monitor_duration'; -export { EmptyState } from './empty_state/empty_state'; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx deleted file mode 100644 index 3a96aa7c0275b..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx +++ /dev/null @@ -1,71 +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, { useContext, useEffect } from 'react'; -import { connect } from 'react-redux'; -import { Dispatch } from 'redux'; -import { AppState } from '../../../state'; -import { monitorLocationsSelector, monitorStatusSelector } from '../../../state/selectors'; -import { MonitorStatusBarComponent } from '../../functional/monitor_status_details/monitor_status_bar'; -import { getMonitorStatusAction } from '../../../state/actions'; -import { useGetUrlParams } from '../../../hooks'; -import { Ping } from '../../../../common/runtime_types'; -import { MonitorLocations } from '../../../../common/runtime_types/monitor'; -import { UptimeRefreshContext } from '../../../contexts'; - -interface StateProps { - monitorStatus: Ping; - monitorLocations: MonitorLocations; -} - -interface DispatchProps { - loadMonitorStatus: typeof getMonitorStatusAction; -} - -interface OwnProps { - monitorId: string; -} - -type Props = OwnProps & StateProps & DispatchProps; - -const Container: React.FC = ({ - loadMonitorStatus, - monitorId, - monitorStatus, - monitorLocations, -}: Props) => { - const { lastRefresh } = useContext(UptimeRefreshContext); - - const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = useGetUrlParams(); - - useEffect(() => { - loadMonitorStatus({ dateStart, dateEnd, monitorId }); - }, [monitorId, dateStart, dateEnd, loadMonitorStatus, lastRefresh]); - - return ( - - ); -}; - -const mapStateToProps = (state: AppState, ownProps: OwnProps) => ({ - monitorStatus: monitorStatusSelector(state), - monitorLocations: monitorLocationsSelector(state, ownProps.monitorId), -}); - -const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ - loadMonitorStatus: params => dispatch(getMonitorStatusAction(params)), -}); - -// @ts-ignore TODO: Investigate typescript issues here -export const MonitorStatusBar = connect( - // @ts-ignore TODO: Investigate typescript issues here - mapStateToProps, - mapDispatchToProps -)(Container); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx deleted file mode 100644 index 9d2e48830fbfe..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_details_container.tsx +++ /dev/null @@ -1,63 +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, { useContext, useEffect } from 'react'; -import { connect } from 'react-redux'; -import { Dispatch } from 'redux'; -import { useGetUrlParams } from '../../../hooks'; -import { AppState } from '../../../state'; -import { monitorLocationsSelector } from '../../../state/selectors'; -import { getMonitorLocationsAction, MonitorLocationsPayload } from '../../../state/actions/monitor'; -import { MonitorStatusDetailsComponent } from '../../functional/monitor_status_details'; -import { MonitorLocations } from '../../../../common/runtime_types'; -import { UptimeRefreshContext } from '../../../contexts'; - -interface OwnProps { - monitorId: string; -} - -interface StoreProps { - monitorLocations: MonitorLocations; -} - -interface DispatchProps { - loadMonitorLocations: typeof getMonitorLocationsAction; -} - -type Props = OwnProps & StoreProps & DispatchProps; - -export const Container: React.FC = ({ - loadMonitorLocations, - monitorLocations, - monitorId, -}: Props) => { - const { lastRefresh } = useContext(UptimeRefreshContext); - - const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = useGetUrlParams(); - - useEffect(() => { - loadMonitorLocations({ dateStart, dateEnd, monitorId }); - }, [loadMonitorLocations, monitorId, dateStart, dateEnd, lastRefresh]); - - return ( - - ); -}; -const mapStateToProps = (state: AppState, { monitorId }: OwnProps) => ({ - monitorLocations: monitorLocationsSelector(state, monitorId), -}); - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - loadMonitorLocations: (params: MonitorLocationsPayload) => { - dispatch(getMonitorLocationsAction(params)); - }, -}); - -export const MonitorStatusDetails = connect( - // @ts-ignore TODO: Investigate typescript issues here - mapStateToProps, - mapDispatchToProps -)(Container); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx deleted file mode 100644 index ec2081d715554..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/duration_chart.tsx +++ /dev/null @@ -1,155 +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, { useState } from 'react'; -import { i18n } from '@kbn/i18n'; -import moment from 'moment'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; -import { Axis, Chart, Position, timeFormatter, Settings, SeriesIdentifier } from '@elastic/charts'; -import { getChartDateLabel } from '../../../lib/helper'; -import { LocationDurationLine } from '../../../../common/types'; -import { DurationLineSeriesList } from './duration_line_series_list'; -import { ChartWrapper } from './chart_wrapper'; -import { useUrlParams } from '../../../hooks'; -import { getTickFormat } from './get_tick_format'; -import { ChartEmptyState } from './chart_empty_state'; -import { DurationAnomaliesBar } from './duration_line_bar_list'; -import { MLIntegrationComponent } from '../../monitor_details/ml/ml_integeration'; -import { AnomalyRecords } from '../../../state/actions'; - -interface DurationChartProps { - /** - * Timeseries data that is used to express an average line series - * on the duration chart. One entry per location - */ - locationDurationLines: LocationDurationLine[]; - - /** - * To represent the loading spinner on chart - */ - loading: boolean; - - hasMLJob: boolean; - - anomalies: AnomalyRecords | null; -} - -/** - * This chart is intended to visualize monitor duration performance over time to - * the users in a helpful way. Its x-axis is based on a timeseries, the y-axis is in - * milliseconds. - * @param props The props required for this component to render properly - */ -export const DurationChartComponent = ({ - locationDurationLines, - anomalies, - loading, - hasMLJob, -}: DurationChartProps) => { - const hasLines = locationDurationLines.length > 0; - const [getUrlParams, updateUrlParams] = useUrlParams(); - const { absoluteDateRangeStart: min, absoluteDateRangeEnd: max } = getUrlParams(); - - const [hiddenLegends, setHiddenLegends] = useState([]); - - const onBrushEnd = (minX: number, maxX: number) => { - updateUrlParams({ - dateRangeStart: moment(minX).toISOString(), - dateRangeEnd: moment(maxX).toISOString(), - }); - }; - - const legendToggleVisibility = (legendItem: SeriesIdentifier | null) => { - if (legendItem) { - setHiddenLegends(prevState => { - if (prevState.includes(legendItem.specId)) { - return [...prevState.filter(item => item !== legendItem.specId)]; - } else { - return [...prevState, legendItem.specId]; - } - }); - } - }; - - return ( - <> - - - - -

- {hasMLJob ? ( - - ) : ( - - )} -

-
-
- - - -
- - - {hasLines ? ( - - - - getTickFormat(d)} - title={i18n.translate('xpack.uptime.monitorCharts.durationChart.leftAxis.title', { - defaultMessage: 'Duration ms', - })} - /> - - - - ) : ( - up }} - /> - } - title={i18n.translate('xpack.uptime.durationChart.emptyPrompt.title', { - defaultMessage: 'No duration data available', - })} - /> - )} - -
- - ); -}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts b/x-pack/legacy/plugins/uptime/public/components/functional/index.ts deleted file mode 100644 index 07809561c31b7..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { - ToggleAlertFlyoutButtonComponent, - UptimeAlertsContextProvider, - UptimeAlertsFlyoutWrapperComponent, -} from './alerts'; -export * from './alerts'; -export { DonutChart } from './charts/donut_chart'; -export { KueryBarComponent } from './kuery_bar/kuery_bar'; -export { MonitorCharts } from './monitor_charts'; -export { MonitorListComponent } from './monitor_list'; -export { OverviewPageParsingErrorCallout } from './overview_page_parsing_error_callout'; -export { PingListComponent } from './ping_list'; -export { PingHistogramComponent } from './charts'; -export { StatusPanel } from './status_panel'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/index.ts b/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/index.ts deleted file mode 100644 index 0cb11587eee48..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/index.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. - */ - -export { MonitorSSLCertificate } from './monitor_ssl_certificate'; -export { MonitorStatusBarComponent } from './monitor_status_bar'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/search_schema.ts b/x-pack/legacy/plugins/uptime/public/components/functional/search_schema.ts deleted file mode 100644 index bd451a9835288..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/components/functional/search_schema.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. - */ - -export const filterBarSearchSchema = { - strict: true, - fields: { - 'monitor.id': { type: 'string' }, - 'monitor.status': { type: 'string' }, - 'monitor.ip': { type: 'string' }, - 'monitor.host': { type: 'string' }, - 'monitor.scheme': { type: 'string' }, - 'url.port': { type: 'number' }, - }, -}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/monitor_charts.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/__tests__/__snapshots__/monitor_charts.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/monitor_charts.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/__tests__/__snapshots__/monitor_charts.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/monitor_charts.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/__tests__/monitor_charts.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/monitor_charts.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/__tests__/monitor_charts.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/index.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/index.ts new file mode 100644 index 0000000000000..cb7b27afded02 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './ml'; +export * from './ping_list'; +export * from './location_map'; +export * from './monitor_status_details'; +export * from './ping_histogram'; +export * from './monitor_charts'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_map.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_map.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_map.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_map.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_missing.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/__snapshots__/location_status_tags.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_map.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_map.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_map.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_map.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_missing.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_missing.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_missing.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_missing.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_status_tags.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_status_tags.test.tsx similarity index 97% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_status_tags.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_status_tags.test.tsx index 2359938dbbc35..7dde38af99fc3 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/__tests__/location_status_tags.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/__tests__/location_status_tags.test.tsx @@ -8,9 +8,8 @@ import React from 'react'; import moment from 'moment'; import { renderWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; import { MonitorLocation } from '../../../../../common/runtime_types/monitor'; -import { LocationStatusTags } from '../'; +import { LocationStatusTags } from '../index'; -// Failing: https://github.com/elastic/kibana/issues/54818 describe('LocationStatusTags component', () => { let monitorLocations: MonitorLocation[]; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/__mocks__/mock.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/__mocks__/mock.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/__mocks__/mock.ts rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/__mocks__/mock.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/map_config.test.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/map_config.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/__tests__/map_config.test.ts rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/__tests__/map_config.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/embedded_map.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/embedded_map.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/low_poly_layer.json b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/low_poly_layer.json similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/low_poly_layer.json rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/low_poly_layer.json diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/map_config.ts similarity index 98% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.ts rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/map_config.ts index a43edae438252..ddb52e119fa87 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/map_config.ts +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/map_config.ts @@ -5,7 +5,7 @@ */ import lowPolyLayerFeatures from './low_poly_layer.json'; -import { LocationPoint } from './embedded_map.js'; +import { LocationPoint } from './embedded_map'; import { UptimeAppColors } from '../../../../uptime_app'; /** diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/translations.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/translations.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/translations.ts rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/embeddables/translations.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/index.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/index.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/index.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/index.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_map.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_map.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_map.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_missing.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_missing.tsx similarity index 97% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_missing.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_missing.tsx index a20889f6cc653..6ce31e4cc8243 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_missing.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_missing.tsx @@ -16,7 +16,7 @@ import { } from '@elastic/eui'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; -import { LocationLink } from '../monitor_list/monitor_list_drawer'; +import { LocationLink } from '../../common/location_link'; const EuiPopoverRight = styled(EuiFlexItem)` margin-left: auto; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_status_tags.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/location_map/location_status_tags.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/location_map/location_status_tags.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/confirm_delete.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/license_info.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/license_info.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/license_info.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_flyout.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_integerations.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_job_link.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_job_link.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_job_link.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_job_link.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/__snapshots__/ml_manage_job.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/confirm_delete.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/confirm_delete.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/confirm_delete.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/confirm_delete.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/license_info.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/license_info.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/license_info.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/license_info.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/ml_flyout.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_flyout.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/ml_flyout.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_flyout.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/ml_integerations.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_integerations.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/ml_integerations.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_integerations.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/ml_job_link.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_job_link.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/ml_job_link.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_job_link.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/ml_manage_job.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_manage_job.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/__tests__/ml_manage_job.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/__tests__/ml_manage_job.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/confirm_delete.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/confirm_delete.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/confirm_delete.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/confirm_delete.tsx diff --git a/x-pack/legacy/plugins/uptime/common/domain_types/index.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/index.ts similarity index 81% rename from x-pack/legacy/plugins/uptime/common/domain_types/index.ts rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/index.ts index 1cf72ca2dac0b..c644c94d13878 100644 --- a/x-pack/legacy/plugins/uptime/common/domain_types/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './monitors'; +export { ManageMLJobComponent } from './manage_ml_job'; diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/license_info.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/license_info.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/license_info.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/license_info.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/manage_ml_job.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx similarity index 97% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/manage_ml_job.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx index ec3e8bb2b9f68..46ac24e9455e5 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/manage_ml_job.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/manage_ml_job.tsx @@ -13,7 +13,7 @@ import { UptimeSettingsContext } from '../../../contexts'; import * as labels from './translations'; import { getMLJobLinkHref } from './ml_job_link'; import { useGetUrlParams } from '../../../hooks'; -import { useMonitorId } from '../../../hooks/use_monitor'; +import { useMonitorId } from '../../../hooks'; interface Props { hasMLJob: boolean; diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_flyout.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_flyout.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_flyout.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_flyout.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_flyout_container.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx similarity index 98% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_flyout_container.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx index a13de192cc443..c3e8579ca4837 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_flyout_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_flyout_container.tsx @@ -25,7 +25,7 @@ import { ML_JOB_ID } from '../../../../common/constants'; import { UptimeRefreshContext, UptimeSettingsContext } from '../../../contexts'; import { useGetUrlParams } from '../../../hooks'; import { getDynamicSettings } from '../../../state/actions/dynamic_settings'; -import { useMonitorId } from '../../../hooks/use_monitor'; +import { useMonitorId } from '../../../hooks'; interface Props { onClose: () => void; diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_integeration.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_integeration.tsx similarity index 98% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_integeration.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_integeration.tsx index e053ca733cb8c..4963a901f0ecc 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_integeration.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_integeration.tsx @@ -21,7 +21,7 @@ import * as labels from './translations'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { ManageMLJobComponent } from './manage_ml_job'; import { JobStat } from '../../../../../../../plugins/ml/common/types/data_recognizer'; -import { useMonitorId } from '../../../hooks/use_monitor'; +import { useMonitorId } from '../../../hooks'; export const MLIntegrationComponent = () => { const [isMlFlyoutOpen, setIsMlFlyoutOpen] = useState(false); diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_job_link.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_job_link.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/ml_job_link.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/ml_job_link.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/translations.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/translations.tsx similarity index 95% rename from x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/translations.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ml/translations.tsx index 32374674771e8..bcc3fca770652 100644 --- a/x-pack/legacy/plugins/uptime/public/components/monitor_details/ml/translations.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/ml/translations.tsx @@ -96,13 +96,6 @@ export const MANAGE_ANOMALY_DETECTION = i18n.translate( } ); -export const VIEW_EXISTING_JOB = i18n.translate( - 'xpack.uptime.ml.enableAnomalyDetectionPanel.callout.jobExistsDescription.viewJobLinkText', - { - defaultMessage: 'View existing job', - } -); - export const ML_MANAGEMENT_PAGE = i18n.translate( 'xpack.uptime.ml.enableAnomalyDetectionPanel.manageMLJobDescription.mlJobsPageLinkText', { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_charts.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_charts.tsx similarity index 68% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_charts.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/monitor_charts.tsx index c5edd0fd85977..f9cc1aa52b902 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_charts.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_charts.tsx @@ -6,7 +6,8 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { PingHistogram, DurationChart } from '../connected'; +import { PingHistogram } from './ping_histogram/ping_histogram_container'; +import { MonitorDuration } from './monitor_duration/monitor_duration_container'; interface MonitorChartsProps { monitorId: string; @@ -16,10 +17,10 @@ export const MonitorCharts = ({ monitorId }: MonitorChartsProps) => { return ( - + - + ); diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/index.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/index.ts new file mode 100644 index 0000000000000..aa3230a3f9bc0 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { MonitorDuration } from './monitor_duration_container'; +export { MonitorDurationComponent } from './monitor_duration'; diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration.tsx new file mode 100644 index 0000000000000..af1c8dbdc49e3 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration.tsx @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; +import { LocationDurationLine } from '../../../../common/types'; +import { MLIntegrationComponent } from '../ml/ml_integeration'; +import { AnomalyRecords } from '../../../state/actions'; +import { DurationChartComponent } from '../../common/charts'; + +interface DurationChartProps { + loading: boolean; + hasMLJob: boolean; + anomalies: AnomalyRecords | null; + locationDurationLines: LocationDurationLine[]; +} + +/** + * This chart is intended to visualize monitor duration performance over time to + * the users in a helpful way. Its x-axis is based on a timeseries, the y-axis is in + * milliseconds. + * @param props The props required for this component to render properly + */ +export const MonitorDurationComponent = ({ + locationDurationLines, + anomalies, + loading, + hasMLJob, +}: DurationChartProps) => { + return ( + + + + +

+ {hasMLJob ? ( + + ) : ( + + )} +

+
+
+ + + +
+ +
+ ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/charts/monitor_duration.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx similarity index 91% rename from x-pack/legacy/plugins/uptime/public/components/connected/charts/monitor_duration.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx index 40480905350af..7e39b977f1271 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/charts/monitor_duration.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_duration/monitor_duration_container.tsx @@ -12,7 +12,6 @@ import { getMLCapabilitiesAction, getMonitorDurationAction, } from '../../../state/actions'; -import { DurationChartComponent } from '../../functional/charts'; import { anomaliesSelector, hasMLFeatureAvailable, @@ -22,12 +21,10 @@ import { import { UptimeRefreshContext } from '../../../contexts'; import { getMLJobId } from '../../../state/api/ml_anomaly'; import { JobStat } from '../../../../../../../plugins/ml/common/types/data_recognizer'; +import { MonitorDurationComponent } from './monitor_duration'; +import { MonitorIdParam } from '../../../../common/types'; -interface Props { - monitorId: string; -} - -export const DurationChart: React.FC = ({ monitorId }: Props) => { +export const MonitorDuration: React.FC = ({ monitorId }) => { const { dateRangeStart, dateRangeEnd, @@ -75,7 +72,7 @@ export const DurationChart: React.FC = ({ monitorId }: Props) => { }, [dispatch]); return ( - { let monitorLocations: MonitorLocation[]; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/index.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/index.ts similarity index 64% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/index.ts rename to x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/index.ts index 385788cc825a0..e95f14472e9e8 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/index.ts @@ -5,5 +5,8 @@ */ export { MonitorStatusBarComponent } from './monitor_status_bar'; -export { MonitorStatusDetailsComponent } from './monitor_status_details'; +export { MonitorStatusDetailsComponent } from './status_details'; export { StatusByLocations } from './monitor_status_bar/status_by_location'; + +export { MonitorStatusDetails } from './status_details_container'; +export { MonitorStatusBar } from './monitor_status_bar/status_bar_container'; diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/index.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/index.ts new file mode 100644 index 0000000000000..3c861412a39e9 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/index.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 { MonitorSSLCertificate } from './ssl_certificate'; +export { MonitorStatusBarComponent } from './status_bar'; +export { MonitorStatusBar } from './status_bar_container'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/monitor_ssl_certificate.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/monitor_ssl_certificate.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/monitor_status_bar.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar.tsx similarity index 95% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/monitor_status_bar.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar.tsx index ac3cedc517995..36159dc29eccd 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/monitor_status_bar.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar.tsx @@ -14,7 +14,7 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; -import { MonitorSSLCertificate } from './monitor_ssl_certificate'; +import { MonitorSSLCertificate } from './ssl_certificate'; import * as labels from './translations'; import { StatusByLocations } from './status_by_location'; import { Ping } from '../../../../../common/runtime_types'; @@ -22,7 +22,7 @@ import { MonitorLocations } from '../../../../../common/runtime_types'; interface MonitorStatusBarProps { monitorId: string; - monitorStatus: Ping; + monitorStatus: Ping | null; monitorLocations: MonitorLocations; } diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar_container.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar_container.tsx new file mode 100644 index 0000000000000..9562295437515 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_bar_container.tsx @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { monitorLocationsSelector, monitorStatusSelector } from '../../../../state/selectors'; +import { MonitorStatusBarComponent } from './index'; +import { getMonitorStatusAction } from '../../../../state/actions'; +import { useGetUrlParams } from '../../../../hooks'; +import { UptimeRefreshContext } from '../../../../contexts'; +import { MonitorIdParam } from '../../../../../common/types'; +import { AppState } from '../../../../state'; + +export const MonitorStatusBar: React.FC = ({ monitorId }) => { + const { lastRefresh } = useContext(UptimeRefreshContext); + + const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = useGetUrlParams(); + + const dispatch = useDispatch(); + + const monitorStatus = useSelector(monitorStatusSelector); + const monitorLocations = useSelector((state: AppState) => + monitorLocationsSelector(state, monitorId) + ); + + useEffect(() => { + dispatch(getMonitorStatusAction({ dateStart, dateEnd, monitorId })); + }, [monitorId, dateStart, dateEnd, lastRefresh, dispatch]); + + return ( + + ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/status_by_location.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_by_location.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/status_by_location.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/status_by_location.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/translations.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/translations.ts similarity index 99% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/translations.ts rename to x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/translations.ts index 1c2844f4f6ccf..f60a1ceeaafb8 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_bar/translations.ts +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/translations.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import { i18n } from '@kbn/i18n'; export const healthStatusMessageAriaLabel = i18n.translate( diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_details.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/status_details.tsx similarity index 97% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_details.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/status_details.tsx index 7dea73da7bba0..ebd16b05ecb4a 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/monitor_status_details.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/status_details.tsx @@ -10,7 +10,7 @@ import styled from 'styled-components'; import { LocationMap } from '../location_map'; import { UptimeRefreshContext } from '../../../contexts'; import { MonitorLocations } from '../../../../common/runtime_types'; -import { MonitorStatusBar } from '../../connected'; +import { MonitorStatusBar } from './monitor_status_bar'; interface MonitorStatusDetailsProps { monitorId: string; diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/status_details_container.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/status_details_container.tsx new file mode 100644 index 0000000000000..251f3562f9d1a --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/status_details_container.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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, { useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { useGetUrlParams } from '../../../hooks'; +import { monitorLocationsSelector } from '../../../state/selectors'; +import { getMonitorLocationsAction } from '../../../state/actions/monitor'; +import { MonitorStatusDetailsComponent } from './index'; +import { UptimeRefreshContext } from '../../../contexts'; +import { AppState } from '../../../state'; +import { MonitorIdParam } from '../../../../common/types'; + +export const MonitorStatusDetails: React.FC = ({ monitorId }) => { + const { lastRefresh } = useContext(UptimeRefreshContext); + + const { dateRangeStart: dateStart, dateRangeEnd: dateEnd } = useGetUrlParams(); + + const dispatch = useDispatch(); + const monitorLocations = useSelector((state: AppState) => + monitorLocationsSelector(state, monitorId) + ); + + useEffect(() => { + dispatch(getMonitorLocationsAction({ dateStart, dateEnd, monitorId })); + }, [monitorId, dateStart, dateEnd, lastRefresh, dispatch]); + + return ( + + ); +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/translations.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/translations.ts similarity index 99% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/translations.ts rename to x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/translations.ts index 1c2844f4f6ccf..f60a1ceeaafb8 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_status_details/translations.ts +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/monitor_status_details/translations.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import { i18n } from '@kbn/i18n'; export const healthStatusMessageAriaLabel = i18n.translate( diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/pings/index.ts b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_histogram/index.ts similarity index 80% rename from x-pack/legacy/plugins/uptime/public/components/connected/pings/index.ts rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_histogram/index.ts index 95ced104e5188..c980b41167d0c 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/pings/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_histogram/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { PingList, PingListProps } from './ping_list'; +export { PingHistogram } from './ping_histogram_container'; diff --git a/x-pack/legacy/plugins/uptime/public/components/monitor/ping_histogram/ping_histogram_container.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_histogram/ping_histogram_container.tsx new file mode 100644 index 0000000000000..c0e17966f5b9f --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_histogram/ping_histogram_container.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under 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 } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { PingHistogramComponent } from '../../common/charts'; +import { getPingHistogram } from '../../../state/actions'; +import { selectPingHistogram } from '../../../state/selectors'; +import { useGetUrlParams } from '../../../hooks'; +import { useMonitorId } from '../../../hooks'; +import { ResponsiveWrapperProps, withResponsiveWrapper } from '../../common/higher_order'; + +interface Props { + height: string; +} + +const Container: React.FC = ({ height }) => { + const { + statusFilter, + absoluteDateRangeStart, + absoluteDateRangeEnd, + dateRangeStart: dateStart, + dateRangeEnd: dateEnd, + } = useGetUrlParams(); + + const dispatch = useDispatch(); + const monitorId = useMonitorId(); + + const { loading, data, esKuery, lastRefresh } = useSelector(selectPingHistogram); + + useEffect(() => { + dispatch(getPingHistogram({ monitorId, dateStart, dateEnd, statusFilter, filters: esKuery })); + }, [dateStart, dateEnd, monitorId, statusFilter, lastRefresh, esKuery, dispatch]); + return ( + + ); +}; + +export const PingHistogram = withResponsiveWrapper(Container); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/__snapshots__/doc_link_body.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/doc_link_body.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/__snapshots__/doc_link_body.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/doc_link_body.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/expanded_row.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/__snapshots__/ping_list.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/doc_link_body.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/doc_link_body.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/doc_link_body.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/doc_link_body.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/expanded_row.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/expanded_row.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/ping_list.test.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx similarity index 99% rename from x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/ping_list.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx index ec256a886aa16..cb8413ba08a81 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/__tests__/ping_list.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { PingListComponent, toggleDetails } from '../ping_list'; -import { ExpandedRowMap } from '../../monitor_list/types'; import { Ping, PingsResponse } from '../../../../../common/runtime_types'; +import { ExpandedRowMap } from '../../../overview/monitor_list/types'; describe('PingList component', () => { let response: PingsResponse; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/doc_link_body.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/doc_link_body.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/ping_list/doc_link_body.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/doc_link_body.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/expanded_row.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/ping_list/expanded_row.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/index.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/index.tsx similarity index 85% rename from x-pack/legacy/plugins/uptime/public/components/functional/ping_list/index.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/index.tsx index 808f3f90ef015..7fc19bbc9622b 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/index.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/index.tsx @@ -5,3 +5,4 @@ */ export { PingListComponent } from './ping_list'; +export { PingList } from './ping_list_container'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/location_name.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/location_name.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/ping_list/location_name.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/location_name.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/ping_list.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx similarity index 98% rename from x-pack/legacy/plugins/uptime/public/components/functional/ping_list/ping_list.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx index 934dfd961f9e0..5dfc1c0647430 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/ping_list/ping_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx @@ -26,9 +26,9 @@ import styled from 'styled-components'; import { Ping, GetPingsParams, DateRange } from '../../../../common/runtime_types'; import { convertMicrosecondsToMilliseconds as microsToMillis } from '../../../lib/helper'; import { LocationName } from './location_name'; -import { Pagination } from './../monitor_list'; +import { Pagination } from '../../overview/monitor_list'; import { PingListExpandedRowComponent } from './expanded_row'; -import { PingListProps } from '../../connected/pings'; +import { PingListProps } from './ping_list_container'; export const AllLocationOption = { 'data-test-subj': 'xpack.uptime.pingList.locationOptions.all', @@ -139,7 +139,7 @@ export const PingListComponent = (props: Props) => { })) ); - const hasStatus: boolean = pings.reduce( + const hasStatus = pings.reduce( (hasHttpStatus: boolean, currentPing) => hasHttpStatus || !!currentPing.http?.response?.status_code, false diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/pings/ping_list.tsx b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx similarity index 96% rename from x-pack/legacy/plugins/uptime/public/components/connected/pings/ping_list.tsx rename to x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx index 5b32a623495f1..3c3caab365e3a 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/pings/ping_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx @@ -10,7 +10,7 @@ import { selectPingList } from '../../../state/selectors'; import { getPings } from '../../../state/actions'; import { GetPingsParams } from '../../../../common/runtime_types'; import { UptimeSettingsContext } from '../../../contexts'; -import { PingListComponent } from '../../functional'; +import { PingListComponent } from './index'; export interface PingListProps { monitorId: string; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/overview_page_parsing_error_callout.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/parsing_error_callout.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/overview_page_parsing_error_callout.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/parsing_error_callout.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/snapshot.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/snapshot.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/snapshot_heading.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot_heading.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/__snapshots__/snapshot_heading.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/__tests__/__snapshots__/snapshot_heading.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/overview_page_parsing_error_callout.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/parsing_error_callout.test.tsx similarity index 76% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/overview_page_parsing_error_callout.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/__tests__/parsing_error_callout.test.tsx index fbe55dfedc2fc..01204c33b79d5 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/overview_page_parsing_error_callout.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/parsing_error_callout.test.tsx @@ -6,13 +6,13 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { OverviewPageParsingErrorCallout } from '../overview_page_parsing_error_callout'; +import { ParsingErrorCallout } from '../parsing_error_callout'; describe('OverviewPageParsingErrorCallout', () => { it('renders without errors when a valid error is provided', () => { expect( shallowWithIntl( - ) @@ -21,6 +21,6 @@ describe('OverviewPageParsingErrorCallout', () => { it('renders without errors when an error with no message is provided', () => { const error: any = {}; - expect(shallowWithIntl()).toMatchSnapshot(); + expect(shallowWithIntl()).toMatchSnapshot(); }); }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/snapshot.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/snapshot.test.tsx similarity index 92% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/snapshot.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/__tests__/snapshot.test.tsx index 214b0394369f7..cfcab673dcb35 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/snapshot.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/snapshot.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { Snapshot } from '../../../../common/runtime_types'; -import { SnapshotComponent } from '../snapshot'; +import { SnapshotComponent } from '../snapshot/snapshot'; describe('Snapshot component', () => { const snapshot: Snapshot = { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/snapshot_heading.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/snapshot_heading.test.tsx similarity index 93% rename from x-pack/legacy/plugins/uptime/public/components/functional/__tests__/snapshot_heading.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/__tests__/snapshot_heading.test.tsx index 70d082b26d653..805c116ef538a 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/__tests__/snapshot_heading.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/__tests__/snapshot_heading.test.tsx @@ -6,7 +6,7 @@ import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import React from 'react'; -import { SnapshotHeading } from '../snapshot_heading'; +import { SnapshotHeading } from '../snapshot/snapshot_heading'; describe('SnapshotHeading', () => { it('renders custom heading for no down monitors', () => { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/alerts/__tests__/alert_monitor_status.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/__tests__/alert_monitor_status.test.tsx similarity index 98% rename from x-pack/legacy/plugins/uptime/public/components/functional/alerts/__tests__/alert_monitor_status.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/alerts/__tests__/alert_monitor_status.test.tsx index af8d17d1fc242..8f33b6f652b9d 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/alerts/__tests__/alert_monitor_status.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/__tests__/alert_monitor_status.test.tsx @@ -14,8 +14,8 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers'; describe('alert monitor status component', () => { describe('handleAlertFieldNumberChange', () => { - let mockSetIsInvalid: jest.Mock; - let mockSetFieldValue: jest.Mock; + let mockSetIsInvalid: jest.Mock; + let mockSetFieldValue: jest.Mock; beforeEach(() => { mockSetIsInvalid = jest.fn(); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/alerts/alert_monitor_status.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alert_monitor_status.tsx similarity index 99% rename from x-pack/legacy/plugins/uptime/public/components/functional/alerts/alert_monitor_status.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/alerts/alert_monitor_status.tsx index b86e85f35b17d..83892bf23dced 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/alerts/alert_monitor_status.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alert_monitor_status.tsx @@ -19,7 +19,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { DataPublicPluginSetup } from 'src/plugins/data/public'; -import { KueryBar } from '../../connected/kuerybar/kuery_bar_container'; +import { KueryBar } from '..'; interface AlertFieldNumberProps { 'aria-label': string; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/alerts/alert_monitor_status.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx similarity index 86% rename from x-pack/legacy/plugins/uptime/public/components/connected/alerts/alert_monitor_status.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx index 1529ab6db8875..9dd27db0be607 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/alerts/alert_monitor_status.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/alert_monitor_status.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { useSelector } from 'react-redux'; import { DataPublicPluginSetup } from 'src/plugins/data/public'; -import { selectMonitorStatusAlert } from '../../../state/selectors'; -import { AlertMonitorStatusComponent } from '../../functional/alerts/alert_monitor_status'; +import { selectMonitorStatusAlert } from '../../../../state/selectors'; +import { AlertMonitorStatusComponent } from '../index'; interface Props { autocomplete: DataPublicPluginSetup['autocomplete']; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/alerts/index.ts b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/connected/alerts/index.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/index.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/alerts/toggle_alert_flyout_button.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx similarity index 80% rename from x-pack/legacy/plugins/uptime/public/components/connected/alerts/toggle_alert_flyout_button.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx index 43b0be45365a1..45ba72d76fba6 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/alerts/toggle_alert_flyout_button.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/toggle_alert_flyout_button.tsx @@ -6,8 +6,8 @@ import React from 'react'; import { useDispatch } from 'react-redux'; -import { ToggleAlertFlyoutButtonComponent } from '../../functional'; -import { setAlertFlyoutVisible } from '../../../state/actions'; +import { setAlertFlyoutVisible } from '../../../../state/actions'; +import { ToggleAlertFlyoutButtonComponent } from '../index'; export const ToggleAlertFlyoutButton = () => { const dispatch = useDispatch(); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/alerts/uptime_alerts_flyout_wrapper.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx similarity index 83% rename from x-pack/legacy/plugins/uptime/public/components/connected/alerts/uptime_alerts_flyout_wrapper.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx index a49468ad3dd06..7bfd44a762455 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/alerts/uptime_alerts_flyout_wrapper.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/alerts_containers/uptime_alerts_flyout_wrapper.tsx @@ -6,9 +6,9 @@ import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { UptimeAlertsFlyoutWrapperComponent } from '../../functional'; -import { setAlertFlyoutVisible } from '../../../state/actions'; -import { selectAlertFlyoutVisibility } from '../../../state/selectors'; +import { setAlertFlyoutVisible } from '../../../../state/actions'; +import { selectAlertFlyoutVisibility } from '../../../../state/selectors'; +import { UptimeAlertsFlyoutWrapperComponent } from '../index'; interface Props { alertTypeId?: string; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/alerts/index.ts b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/index.ts similarity index 93% rename from x-pack/legacy/plugins/uptime/public/components/functional/alerts/index.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/alerts/index.ts index 275333b60c5ee..5ca0f4c3fe8a7 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/alerts/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/index.ts @@ -8,3 +8,4 @@ export { AlertMonitorStatusComponent } from './alert_monitor_status'; export { ToggleAlertFlyoutButtonComponent } from './toggle_alert_flyout_button'; export { UptimeAlertsContextProvider } from './uptime_alerts_context_provider'; export { UptimeAlertsFlyoutWrapperComponent } from './uptime_alerts_flyout_wrapper'; +export * from './alerts_containers'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/alerts/toggle_alert_flyout_button.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/alerts/toggle_alert_flyout_button.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/alerts/toggle_alert_flyout_button.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/alerts/uptime_alerts_context_provider.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/alerts/uptime_alerts_context_provider.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/alerts/uptime_alerts_context_provider.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/alerts/uptime_alerts_flyout_wrapper.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/alerts/uptime_alerts_flyout_wrapper.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/alerts/uptime_alerts_flyout_wrapper.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/data_or_index_missing.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/data_or_index_missing.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/data_or_index_missing.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/data_or_index_missing.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/__snapshots__/empty_state.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/data_or_index_missing.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/data_or_index_missing.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/data_or_index_missing.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/data_or_index_missing.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/empty_state.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/empty_state.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/empty_state/__tests__/empty_state.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/empty_state/__tests__/empty_state.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/data_or_index_missing.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/empty_state/data_or_index_missing.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/empty_state/data_or_index_missing.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx similarity index 95% rename from x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx index b0868af70480a..9a62cb9cdaeee 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/empty_state/empty_state.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_container.tsx @@ -8,7 +8,7 @@ import React, { useContext, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { indexStatusAction } from '../../../state/actions'; import { indexStatusSelector, selectDynamicSettings } from '../../../state/selectors'; -import { EmptyStateComponent } from '../../functional/empty_state/empty_state'; +import { EmptyStateComponent } from './index'; import { UptimeRefreshContext } from '../../../contexts'; import { getDynamicSettings } from '../../../state/actions/dynamic_settings'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_error.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_error.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_error.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_loading.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_loading.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/empty_state/empty_state_loading.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/empty_state/empty_state_loading.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/index.ts b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/index.ts new file mode 100644 index 0000000000000..9f2a668f4c3a5 --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/overview/empty_state/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { EmptyStateComponent } from './empty_state'; +export { EmptyState } from './empty_state_container'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_popover.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/filter_status_button.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/parse_filter_map.test.ts.snap b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/parse_filter_map.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/__snapshots__/parse_filter_map.test.ts.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/__snapshots__/parse_filter_map.test.ts.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/filter_popover.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/filter_popover.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/filter_popover.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/filter_popover.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/filter_status_button.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/filter_status_button.test.tsx similarity index 93% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/filter_status_button.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/filter_status_button.test.tsx index 1813229a97d1b..2ad4d971cf3b0 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/filter_status_button.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/filter_status_button.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { FilterStatusButton, FilterStatusButtonProps } from '../filter_status_button'; -import { shallowWithRouter } from '../../../../lib/'; +import { shallowWithRouter } from '../../../../lib'; describe('FilterStatusButton', () => { let props: FilterStatusButtonProps; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/parse_filter_map.test.ts b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/parse_filter_map.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/parse_filter_map.test.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/parse_filter_map.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/toggle_selected_item.test.ts b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/toggle_selected_item.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/__tests__/toggle_selected_item.test.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/__tests__/toggle_selected_item.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_group.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_group.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_group.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_group.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/filter_group/filter_group_container.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_group_container.tsx similarity index 94% rename from x-pack/legacy/plugins/uptime/public/components/connected/filter_group/filter_group_container.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_group_container.tsx index 569c6bb883cbd..3612604fdf116 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/filter_group/filter_group_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_group_container.tsx @@ -7,10 +7,10 @@ import React, { useContext, useEffect } from 'react'; import { connect } from 'react-redux'; import { useUrlParams } from '../../../hooks'; -import { parseFiltersMap } from '../../functional/filter_group/parse_filter_map'; +import { parseFiltersMap } from './parse_filter_map'; import { AppState } from '../../../state'; import { fetchOverviewFilters, GetOverviewFiltersPayload } from '../../../state/actions'; -import { FilterGroupComponent } from '../../functional/filter_group'; +import { FilterGroupComponent } from './index'; import { OverviewFilters } from '../../../../common/runtime_types/overview_filters'; import { UptimeRefreshContext } from '../../../contexts'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_popover.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_popover.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_popover.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_status_button.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_status_button.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/filter_status_button.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/filter_status_button.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/index.ts b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/index.ts similarity index 84% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/index.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/index.ts index 2aae026144d8f..933fddf1cde27 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/index.ts @@ -5,3 +5,4 @@ */ export { FilterGroupComponent } from './filter_group'; +export { FilterGroup } from './filter_group_container'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/parse_filter_map.ts b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/parse_filter_map.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/parse_filter_map.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/parse_filter_map.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/toggle_selected_item.ts b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/toggle_selected_item.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/toggle_selected_item.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/filter_group/uptime_filter_button.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/filter_group/uptime_filter_button.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/filter_group/uptime_filter_button.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/filter_group/uptime_filter_button.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/index.ts b/x-pack/legacy/plugins/uptime/public/components/overview/index.ts new file mode 100644 index 0000000000000..ac293e9233c8c --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/overview/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './monitor_list'; +export * from './empty_state'; +export * from './filter_group'; +export * from './alerts'; +export * from './snapshot'; +export * from './kuery_bar'; + +export { ParsingErrorCallout } from './parsing_error_callout'; diff --git a/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/index.ts b/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/index.ts new file mode 100644 index 0000000000000..60801a0ab705a --- /dev/null +++ b/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { KueryBarComponent } from './kuery_bar'; +export { KueryBar } from './kuery_bar_container'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/kuery_bar.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/kuery_bar.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/kuery_bar_container.tsx similarity index 90% rename from x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/kuery_bar_container.tsx index 132ae57b5154f..5e1e184b2d6e6 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/kuerybar/kuery_bar_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/kuery_bar_container.tsx @@ -8,7 +8,7 @@ import { connect } from 'react-redux'; import { AppState } from '../../../state'; import { selectIndexPattern } from '../../../state/selectors'; import { getIndexPattern } from '../../../state/actions'; -import { KueryBarComponent } from '../../functional/kuery_bar/kuery_bar'; +import { KueryBarComponent } from './kuery_bar'; const mapStateToProps = (state: AppState) => ({ ...selectIndexPattern(state) }); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/click_outside.js b/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/click_outside.js similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/click_outside.js rename to x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/click_outside.js diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/index.d.ts b/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.d.ts similarity index 95% rename from x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/index.d.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.d.ts index c9f43b3a620bd..defde6203a8c5 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/index.d.ts +++ b/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.d.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component } from 'react'; +import React from 'react'; interface TypeaheadProps { onChange: (inputValue: string, selectionStart: number) => void; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/index.js b/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.js similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/index.js rename to x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/index.js diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/suggestion.js b/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/suggestion.js rename to x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/suggestions.js b/x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/kuery_bar/typeahead/suggestions.js rename to x-pack/legacy/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_status_column.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list_status_column.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_list_status_column.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list_status_column.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_page_size_select.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_page_size_select.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_page_size_select.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_page_size_select.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_status_column.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_status_column.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_list_status_column.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list_status_column.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_page_link.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_page_link.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/monitor_page_link.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_page_link.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/parse_timestamp.test.ts b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/parse_timestamp.test.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/__tests__/parse_timestamp.test.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/__tests__/parse_timestamp.test.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/index.ts b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/index.ts similarity index 69% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/index.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/index.ts index 2dc43050f9515..45e8822a317a4 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/index.ts @@ -7,3 +7,5 @@ export { MonitorListComponent } from './monitor_list'; export { Criteria, Pagination } from './types'; export { LocationLink } from './monitor_list_drawer'; +export { MonitorListDrawer } from './monitor_list_drawer/list_drawer_container'; +export { ActionsPopover } from './monitor_list_drawer/actions_popover/actions_popover_container'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx similarity index 97% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx index 6705101ccd572..18e2e2437e147 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx @@ -22,13 +22,13 @@ import { HistogramPoint, FetchMonitorStatesQueryArgs } from '../../../../common/ import { MonitorSummary } from '../../../../common/runtime_types'; import { MonitorListStatusColumn } from './monitor_list_status_column'; import { ExpandedRowMap } from './types'; -import { MonitorBarSeries } from '../charts'; +import { MonitorBarSeries } from '../../common/charts'; import { MonitorPageLink } from './monitor_page_link'; import { OverviewPageLink } from './overview_page_link'; import * as labels from './translations'; -import { MonitorListDrawer } from '../../connected'; import { MonitorListPageSizeSelect } from './monitor_list_page_size_select'; -import { MonitorListProps } from '../../connected/monitor/monitor_list'; +import { MonitorListDrawer } from './monitor_list_drawer/list_drawer_container'; +import { MonitorListProps } from './monitor_list_container'; import { MonitorList } from '../../../state/reducers/monitor_list'; import { useUrlParams } from '../../../hooks'; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/monitor_list.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_container.tsx similarity index 93% rename from x-pack/legacy/plugins/uptime/public/components/connected/monitor/monitor_list.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_container.tsx index c9457664566b7..5bfe6ff0c5b4f 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/monitor_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_container.tsx @@ -9,7 +9,7 @@ import { useSelector, useDispatch } from 'react-redux'; import { getMonitorList } from '../../../state/actions'; import { FetchMonitorStatesQueryArgs } from '../../../../common/runtime_types'; import { monitorListSelector } from '../../../state/selectors'; -import { MonitorListComponent } from '../../functional/monitor_list'; +import { MonitorListComponent } from './index'; export interface MonitorListProps { filters?: string; @@ -18,13 +18,16 @@ export interface MonitorListProps { export const MonitorList: React.FC = props => { const dispatch = useDispatch(); + const dispatchCallback = useCallback( (params: FetchMonitorStatesQueryArgs) => { dispatch(getMonitorList(params)); }, [dispatch] ); + const monitorListState = useSelector(monitorListSelector); + return ( ); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_group.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_link.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_link.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_link.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/integration_link.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_list_drawer.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_list.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/monitor_status_row.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/__snapshots__/most_recent_error.test.tsx.snap diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/data.json similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/data.json rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/data.json diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/integration_group.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_group.test.tsx similarity index 94% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/integration_group.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_group.test.tsx index 48fa2c9766681..25cf400bcd0fd 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/integration_group.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_group.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { MonitorSummary } from '../../../../../../common/runtime_types'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { IntegrationGroup } from '../integration_group'; +import { IntegrationGroup } from '../actions_popover/integration_group'; describe('IntegrationGroup', () => { let summary: MonitorSummary; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx similarity index 93% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx index ba313f255f13d..8ee83bc38957b 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/integration_link.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { IntegrationLink } from '../integration_link'; +import { IntegrationLink } from '../actions_popover/integration_link'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; describe('IntegrationLink component', () => { diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_list_drawer.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_list.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/monitor_status_row.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/__tests__/most_recent_error.test.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_actions_popover.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx similarity index 89% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_actions_popover.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx index c57ecbaa4d3ed..e86e6b309214f 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_actions_popover.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover.tsx @@ -9,20 +9,20 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { EuiPopover, EuiButton } from '@elastic/eui'; import { IntegrationGroup } from './integration_group'; -import { MonitorSummary } from '../../../../../common/runtime_types'; -import { toggleIntegrationsPopover, PopoverState } from '../../../../state/actions'; +import { MonitorSummary } from '../../../../../../common/runtime_types'; +import { toggleIntegrationsPopover, PopoverState } from '../../../../../state/actions'; -interface MonitorListActionsPopoverProps { +interface ActionsPopoverProps { summary: MonitorSummary; popoverState: PopoverState | null; togglePopoverIsVisible: typeof toggleIntegrationsPopover; } -export const MonitorListActionsPopoverComponent = ({ +export const ActionsPopoverComponent = ({ summary, popoverState, togglePopoverIsVisible, -}: MonitorListActionsPopoverProps) => { +}: ActionsPopoverProps) => { const popoverId = `${summary.monitor_id}_popover`; const monitorUrl: string | undefined = get(summary, 'state.url.full', undefined); diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/drawer_popover_container.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx similarity index 63% rename from x-pack/legacy/plugins/uptime/public/components/connected/monitor/drawer_popover_container.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx index be29e12f716a9..b1c25ddd7a338 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/drawer_popover_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/actions_popover_container.tsx @@ -5,10 +5,10 @@ */ import { connect } from 'react-redux'; -import { AppState } from '../../../state'; -import { isIntegrationsPopupOpen } from '../../../state/selectors'; -import { PopoverState, toggleIntegrationsPopover } from '../../../state/actions'; -import { MonitorListActionsPopoverComponent } from '../../functional/monitor_list/monitor_list_drawer'; +import { AppState } from '../../../../../state'; +import { isIntegrationsPopupOpen } from '../../../../../state/selectors'; +import { PopoverState, toggleIntegrationsPopover } from '../../../../../state/actions'; +import { ActionsPopoverComponent } from '../index'; const mapStateToProps = (state: AppState) => ({ popoverState: isIntegrationsPopupOpen(state), @@ -20,7 +20,4 @@ const mapDispatchToProps = (dispatch: any) => ({ }, }); -export const MonitorListActionsPopover = connect( - mapStateToProps, - mapDispatchToProps -)(MonitorListActionsPopoverComponent); +export const ActionsPopover = connect(mapStateToProps, mapDispatchToProps)(ActionsPopoverComponent); diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/integration_group.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx similarity index 98% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/integration_group.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx index cc06b9a2306c7..bbcba7238748d 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/integration_group.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_group.tsx @@ -18,9 +18,9 @@ import { getLoggingContainerHref, getLoggingIpHref, getLoggingKubernetesHref, -} from '../../../../lib/helper'; -import { MonitorSummary } from '../../../../../common/runtime_types'; -import { UptimeSettingsContext } from '../../../../contexts'; +} from '../../../../../lib/helper'; +import { MonitorSummary } from '../../../../../../common/runtime_types'; +import { UptimeSettingsContext } from '../../../../../contexts'; interface IntegrationGroupProps { summary: MonitorSummary; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/integration_link.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_link.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/integration_link.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/actions_popover/integration_link.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/index.ts b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/index.ts similarity index 63% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/index.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/index.ts index 2933a71c2240b..32c722b806f2b 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/index.ts +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/index.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { LocationLink } from './location_link'; -export { MonitorListActionsPopoverComponent } from './monitor_list_actions_popover'; +export { LocationLink } from '../../../common/location_link'; +export { ActionsPopoverComponent } from './actions_popover/actions_popover'; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/list_drawer_container.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx similarity index 72% rename from x-pack/legacy/plugins/uptime/public/components/connected/monitor/list_drawer_container.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx index 37a96ba0396aa..bec32ace27f2b 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/list_drawer_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/list_drawer_container.tsx @@ -6,13 +6,13 @@ import React, { useEffect } from 'react'; import { connect } from 'react-redux'; -import { AppState } from '../../../state'; -import { monitorDetailsSelector } from '../../../state/selectors'; -import { MonitorDetailsActionPayload } from '../../../state/actions/types'; -import { getMonitorDetailsAction } from '../../../state/actions/monitor'; -import { MonitorListDrawerComponent } from '../../functional/monitor_list/monitor_list_drawer/monitor_list_drawer'; -import { useGetUrlParams } from '../../../hooks'; -import { MonitorDetails, MonitorSummary } from '../../../../common/runtime_types'; +import { AppState } from '../../../../state'; +import { monitorDetailsSelector } from '../../../../state/selectors'; +import { MonitorDetailsActionPayload } from '../../../../state/actions/types'; +import { getMonitorDetailsAction } from '../../../../state/actions/monitor'; +import { MonitorListDrawerComponent } from './monitor_list_drawer'; +import { useGetUrlParams } from '../../../../hooks'; +import { MonitorDetails, MonitorSummary } from '../../../../../common/runtime_types'; interface ContainerProps { summary: MonitorSummary; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx similarity index 93% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx index 6dc9ebbef1287..8e97ce4d692d7 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_list_drawer.tsx @@ -10,7 +10,7 @@ import { EuiLink, EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiText } from import { MostRecentError } from './most_recent_error'; import { MonitorStatusList } from './monitor_status_list'; import { MonitorDetails, MonitorSummary } from '../../../../../common/runtime_types'; -import { MonitorListActionsPopover } from '../../../connected'; +import { ActionsPopover } from './actions_popover/actions_popover_container'; const ContainerDiv = styled.div` padding: 10px; @@ -48,7 +48,7 @@ export function MonitorListDrawerComponent({ summary, monitorDetails }: MonitorL - + diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_status_list.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx similarity index 97% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_status_list.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx index 8a46167dcd3bc..cd1a5a95b8adb 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_status_list.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_list.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { get, capitalize } from 'lodash'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { LocationLink } from './location_link'; +import { LocationLink } from '../../../common/location_link'; import { MonitorStatusRow } from './monitor_status_row'; import { Check } from '../../../../../common/runtime_types'; import { STATUS, UNNAMED_LOCATION } from '../../../../../common/constants'; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_status_row.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/monitor_status_row.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/monitor_status_row.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/most_recent_error.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_drawer/most_recent_error.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_drawer/most_recent_error.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_page_size_select.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_page_size_select.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_page_size_select.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_page_size_select.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_status_column.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_status_column.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_list_status_column.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_list_status_column.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_page_link.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_page_link.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/monitor_page_link.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/monitor_page_link.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/overview_page_link.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/overview_page_link.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/overview_page_link.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/overview_page_link.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/parse_timestamp.ts b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/parse_timestamp.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/parse_timestamp.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/parse_timestamp.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/translations.ts b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/translations.ts similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/translations.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/translations.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/types.ts b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/types.ts similarity index 83% rename from x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/types.ts rename to x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/types.ts index a25603d3603d9..6a6cee4a7d96d 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/monitor_list/types.ts +++ b/x-pack/legacy/plugins/uptime/public/components/overview/monitor_list/types.ts @@ -4,13 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface CondensedCheck { - childStatuses: CondensedCheckStatus[]; - location: string | null; - status: string; - timestamp: string; -} - export interface CondensedCheckStatus { ip?: string | null; status: string; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/overview_container.tsx similarity index 74% rename from x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/overview_container.tsx index 79aaa071507e1..d64e489c48076 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/pages/overview_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/overview_container.tsx @@ -5,10 +5,10 @@ */ import { connect } from 'react-redux'; -import { OverviewPageComponent } from '../../../pages/overview'; -import { selectIndexPattern } from '../../../state/selectors'; -import { AppState } from '../../../state'; -import { setEsKueryString } from '../../../state/actions'; +import { OverviewPageComponent } from '../../pages/overview'; +import { selectIndexPattern } from '../../state/selectors'; +import { AppState } from '../../state'; +import { setEsKueryString } from '../../state/actions'; interface DispatchProps { setEsKueryFilters: typeof setEsKueryString; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/overview_page_parsing_error_callout.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/parsing_error_callout.tsx similarity index 89% rename from x-pack/legacy/plugins/uptime/public/components/functional/overview_page_parsing_error_callout.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/parsing_error_callout.tsx index b71a4f2f8646a..96ea14cdf9f37 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/overview_page_parsing_error_callout.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/parsing_error_callout.tsx @@ -13,13 +13,11 @@ interface HasMessage { message: string; } -interface OverviewPageParsingErrorCalloutProps { +interface ParsingErrorCalloutProps { error: HasMessage; } -export const OverviewPageParsingErrorCallout = ({ - error, -}: OverviewPageParsingErrorCalloutProps) => ( +export const ParsingErrorCallout = ({ error }: ParsingErrorCalloutProps) => ( = ({ height }: Props) => { + const { dateRangeStart, dateRangeEnd, statusFilter } = useGetUrlParams(); + + const { count, lastRefresh, loading, esKuery } = useSelector(snapshotDataSelector); + + const dispatch = useDispatch(); + + useEffect(() => { + dispatch( + getSnapshotCountAction({ dateRangeStart, dateRangeEnd, filters: esKuery, statusFilter }) + ); + }, [dateRangeStart, dateRangeEnd, esKuery, lastRefresh, statusFilter, dispatch]); + return ; +}; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/snapshot_heading.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/snapshot/snapshot_heading.tsx similarity index 100% rename from x-pack/legacy/plugins/uptime/public/components/functional/snapshot_heading.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/snapshot/snapshot_heading.tsx diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/status_panel.tsx b/x-pack/legacy/plugins/uptime/public/components/overview/status_panel.tsx similarity index 87% rename from x-pack/legacy/plugins/uptime/public/components/functional/status_panel.tsx rename to x-pack/legacy/plugins/uptime/public/components/overview/status_panel.tsx index 2c0be2aa15d6f..9edcb08a6d5b1 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/status_panel.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/overview/status_panel.tsx @@ -6,7 +6,8 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; -import { PingHistogram, Snapshot } from '../connected'; +import { PingHistogram } from '../monitor'; +import { Snapshot } from './snapshot/snapshot_container'; const STATUS_CHART_HEIGHT = '160px'; diff --git a/x-pack/legacy/plugins/uptime/public/contexts/uptime_settings_context.tsx b/x-pack/legacy/plugins/uptime/public/contexts/uptime_settings_context.tsx index c5a0ec4831798..137846de103b4 100644 --- a/x-pack/legacy/plugins/uptime/public/contexts/uptime_settings_context.tsx +++ b/x-pack/legacy/plugins/uptime/public/contexts/uptime_settings_context.tsx @@ -7,7 +7,7 @@ import React, { createContext, useMemo } from 'react'; import { UptimeAppProps } from '../uptime_app'; import { CLIENT_DEFAULTS, CONTEXT_DEFAULTS } from '../../common/constants'; -import { CommonlyUsedRange } from '../components/functional/uptime_date_picker'; +import { CommonlyUsedRange } from '../components/common/uptime_date_picker'; import { useGetUrlParams } from '../hooks'; import { ILicense } from '../../../../../plugins/licensing/common/types'; diff --git a/x-pack/legacy/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx b/x-pack/legacy/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx index 85961003fce72..1ce00fe7ce3af 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx +++ b/x-pack/legacy/plugins/uptime/public/hooks/__tests__/use_breadcrumbs.test.tsx @@ -11,7 +11,7 @@ import { mountWithRouter } from '../../lib'; import { OVERVIEW_ROUTE } from '../../../common/constants'; import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; import { UptimeUrlParams, getSupportedUrlParams } from '../../lib/helper'; -import { makeBaseBreadcrumb, useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { makeBaseBreadcrumb, useBreadcrumbs } from '../use_breadcrumbs'; describe('useBreadcrumbs', () => { it('sets the given breadcrumbs', () => { diff --git a/x-pack/legacy/plugins/uptime/public/lib/alert_types/index.ts b/x-pack/legacy/plugins/uptime/public/lib/alert_types/index.ts index f764505a6d683..74160577cb0b1 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/alert_types/index.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/alert_types/index.ts @@ -6,7 +6,7 @@ // TODO: after NP migration is complete we should be able to remove this lint ignore comment // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertTypeModel } from '../../../../../../plugins/triggers_actions_ui/public/types'; +import { AlertTypeModel } from '../../../../../../plugins/triggers_actions_ui/public'; import { initMonitorStatusAlertType } from './monitor_status'; export type AlertTypeInitializer = (dependenies: { autocomplete: any }) => AlertTypeModel; diff --git a/x-pack/legacy/plugins/uptime/public/lib/alert_types/monitor_status.tsx b/x-pack/legacy/plugins/uptime/public/lib/alert_types/monitor_status.tsx index d059274159c7f..0624d20b197c0 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/alert_types/monitor_status.tsx +++ b/x-pack/legacy/plugins/uptime/public/lib/alert_types/monitor_status.tsx @@ -16,7 +16,7 @@ import { } from '../../../../../../plugins/triggers_actions_ui/public/types'; import { AlertTypeInitializer } from '.'; import { StatusCheckExecutorParamsType } from '../../../common/runtime_types'; -import { AlertMonitorStatus } from '../../components/connected/alerts'; +import { AlertMonitorStatus } from '../../components/overview/alerts/alerts_containers'; export const validate = (alertParams: any): ValidationResult => { const errors: Record = {}; diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_chart_date_label.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_chart_date_label.ts index 126b1d85f749f..aa5a2b0f60e4f 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_chart_date_label.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_chart_date_label.ts @@ -11,7 +11,7 @@ import { CHART_FORMAT_LIMITS } from '../../../../common/constants'; /** * Generates an appropriate date formatting string intended for the y-axis * label of timeseries charts. The function will return day/month values for shorter - * timespans that cross the local date threshold, otherwise it estimates an appropriate + * time spans that cross the local date threshold, otherwise it estimates an appropriate * label for several different stops. * @param dateRangeStart the beginning of the date range * @param dateRangeEnd the end of the date range diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_label_format.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_label_format.ts index 668147fee8055..5957123e9257d 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_label_format.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/charts/get_label_format.ts @@ -49,7 +49,7 @@ const dateStops: Array<{ key: number; value: string }> = [ ]; /** - * Returns an appropriate label format bbased on pre-defined intervals. + * Returns an appropriate label format based on pre-defined intervals. * @param delta The length of the timespan in milliseconds */ export const getLabelFormat = (delta: number): string => { diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/convert_measurements.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/convert_measurements.ts index 4ad9b81b9e660..da97b6400a9a5 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/convert_measurements.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/convert_measurements.ts @@ -8,7 +8,7 @@ const NUM_MICROSECONDS_IN_MILLISECOND = 1000; /** * This simply converts microseconds to milliseconds. People tend to prefer ms to us - * when visualizaing request duration times. + * when visualizing request duration times. */ export const convertMicrosecondsToMilliseconds = (microseconds: number | null): number | null => { if (!microseconds && microseconds !== 0) return null; diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_absolute_date.test.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_absolute_date.test.ts index 691b38bdf9ca2..16888aec21cfe 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_absolute_date.test.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_absolute_date.test.ts @@ -24,7 +24,7 @@ describe('parseAbsoluteDate', () => { it('returns the default value if the parser provides `undefined`', () => { dateMathSpy.mockReturnValue(undefined); - const result = parseAbsoluteDate('this is not a valid datae', 12345); + const result = parseAbsoluteDate('this is not a valid date', 12345); expect(result).toBe(12345); }); }); diff --git a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_autorefresh_interval.test.ts b/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_autorefresh_interval.test.ts deleted file mode 100644 index a5c2168378089..0000000000000 --- a/x-pack/legacy/plugins/uptime/public/lib/helper/url_params/__tests__/parse_autorefresh_interval.test.ts +++ /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 { parseUrlInt } from '../parse_url_int'; - -describe('parseUrlInt', () => { - it('parses a number', () => { - const result = parseUrlInt('23', 50); - expect(result).toBe(23); - }); - - it('returns default value for empty string', () => { - const result = parseUrlInt('', 50); - expect(result).toBe(50); - }); - - it('returns default value for non-numeric string', () => { - const result = parseUrlInt('abc', 50); - expect(result).toBe(50); - }); -}); diff --git a/x-pack/legacy/plugins/uptime/public/lib/lib.ts b/x-pack/legacy/plugins/uptime/public/lib/lib.ts index 6b6191441c931..7dd3aa9eed5ce 100644 --- a/x-pack/legacy/plugins/uptime/public/lib/lib.ts +++ b/x-pack/legacy/plugins/uptime/public/lib/lib.ts @@ -5,7 +5,6 @@ */ import { ReactElement } from 'react'; -import { ChromeBreadcrumb } from 'src/core/public'; import { UMBadge } from '../badge'; import { UptimeAppProps } from '../uptime_app'; @@ -13,8 +12,6 @@ export interface UMFrontendLibs { framework: UMFrameworkAdapter; } -export type UMUpdateBreadcrumbs = (breadcrumbs: ChromeBreadcrumb[]) => void; - export type UMUpdateBadge = (badge: UMBadge) => void; export type BootstrapUptimeApp = (props: UptimeAppProps) => ReactElement; diff --git a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx index 683af81239c99..4495be9b24dc1 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx @@ -8,12 +8,13 @@ import { EuiSpacer } from '@elastic/eui'; import React from 'react'; import { useSelector } from 'react-redux'; import { useTrackPageview } from '../../../../../plugins/observability/public'; -import { MonitorStatusDetails, PingList } from '../components/connected'; import { monitorStatusSelector } from '../state/selectors'; import { PageHeader } from './page_header'; -import { MonitorCharts } from '../components/functional'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; import { useMonitorId, useUptimeTelemetry, UptimePage } from '../hooks'; +import { MonitorCharts } from '../components/monitor'; +import { MonitorStatusDetails } from '../components/monitor'; +import { PingList } from '../components/monitor'; export const MonitorPage: React.FC = () => { const monitorId = useMonitorId(); diff --git a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx index 5b51a208a4c37..adc36efa6f7db 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/overview.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/overview.tsx @@ -8,15 +8,16 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import React, { useEffect } from 'react'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; -import { OverviewPageParsingErrorCallout, StatusPanel } from '../components/functional'; import { useUptimeTelemetry, UptimePage, useGetUrlParams } from '../hooks'; import { stringifyUrlParams } from '../lib/helper/stringify_url_params'; import { useTrackPageview } from '../../../../../plugins/observability/public'; import { DataPublicPluginSetup, IIndexPattern } from '../../../../../../src/plugins/data/public'; -import { EmptyState, FilterGroup, KueryBar, MonitorList } from '../components/connected'; import { useUpdateKueryString } from '../hooks'; import { PageHeader } from './page_header'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; +import { MonitorList } from '../components/overview/monitor_list/monitor_list_container'; +import { EmptyState, FilterGroup, KueryBar, ParsingErrorCallout } from '../components/overview'; +import { StatusPanel } from '../components/overview/status_panel'; interface OverviewPageProps { autocomplete: DataPublicPluginSetup['autocomplete']; @@ -75,7 +76,7 @@ export const OverviewPageComponent = ({ autocomplete, indexPattern, setEsKueryFi - {error && } + {error && } diff --git a/x-pack/legacy/plugins/uptime/public/pages/page_header.tsx b/x-pack/legacy/plugins/uptime/public/pages/page_header.tsx index 49e6ddb56602c..b10bc6ba44f8a 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/page_header.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/page_header.tsx @@ -8,9 +8,9 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSpacer, EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Link } from 'react-router-dom'; -import { UptimeDatePicker } from '../components/functional/uptime_date_picker'; +import { UptimeDatePicker } from '../components/common/uptime_date_picker'; import { SETTINGS_ROUTE } from '../../common/constants'; -import { ToggleAlertFlyoutButton } from '../components/connected'; +import { ToggleAlertFlyoutButton } from '../components/overview/alerts/alerts_containers'; interface PageHeaderProps { headingText: string; diff --git a/x-pack/legacy/plugins/uptime/public/routes.tsx b/x-pack/legacy/plugins/uptime/public/routes.tsx index bb0700287dbf1..b5e20ef8a70a9 100644 --- a/x-pack/legacy/plugins/uptime/public/routes.tsx +++ b/x-pack/legacy/plugins/uptime/public/routes.tsx @@ -7,7 +7,7 @@ import React, { FC } from 'react'; import { Route, Switch } from 'react-router-dom'; import { DataPublicPluginSetup } from '../../../../../src/plugins/data/public'; -import { OverviewPage } from './components/connected/pages/overview_container'; +import { OverviewPage } from './components/overview/overview_container'; import { MONITOR_ROUTE, OVERVIEW_ROUTE, SETTINGS_ROUTE } from '../common/constants'; import { MonitorPage, NotFoundPage, SettingsPage } from './pages'; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts b/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts index b36eccca98da9..c3d0a0180cf51 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/monitor.ts @@ -8,7 +8,7 @@ import { BaseParams } from './types'; import { MonitorDetailsType, MonitorLocationsType } from '../../../common/runtime_types'; import { QueryParams } from '../actions/types'; import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; interface ApiRequest { monitorId: string; diff --git a/x-pack/legacy/plugins/uptime/public/state/api/monitor_duration.ts b/x-pack/legacy/plugins/uptime/public/state/api/monitor_duration.ts index daf725119fcf3..91034f1784b15 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/monitor_duration.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/monitor_duration.ts @@ -6,7 +6,7 @@ import { BaseParams } from './types'; import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; export const fetchMonitorDuration = async ({ monitorId, dateStart, dateEnd }: BaseParams) => { const queryParams = { diff --git a/x-pack/legacy/plugins/uptime/public/state/api/overview_filters.ts b/x-pack/legacy/plugins/uptime/public/state/api/overview_filters.ts index 9943bc27f11f0..6330d8a912210 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/overview_filters.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/overview_filters.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { GetOverviewFiltersPayload } from '../actions/overview_filters'; +import { GetOverviewFiltersPayload } from '../actions'; import { OverviewFiltersType } from '../../../common/runtime_types'; import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; export const fetchOverviewFilters = async ({ dateRangeStart, diff --git a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts index bbd3ddf399dcc..6de27879a49f5 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/ping.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/ping.ts @@ -13,7 +13,7 @@ import { HistogramResult, } from '../../../common/runtime_types'; import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; export const fetchPings: APIFn = async ({ dateRange: { from, to }, diff --git a/x-pack/legacy/plugins/uptime/public/state/api/snapshot.ts b/x-pack/legacy/plugins/uptime/public/state/api/snapshot.ts index e663d0241d688..9ee53dd2cbcef 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/snapshot.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/snapshot.ts @@ -6,7 +6,7 @@ import { SnapshotType, Snapshot } from '../../../common/runtime_types'; import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants/rest_api'; +import { API_URLS } from '../../../common/constants'; export interface SnapShotQueryParams { dateRangeStart: string; diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts index fa3e377b5ebf4..a98e89a27a711 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts @@ -26,7 +26,7 @@ type MonitorStatusPayload = QueryParams & Ping; export const monitorStatusReducer = handleActions( { - [String(getMonitorStatusAction)]: (state, action: Action) => ({ + [String(getMonitorStatusAction)]: state => ({ ...state, loading: true, }), @@ -43,7 +43,7 @@ export const monitorStatusReducer = handleActions) => ({ + [String(getMonitorStatusActionFail)]: state => ({ ...state, loading: false, }), diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/ui.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/ui.ts index 702d314250521..c533f293fc940 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/ui.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/ui.ts @@ -13,7 +13,7 @@ import { triggerAppRefresh, UiPayload, setAlertFlyoutVisible, -} from '../actions/ui'; +} from '../actions'; export interface UiState { alertFlyoutVisible: boolean; diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts index dc5df3f93804d..7260c61f44147 100644 --- a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts @@ -46,6 +46,16 @@ export const selectPingList = ({ pingList, ui: { lastRefresh } }: AppState) => ( lastRefresh, }); +export const snapshotDataSelector = ({ + snapshot: { count, loading }, + ui: { lastRefresh, esKuery }, +}: AppState) => ({ + count, + lastRefresh, + loading, + esKuery, +}); + const mlCapabilitiesSelector = (state: AppState) => state.ml.mlCapabilities.data; export const hasMLFeatureAvailable = createSelector( diff --git a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx index 556e5b9bf299e..92775a2663863 100644 --- a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx +++ b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx @@ -18,12 +18,14 @@ import { UptimeSettingsContextProvider, UptimeThemeContextProvider, } from './contexts'; -import { CommonlyUsedRange } from './components/functional/uptime_date_picker'; +import { CommonlyUsedRange } from './components/common/uptime_date_picker'; import { store } from './state'; import { setBasePath } from './state/actions'; import { PageRouter } from './routes'; -import { UptimeAlertsFlyoutWrapper } from './components/connected'; -import { UptimeAlertsContextProvider } from './components/functional/alerts'; +import { + UptimeAlertsContextProvider, + UptimeAlertsFlyoutWrapper, +} from './components/overview/alerts'; import { kibanaService } from './state/kibana_service'; export interface UptimeAppColors { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 1170d270e42ac..4e1217ac9e7b5 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -16254,7 +16254,6 @@ "xpack.uptime.locationName.helpLinkAnnotation": "場所を追加", "xpack.uptime.ml.durationChart.exploreInMlApp": "ML アプリで探索", "xpack.uptime.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle": "異常検知", - "xpack.uptime.ml.enableAnomalyDetectionPanel.callout.jobExistsDescription.viewJobLinkText": "既存のジョブを表示", "xpack.uptime.ml.enableAnomalyDetectionPanel.createMLJobDescription": "ここでは稼働状況監視の応答時間について異常スコアを計算する機械学習ジョブを作成できます。\n 有効にすると、詳細ページの監視期間チャートに予想範囲が表示され、グラフに異常の注釈が付きます。\n 地理的な地域にわたって遅延が増える期間を特定することもできます。", "xpack.uptime.ml.enableAnomalyDetectionPanel.createNewJobButtonLabel": "新規ジョブを作成", "xpack.uptime.ml.enableAnomalyDetectionPanel.disableAnomalyDetectionTitle": "異常検知を無効にする", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f86e03b6c10e1..bfdcc8b865313 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -16259,7 +16259,6 @@ "xpack.uptime.locationName.helpLinkAnnotation": "添加位置", "xpack.uptime.ml.durationChart.exploreInMlApp": "在 ML 应用中浏览", "xpack.uptime.ml.enableAnomalyDetectionPanel.anomalyDetectionTitle": "异常检测", - "xpack.uptime.ml.enableAnomalyDetectionPanel.callout.jobExistsDescription.viewJobLinkText": "查看现有作业", "xpack.uptime.ml.enableAnomalyDetectionPanel.createMLJobDescription": "在此处可以创建 Machine Learning 作业,以便为运行时间监测计算\n 响应持续时间的异常分数。启用后,详情页面上的监测持续时间图表\n 将显示预期边界并使用异常标注图表。您还可能\n 识别在所有地理区域的延迟增长时段。", "xpack.uptime.ml.enableAnomalyDetectionPanel.createNewJobButtonLabel": "创建新作业", "xpack.uptime.ml.enableAnomalyDetectionPanel.disableAnomalyDetectionTitle": "禁用异常检测", From 9226d4cc94d54b2caaf0b12f98758c0d2df2234b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Mon, 20 Apr 2020 12:43:34 +0100 Subject: [PATCH 03/34] [Telemetry] Use staging if it's not a distributable release (#63875) * [Telemetry] Use staging if it's not a distributable release (instead of 'dev' vs. 'prod' approach) * Rename isProd to isDistributable and useProdKey --- src/plugins/telemetry/server/config.ts | 8 ++++---- .../server/encryption/encrypt.test.ts | 18 +++++++++--------- .../server/encryption/encrypt.ts | 11 +++++++---- .../server/plugin.ts | 10 ++++------ .../server/types.ts | 1 - .../telemetry_collection/get_all_stats.test.ts | 2 -- .../get_stats_with_xpack.test.ts | 1 - 7 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/plugins/telemetry/server/config.ts b/src/plugins/telemetry/server/config.ts index 9621a8b5619b2..99dde0c3b3d96 100644 --- a/src/plugins/telemetry/server/config.ts +++ b/src/plugins/telemetry/server/config.ts @@ -36,8 +36,8 @@ export const configSchema = schema.object({ config: schema.string({ defaultValue: getConfigPath() }), banner: schema.boolean({ defaultValue: true }), url: schema.conditional( - schema.contextRef('dev'), - schema.literal(true), + schema.contextRef('dist'), + schema.literal(false), // Point to staging if it's not a distributable release schema.string({ defaultValue: `https://telemetry-staging.elastic.co/xpack/${ENDPOINT_VERSION}/send`, }), @@ -46,8 +46,8 @@ export const configSchema = schema.object({ }) ), optInStatusUrl: schema.conditional( - schema.contextRef('dev'), - schema.literal(true), + schema.contextRef('dist'), + schema.literal(false), // Point to staging if it's not a distributable release schema.string({ defaultValue: `https://telemetry-staging.elastic.co/opt_in_status/${ENDPOINT_VERSION}/send`, }), diff --git a/src/plugins/telemetry_collection_manager/server/encryption/encrypt.test.ts b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.test.ts index c04625eb1dd42..6d64268569e06 100644 --- a/src/plugins/telemetry_collection_manager/server/encryption/encrypt.test.ts +++ b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.test.ts @@ -22,14 +22,14 @@ import { encryptTelemetry, getKID } from './encrypt'; describe('getKID', () => { it(`returns 'kibana_dev' kid for development`, async () => { - const isProd = false; - const kid = getKID(isProd); + const useProdKey = false; + const kid = getKID(useProdKey); expect(kid).toBe('kibana_dev'); }); it(`returns 'kibana_prod' kid for development`, async () => { - const isProd = true; - const kid = getKID(isProd); + const useProdKey = true; + const kid = getKID(useProdKey); expect(kid).toBe('kibana'); }); }); @@ -41,19 +41,19 @@ describe('encryptTelemetry', () => { it('encrypts payload', async () => { const payload = { some: 'value' }; - await encryptTelemetry(payload, { isProd: true }); + await encryptTelemetry(payload, { useProdKey: true }); expect(createRequestEncryptor).toBeCalledWith(telemetryJWKS); }); - it('uses kibana kid on { isProd: true }', async () => { + it('uses kibana kid on { useProdKey: true }', async () => { const payload = { some: 'value' }; - await encryptTelemetry(payload, { isProd: true }); + await encryptTelemetry(payload, { useProdKey: true }); expect(mockEncrypt).toBeCalledWith('kibana', payload); }); - it('uses kibana_dev kid on { isProd: false }', async () => { + it('uses kibana_dev kid on { useProdKey: false }', async () => { const payload = { some: 'value' }; - await encryptTelemetry(payload, { isProd: false }); + await encryptTelemetry(payload, { useProdKey: false }); expect(mockEncrypt).toBeCalledWith('kibana_dev', payload); }); }); diff --git a/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts index 44f053064cfcb..89f34d794f059 100644 --- a/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts +++ b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts @@ -20,12 +20,15 @@ import { createRequestEncryptor } from '@elastic/request-crypto'; import { telemetryJWKS } from './telemetry_jwks'; -export function getKID(isProd = false): string { - return isProd ? 'kibana' : 'kibana_dev'; +export function getKID(useProdKey = false): string { + return useProdKey ? 'kibana' : 'kibana_dev'; } -export async function encryptTelemetry(payload: any, { isProd = false } = {}): Promise { - const kid = getKID(isProd); +export async function encryptTelemetry( + payload: any, + { useProdKey = false } = {} +): Promise { + const kid = getKID(useProdKey); const encryptor = await createRequestEncryptor(telemetryJWKS); const clusters = [].concat(payload); return Promise.all(clusters.map((cluster: any) => encryptor.encrypt(kid, cluster))); diff --git a/src/plugins/telemetry_collection_manager/server/plugin.ts b/src/plugins/telemetry_collection_manager/server/plugin.ts index f2f20e215c535..0b57fae83c0fb 100644 --- a/src/plugins/telemetry_collection_manager/server/plugin.ts +++ b/src/plugins/telemetry_collection_manager/server/plugin.ts @@ -50,12 +50,12 @@ export class TelemetryCollectionManagerPlugin private readonly collections: Array> = []; private usageGetterMethodPriority = -1; private usageCollection?: UsageCollectionSetup; - private readonly isDev: boolean; + private readonly isDistributable: boolean; private readonly version: string; constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); - this.isDev = initializerContext.env.mode.dev; + this.isDistributable = initializerContext.env.packageInfo.dist; this.version = initializerContext.env.packageInfo.version; } @@ -158,7 +158,7 @@ export class TelemetryCollectionManagerPlugin if (config.unencrypted) { return optInStats; } - return encryptTelemetry(optInStats, { isProd: !this.isDev }); + return encryptTelemetry(optInStats, { useProdKey: this.isDistributable }); } } catch (err) { this.logger.debug(`Failed to collect any opt in stats with registered collections.`); @@ -176,7 +176,6 @@ export class TelemetryCollectionManagerPlugin ) => { const context: StatsCollectionContext = { logger: this.logger.get(collection.title), - isDev: this.isDev, version: this.version, ...collection.customContext, }; @@ -206,7 +205,7 @@ export class TelemetryCollectionManagerPlugin return usageData; } - return encryptTelemetry(usageData, { isProd: !this.isDev }); + return encryptTelemetry(usageData, { useProdKey: this.isDistributable }); } } catch (err) { this.logger.debug( @@ -225,7 +224,6 @@ export class TelemetryCollectionManagerPlugin ): Promise { const context: StatsCollectionContext = { logger: this.logger.get(collection.title), - isDev: this.isDev, version: this.version, ...collection.customContext, }; diff --git a/src/plugins/telemetry_collection_manager/server/types.ts b/src/plugins/telemetry_collection_manager/server/types.ts index e23d6a4c388f4..d3a47694d38a7 100644 --- a/src/plugins/telemetry_collection_manager/server/types.ts +++ b/src/plugins/telemetry_collection_manager/server/types.ts @@ -101,7 +101,6 @@ export interface ESLicense { export interface StatsCollectionContext { logger: Logger; - isDev: boolean; version: string; } diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts b/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts index 1a9f2a4da32c2..f0ad6399c6c72 100644 --- a/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts +++ b/x-pack/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts @@ -182,7 +182,6 @@ describe('get_all_stats', () => { }, { logger: coreMock.createPluginInitializerContext().logger.get('test'), - isDev: true, version: 'version', maxBucketSize: 1, } @@ -208,7 +207,6 @@ describe('get_all_stats', () => { }, { logger: coreMock.createPluginInitializerContext().logger.get('test'), - isDev: true, version: 'version', maxBucketSize: 1, } diff --git a/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts b/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts index f2a9995098e59..5dfe3d3e99a7f 100644 --- a/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts +++ b/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts @@ -31,7 +31,6 @@ const kibana = { const getContext = () => ({ version: '8675309-snapshot', - isDev: true, logger: coreMock.createPluginInitializerContext().logger.get('test'), }); From 21dda535ebfa31b794cc0a1ba608570bcd6a2256 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Mon, 20 Apr 2020 14:08:04 +0200 Subject: [PATCH 04/34] =?UTF-8?q?Upgrade=20`papaparse`=20dependency=20(`4.?= =?UTF-8?q?6.3`=20=E2=86=92=20`5.2.0`).=20(#63879)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- x-pack/package.json | 4 ++-- yarn.lock | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/x-pack/package.json b/x-pack/package.json index a4fdb17f52fe5..3c6146b491f60 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -85,7 +85,7 @@ "@types/node-fetch": "^2.5.0", "@types/nodemailer": "^6.2.1", "@types/object-hash": "^1.3.0", - "@types/papaparse": "^4.5.11", + "@types/papaparse": "^5.0.3", "@types/pngjs": "^3.3.2", "@types/prop-types": "^15.5.3", "@types/proper-lockfile": "^3.0.1", @@ -290,7 +290,7 @@ "oboe": "^2.1.4", "oppsy": "^2.0.0", "p-retry": "^4.2.0", - "papaparse": "^4.6.3", + "papaparse": "^5.2.0", "pdfmake": "^0.1.63", "pluralize": "3.1.0", "pngjs": "3.4.0", diff --git a/yarn.lock b/yarn.lock index 45540cd2675b7..b47befbf9057b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4477,10 +4477,10 @@ dependencies: "@types/node" "*" -"@types/papaparse@^4.5.11": - version "4.5.11" - resolved "https://registry.yarnpkg.com/@types/papaparse/-/papaparse-4.5.11.tgz#dcd4f64da55f768c2e2cf92ccac1973c67a73890" - integrity sha512-zOw6K7YyA/NuZ2yZ8lzZFe2U3fn+vFfcRfiQp4ZJHG6y8WYWy2SYFbq6mp4yUgpIruJHBjKZtgyE0vvCoWEq+A== +"@types/papaparse@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/papaparse/-/papaparse-5.0.3.tgz#7cedc1ebc9484819af8306a8b42f9f08ca9bdb44" + integrity sha512-SgWGWnBGxl6XgjKDM2eoDg163ZFQtH6m6C2aOuaAf1T2gUB3rjaiPDDARbY9WlacRgZqieRG9imAfJaJ+5ouDA== dependencies: "@types/node" "*" @@ -22585,10 +22585,10 @@ pako@~1.0.5: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" integrity sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg== -papaparse@^4.6.3: - version "4.6.3" - resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-4.6.3.tgz#742e5eaaa97fa6c7e1358d2934d8f18f44aee781" - integrity sha512-LRq7BrHC2kHPBYSD50aKuw/B/dGcg29omyJbKWY3KsYUZU69RKwaBHu13jGmCYBtOc4odsLCrFyk6imfyNubJQ== +papaparse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.2.0.tgz#97976a1b135c46612773029153dc64995caa3b7b" + integrity sha512-ylq1wgUSnagU+MKQtNeVqrPhZuMYBvOSL00DHycFTCxownF95gpLAk1HiHdUW77N8yxRq1qHXLdlIPyBSG9NSA== parallel-transform@^1.1.0: version "1.1.0" From 8f7bb05169d148751fc66e235dfe77ab0315dee0 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 20 Apr 2020 15:04:14 +0200 Subject: [PATCH 05/34] [Discuss] Remove expressions plugin's dependency on inspector plugin (#63841) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 💡 use inspector service in visualizations to open it * refactor: 💡 remove expressions plugin dependency on inspector * test: 💍 fix Jest mock * fix: 🐛 remove Inspectore from Expressions plugin dependency inf * docs: ✏️ add JSDocs for createStartServicesGetter() method * test: 💍 fix TypeScript errors in expressions mocks --- src/plugins/expressions/kibana.json | 3 +- src/plugins/expressions/public/loader.ts | 16 ++----- src/plugins/expressions/public/mocks.tsx | 3 -- src/plugins/expressions/public/plugin.ts | 9 +--- .../public/react_expression_renderer.tsx | 3 +- src/plugins/expressions/public/render.ts | 3 +- src/plugins/expressions/public/services.ts | 3 -- .../core/create_start_service_getter.ts | 42 +++++++++++++++++++ src/plugins/visualizations/kibana.json | 2 +- .../create_vis_embeddable_from_object.ts | 4 +- .../public/embeddable/visualize_embeddable.ts | 17 ++++++-- .../visualize_embeddable_factory.tsx | 13 ++++-- src/plugins/visualizations/public/mocks.ts | 5 ++- src/plugins/visualizations/public/plugin.ts | 34 ++++++++++----- 14 files changed, 107 insertions(+), 50 deletions(-) diff --git a/src/plugins/expressions/kibana.json b/src/plugins/expressions/kibana.json index cba693dd4bc20..5d2112103e94d 100644 --- a/src/plugins/expressions/kibana.json +++ b/src/plugins/expressions/kibana.json @@ -4,7 +4,6 @@ "server": true, "ui": true, "requiredPlugins": [ - "bfetch", - "inspector" + "bfetch" ] } diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts index fbe2f37c648d6..418ff6fdf8614 100644 --- a/src/plugins/expressions/public/loader.ts +++ b/src/plugins/expressions/public/loader.ts @@ -19,13 +19,14 @@ import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { filter, map } from 'rxjs/operators'; -import { Adapters, InspectorSession } from '../../inspector/public'; -import { ExpressionRenderHandler } from './render'; +import { Adapters } from '../../inspector/public'; import { IExpressionLoaderParams } from './types'; import { ExpressionAstExpression } from '../common'; -import { getInspector, getExpressionsService } from './services'; import { ExecutionContract } from '../common/execution/execution_contract'; +import { ExpressionRenderHandler } from './render'; +import { getExpressionsService } from './services'; + type Data = any; export class ExpressionLoader { @@ -120,15 +121,6 @@ export class ExpressionLoader { return this.renderHandler.getElement(); } - openInspector(title: string): InspectorSession | undefined { - const inspector = this.inspect(); - if (inspector) { - return getInspector().open(inspector, { - title, - }); - } - } - inspect(): Adapters | undefined { return this.execution ? (this.execution.inspect() as Adapters) : undefined; } diff --git a/src/plugins/expressions/public/mocks.tsx b/src/plugins/expressions/public/mocks.tsx index cb7089f814643..b8f2f693e9c77 100644 --- a/src/plugins/expressions/public/mocks.tsx +++ b/src/plugins/expressions/public/mocks.tsx @@ -22,7 +22,6 @@ import { ExpressionsSetup, ExpressionsStart, plugin as pluginInitializer } from /* eslint-disable */ import { coreMock } from '../../../core/public/mocks'; -import { inspectorPluginMock } from '../../inspector/public/mocks'; import { bfetchPluginMock } from '../../bfetch/public/mocks'; /* eslint-enable */ @@ -89,7 +88,6 @@ const createPlugin = async () => { const plugin = pluginInitializer(pluginInitializerContext); const setup = await plugin.setup(coreSetup, { bfetch: bfetchPluginMock.createSetupContract(), - inspector: inspectorPluginMock.createSetupContract(), }); return { @@ -101,7 +99,6 @@ const createPlugin = async () => { doStart: async () => await plugin.start(coreStart, { bfetch: bfetchPluginMock.createStartContract(), - inspector: inspectorPluginMock.createStartContract(), }), }; }; diff --git a/src/plugins/expressions/public/plugin.ts b/src/plugins/expressions/public/plugin.ts index 7c0de271b7706..720c3b701d504 100644 --- a/src/plugins/expressions/public/plugin.ts +++ b/src/plugins/expressions/public/plugin.ts @@ -29,11 +29,9 @@ import { ExpressionsServiceStart, ExecutionContext, } from '../common'; -import { Setup as InspectorSetup, Start as InspectorStart } from '../../inspector/public'; import { BfetchPublicSetup, BfetchPublicStart } from '../../bfetch/public'; import { setCoreStart, - setInspector, setInterpreter, setRenderersRegistry, setNotifications, @@ -45,12 +43,10 @@ import { render, ExpressionRenderHandler } from './render'; export interface ExpressionsSetupDeps { bfetch: BfetchPublicSetup; - inspector: InspectorSetup; } export interface ExpressionsStartDeps { bfetch: BfetchPublicStart; - inspector: InspectorStart; } export interface ExpressionsSetup extends ExpressionsServiceSetup { @@ -120,7 +116,7 @@ export class ExpressionsPublicPlugin }); } - public setup(core: CoreSetup, { inspector, bfetch }: ExpressionsSetupDeps): ExpressionsSetup { + public setup(core: CoreSetup, { bfetch }: ExpressionsSetupDeps): ExpressionsSetup { this.configureExecutor(core); const { expressions } = this; @@ -180,9 +176,8 @@ export class ExpressionsPublicPlugin return Object.freeze(setup); } - public start(core: CoreStart, { inspector, bfetch }: ExpressionsStartDeps): ExpressionsStart { + public start(core: CoreStart, { bfetch }: ExpressionsStartDeps): ExpressionsStart { setCoreStart(core); - setInspector(inspector); setNotifications(core.notifications); const { expressions } = this; diff --git a/src/plugins/expressions/public/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer.tsx index 242a49c6d6639..2c99f173c9f33 100644 --- a/src/plugins/expressions/public/react_expression_renderer.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.tsx @@ -17,8 +17,7 @@ * under the License. */ -import { useRef, useEffect, useState, useLayoutEffect } from 'react'; -import React from 'react'; +import React, { useRef, useEffect, useState, useLayoutEffect } from 'react'; import classNames from 'classnames'; import { Subscription } from 'rxjs'; import { filter } from 'rxjs/operators'; diff --git a/src/plugins/expressions/public/render.ts b/src/plugins/expressions/public/render.ts index ad4d16bcd1323..4aaf0da60fc60 100644 --- a/src/plugins/expressions/public/render.ts +++ b/src/plugins/expressions/public/render.ts @@ -21,10 +21,11 @@ import * as Rx from 'rxjs'; import { Observable } from 'rxjs'; import { filter } from 'rxjs/operators'; import { RenderError, RenderErrorHandlerFnType, IExpressionLoaderParams } from './types'; -import { getRenderersRegistry } from './services'; import { renderErrorHandler as defaultRenderErrorHandler } from './render_error_handler'; import { IInterpreterRenderHandlers, ExpressionAstExpression } from '../common'; +import { getRenderersRegistry } from './services'; + export type IExpressionRendererExtraHandlers = Record; export interface ExpressionRenderHandlerParams { diff --git a/src/plugins/expressions/public/services.ts b/src/plugins/expressions/public/services.ts index a203e87414571..016456c956666 100644 --- a/src/plugins/expressions/public/services.ts +++ b/src/plugins/expressions/public/services.ts @@ -20,14 +20,11 @@ import { NotificationsStart } from 'kibana/public'; import { createKibanaUtilsCore, createGetterSetter } from '../../kibana_utils/public'; import { ExpressionInterpreter } from './types'; -import { Start as IInspector } from '../../inspector/public'; import { ExpressionsSetup } from './plugin'; import { ExpressionsService } from '../common'; export const { getCoreStart, setCoreStart } = createKibanaUtilsCore(); -export const [getInspector, setInspector] = createGetterSetter('Inspector'); - export const [getInterpreter, setInterpreter] = createGetterSetter( 'Interpreter' ); diff --git a/src/plugins/kibana_utils/public/core/create_start_service_getter.ts b/src/plugins/kibana_utils/public/core/create_start_service_getter.ts index e507d1ae778e5..5e385eb5ed473 100644 --- a/src/plugins/kibana_utils/public/core/create_start_service_getter.ts +++ b/src/plugins/kibana_utils/public/core/create_start_service_getter.ts @@ -30,6 +30,48 @@ export type StartServicesGetter = () = OwnContract >; +/** + * Use this utility to create a synchronous *start* service getter in *setup* + * life-cycle of your plugin. + * + * Below is a usage example in a Kibana plugin. + * + * ```ts + * export interface MyPluginStartDeps { + * data: DataPublicPluginStart; + * expressions: ExpressionsStart; + * inspector: InspectorStart; + * uiActions: UiActionsStart; + * } + * + * class MyPlugin implements Plugin { + * setup(core: CoreSetup, plugins) { + * const start = createStartServicesGetter(core.getStartServices); + * plugins.expressions.registerFunction(myExpressionFunction(start)); + * } + * + * start(core, plugins: MyPluginStartDeps) { + * + * } + * } + * ``` + * + * In `myExpressionFunction` you can make sure you are picking only the dependencies + * your function needs using the `Pick` type. + * + * ```ts + * const myExpressionFunction = + * (start: StartServicesGetter>) => { + * + * start().plugins.indexPatterns.something(123); + * } + * ``` + * + * @param accessor Asynchronous start service accessor provided by platform. + * @returns Returns a function which synchronously returns *start* core services + * and plugin contracts. If you call this function before the *start* life-cycle + * has started it will throw. + */ export const createStartServicesGetter = ( accessor: StartServicesAccessor ): StartServicesGetter => { diff --git a/src/plugins/visualizations/kibana.json b/src/plugins/visualizations/kibana.json index cd22b1375ae1b..f3f9cbd8341ec 100644 --- a/src/plugins/visualizations/kibana.json +++ b/src/plugins/visualizations/kibana.json @@ -3,5 +3,5 @@ "version": "kibana", "server": true, "ui": true, - "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "usageCollection"] + "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "usageCollection", "inspector"] } diff --git a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts index bf2d174f594b2..8e51bd4ac5d4f 100644 --- a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts +++ b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts @@ -28,8 +28,9 @@ import { getTimeFilter, getCapabilities, } from '../services'; +import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory'; -export const createVisEmbeddableFromObject = async ( +export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDeps) => async ( vis: Vis, input: Partial & { id: string }, parent?: IContainer @@ -58,6 +59,7 @@ export const createVisEmbeddableFromObject = async ( indexPatterns, editUrl, editable, + deps, }, input, parent diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index e64d200251797..ffb028ff131b3 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -42,6 +42,7 @@ import { buildPipeline } from '../legacy/build_pipeline'; import { Vis } from '../vis'; import { getExpressions, getUiActions } from '../services'; import { VIS_EVENT_TO_TRIGGER } from './events'; +import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory'; const getKeys = (o: T): Array => Object.keys(o) as Array; @@ -50,6 +51,7 @@ export interface VisualizeEmbeddableConfiguration { indexPatterns?: IIndexPattern[]; editUrl: string; editable: boolean; + deps: VisualizeEmbeddableFactoryDeps; } export interface VisualizeInput extends EmbeddableInput { @@ -84,10 +86,11 @@ export class VisualizeEmbeddable extends Embeddable { - if (this.handler) { - return this.handler.openInspector(this.getTitle() || ''); - } + if (!this.handler) return; + + const adapters = this.handler.inspect(); + if (!adapters) return; + + this.deps.start().plugins.inspector.open(adapters, { + title: this.getTitle() || '', + }); }; /** diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 4b7d01ae3b246..6ab1c98645988 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -25,7 +25,7 @@ import { EmbeddableOutput, ErrorEmbeddable, IContainer, -} from '../../../../plugins/embeddable/public'; +} from '../../../embeddable/public'; import { DisabledLabEmbeddable } from './disabled_lab_embeddable'; import { VisualizeEmbeddable, VisualizeInput, VisualizeOutput } from './visualize_embeddable'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; @@ -39,11 +39,17 @@ import { import { showNewVisModal } from '../wizard'; import { convertToSerializedVis } from '../saved_visualizations/_saved_vis'; import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object'; +import { StartServicesGetter } from '../../../kibana_utils/public'; +import { VisualizationsStartDeps } from '../plugin'; interface VisualizationAttributes extends SavedObjectAttributes { visState: string; } +export interface VisualizeEmbeddableFactoryDeps { + start: StartServicesGetter>; +} + export class VisualizeEmbeddableFactory implements EmbeddableFactoryDefinition< @@ -79,7 +85,8 @@ export class VisualizeEmbeddableFactory return visType.stage !== 'experimental'; }, }; - constructor() {} + + constructor(private readonly deps: VisualizeEmbeddableFactoryDeps) {} public async isEditable() { return getCapabilities().visualize.save as boolean; @@ -101,7 +108,7 @@ export class VisualizeEmbeddableFactory try { const savedObject = await savedVisualizations.get(savedObjectId); const vis = new Vis(savedObject.visState.type, await convertToSerializedVis(savedObject)); - return createVisEmbeddableFromObject(vis, input, parent); + return createVisEmbeddableFromObject(this.deps)(vis, input, parent); } catch (e) { console.error(e); // eslint-disable-line no-console return new ErrorEmbeddable(e, input, parent); diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts index 2aa346423297a..d6eeffdb01459 100644 --- a/src/plugins/visualizations/public/mocks.ts +++ b/src/plugins/visualizations/public/mocks.ts @@ -26,6 +26,7 @@ import { expressionsPluginMock } from '../../../plugins/expressions/public/mocks import { dataPluginMock } from '../../../plugins/data/public/mocks'; import { usageCollectionPluginMock } from '../../../plugins/usage_collection/public/mocks'; import { uiActionsPluginMock } from '../../../plugins/ui_actions/public/mocks'; +import { inspectorPluginMock } from '../../../plugins/inspector/public/mocks'; const createSetupContract = (): VisualizationsSetup => ({ createBaseVisualization: jest.fn(), @@ -53,14 +54,16 @@ const createInstance = async () => { const setup = plugin.setup(coreMock.createSetup(), { data: dataPluginMock.createSetupContract(), - expressions: expressionsPluginMock.createSetupContract(), embeddable: embeddablePluginMock.createSetupContract(), + expressions: expressionsPluginMock.createSetupContract(), + inspector: inspectorPluginMock.createSetupContract(), usageCollection: usageCollectionPluginMock.createSetupContract(), }); const doStart = () => plugin.start(coreMock.createStart(), { data: dataPluginMock.createStartContract(), expressions: expressionsPluginMock.createStartContract(), + inspector: inspectorPluginMock.createStartContract(), uiActions: uiActionsPluginMock.createStartContract(), }); diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 8fcb84b19a9be..b3e8c9b5b61b3 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -43,18 +43,23 @@ import { VisualizeEmbeddableFactory, createVisEmbeddableFromObject, } from './embeddable'; -import { ExpressionsSetup, ExpressionsStart } from '../../../plugins/expressions/public'; -import { EmbeddableSetup } from '../../../plugins/embeddable/public'; +import { ExpressionsSetup, ExpressionsStart } from '../../expressions/public'; +import { EmbeddableSetup } from '../../embeddable/public'; import { visualization as visualizationFunction } from './expressions/visualization_function'; import { visualization as visualizationRenderer } from './expressions/visualization_renderer'; import { range as rangeExpressionFunction } from './expression_functions/range'; import { visDimension as visDimensionExpressionFunction } from './expression_functions/vis_dimension'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../plugins/data/public'; -import { UsageCollectionSetup } from '../../../plugins/usage_collection/public'; +import { + Setup as InspectorSetup, + Start as InspectorStart, +} from '../../../plugins/inspector/public'; +import { UsageCollectionSetup } from '../../usage_collection/public'; +import { createStartServicesGetter, StartServicesGetter } from '../../kibana_utils/public'; import { createSavedVisLoader, SavedVisualizationsLoader } from './saved_visualizations'; import { SerializedVis, Vis } from './vis'; import { showNewVisModal } from './wizard'; -import { UiActionsStart } from '../../../plugins/ui_actions/public'; +import { UiActionsStart } from '../../ui_actions/public'; import { convertFromSerializedVis, convertToSerializedVis, @@ -74,19 +79,21 @@ export interface VisualizationsStart extends TypesStart { convertToSerializedVis: typeof convertToSerializedVis; convertFromSerializedVis: typeof convertFromSerializedVis; showNewVisModal: typeof showNewVisModal; - __LEGACY: { createVisEmbeddableFromObject: typeof createVisEmbeddableFromObject }; + __LEGACY: { createVisEmbeddableFromObject: ReturnType }; } export interface VisualizationsSetupDeps { - expressions: ExpressionsSetup; + data: DataPublicPluginSetup; embeddable: EmbeddableSetup; + expressions: ExpressionsSetup; + inspector: InspectorSetup; usageCollection: UsageCollectionSetup; - data: DataPublicPluginSetup; } export interface VisualizationsStartDeps { data: DataPublicPluginStart; expressions: ExpressionsStart; + inspector: InspectorStart; uiActions: UiActionsStart; } @@ -107,13 +114,16 @@ export class VisualizationsPlugin VisualizationsStartDeps > { private readonly types: TypesService = new TypesService(); + private getStartServicesOrDie?: StartServicesGetter; constructor(initializerContext: PluginInitializerContext) {} public setup( - core: CoreSetup, + core: CoreSetup, { expressions, embeddable, usageCollection, data }: VisualizationsSetupDeps ): VisualizationsSetup { + const start = (this.getStartServicesOrDie = createStartServicesGetter(core.getStartServices)); + setUISettings(core.uiSettings); setUsageCollector(usageCollection); @@ -122,7 +132,7 @@ export class VisualizationsPlugin expressions.registerFunction(rangeExpressionFunction); expressions.registerFunction(visDimensionExpressionFunction); - const embeddableFactory = new VisualizeEmbeddableFactory(); + const embeddableFactory = new VisualizeEmbeddableFactory({ start }); embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory); return { @@ -171,7 +181,11 @@ export class VisualizationsPlugin convertToSerializedVis, convertFromSerializedVis, savedVisualizationsLoader, - __LEGACY: { createVisEmbeddableFromObject }, + __LEGACY: { + createVisEmbeddableFromObject: createVisEmbeddableFromObject({ + start: this.getStartServicesOrDie!, + }), + }, }; } From 878cc8b14e5c18ff78641d8ac8d762d2a527b296 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 20 Apr 2020 16:26:48 +0200 Subject: [PATCH 06/34] Add redirect function to kibana_legacy legacy application service (#63702) --- .../core_plugins/kibana/public/kibana.js | 5 +- .../local_application_service.ts | 25 ++++++-- .../new_platform/new_platform.karma_mock.js | 2 + src/plugins/kibana_legacy/public/mocks.ts | 2 + src/plugins/kibana_legacy/public/plugin.ts | 60 +++++++++++++++++-- 5 files changed, 82 insertions(+), 12 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/kibana.js b/src/legacy/core_plugins/kibana/public/kibana.js index 20c46765dcb30..ea0d5ad3790b1 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 14564cfd9ee78..f38c410e6832f 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/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index f577a29ce90b9..f14f26613ef01 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/plugins/kibana_legacy/public/mocks.ts b/src/plugins/kibana_legacy/public/mocks.ts index 8e9a05b186191..2fdd0d8b4be59 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 2ad620f355848..831fc3f0d4a71 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), }; From 32c6fd777f1b879ce4e03758af28f22800a2fb18 Mon Sep 17 00:00:00 2001 From: spalger Date: Mon, 20 Apr 2020 08:21:11 -0700 Subject: [PATCH 07/34] Revert "[platform] serve plugins from /bundles/plugin:${id}" This reverts commit d538929ed96ff61df0d0d99b4065f881aee4ebcd. --- src/core/public/plugins/plugin_loader.test.ts | 12 ++++++------ src/core/public/plugins/plugin_loader.ts | 2 +- src/legacy/ui/ui_render/bootstrap/template.js.hbs | 8 ++++---- src/optimize/bundles_route/bundles_route.js | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/core/public/plugins/plugin_loader.test.ts b/src/core/public/plugins/plugin_loader.test.ts index 18cc2d7a6f182..b4e2c3095f14a 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 9b35588dfe726..bf7711055e97b 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/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index 4557d911620a2..1093153edbbf7 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 f4e3108f80a3b..0c2e98b5acd63 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 ) From 784b8beb2f31f163eb82a403aace4d789097299b Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Mon, 20 Apr 2020 11:51:53 -0400 Subject: [PATCH 08/34] [ML] DF Analytics Classification exploration: replace table with data grid (#63757) * update classification result to use datagrid * consider isTraining in docCount fetch * fix translations --- .../data_frame_analytics/common/analytics.ts | 22 +- .../classification_exploration_data_grid.tsx | 135 +++++ .../evaluate_panel.tsx | 60 ++- .../results_table.tsx | 470 +++--------------- .../use_explore_data.ts | 226 ++++++--- .../use_explore_data.ts | 4 +- .../translations/translations/ja-JP.json | 7 - .../translations/translations/zh-CN.json | 7 - 8 files changed, 419 insertions(+), 512 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration_data_grid.tsx 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 3c959b827bb1c..fb3b2b3519947 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 0000000000000..424fc002795ca --- /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 91dae49ba5c49..af90547606f82 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 9758dd969b443..bf63dfe68fe9e 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 9527a9adb98ce..c8809ca5e471b 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 c68fe5b2cbee8..978aafd10de11 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/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 4e1217ac9e7b5..39f11ff448292 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9478,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 {# 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 +9572,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 +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": "例: 平均>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 bfdcc8b865313..c24e7d0fa845a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9481,8 +9481,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 +9575,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 +9586,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": "训练误差", From e8c42095d2fff853c67e7000ef093ee6fc6a8c87 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 20 Apr 2020 19:03:01 +0300 Subject: [PATCH 09/34] Index pattern management UI -> TypeScript and New Platform Ready (create edit field) (#63836) * Converted to react. Moved routes to edit_index_pattern * Added withRouter for navigation. Fixed index_header. * Fixed comments * Fixed comments. Co-authored-by: Elastic Machine --- .../edit_index_pattern/constants.ts | 22 +++ .../create_edit_field.html | 0 .../create_edit_field/create_edit_field.js | 180 ------------------ .../create_edit_field/create_edit_field.tsx | 111 +++++++++++ .../create_edit_field/{index.js => index.ts} | 2 +- .../edit_index_pattern/edit_index_pattern.js | 98 +++++++++- .../index_header/index_header.tsx | 4 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 9 files changed, 227 insertions(+), 192 deletions(-) create mode 100644 src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/constants.ts rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/{create_edit_field => }/create_edit_field.html (100%) delete mode 100644 src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.js create mode 100644 src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/create_edit_field.tsx rename src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/{index.js => index.ts} (93%) 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 0000000000000..56da031eb4ee8 --- /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 95d6cb6878e53..0000000000000 --- 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 0000000000000..4839870f0f3c8 --- /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/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/index.js 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/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/index.js rename to src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/index.ts index 890a3b2622577..473a8f5b57c82 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/create_edit_field/index.js +++ 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. */ -import './create_edit_field'; +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 69184a513f53a..3239a17f109e4 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 866d10ecb0e19..deac85d9a32e9 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/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 39f11ff448292..209a3f626272f 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": "デフォルトのインデックスに設定", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c24e7d0fa845a..5d8d733f2b5b6 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": "设置为默认索引", From 7db7597a1d5c639ca03b78f0d0746ef4e6c9abdd Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Mon, 20 Apr 2020 12:04:22 -0400 Subject: [PATCH 10/34] [SIEM][Detection Engine] - Move All Rules tabs to match other tabs UI (#63920) ### Summary Restructure All Rules table tabs to be outside of the table, similar to other tabbed tables in UI. --- .../detection_engine/rules/all/index.test.tsx | 192 +++++++++++++++++- .../detection_engine/rules/all/index.tsx | 52 ++++- .../all_rules_tables/index.test.tsx | 74 +++++++ .../components/all_rules_tables/index.tsx | 51 +---- 4 files changed, 323 insertions(+), 46 deletions(-) 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 f4955c2a93b8d..59b3b02ff3587 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 e96ed856208bd..18ca4d42bd018 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 31aaa426e4f3b..8ea5606d0082c 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 && ( Date: Mon, 20 Apr 2020 09:09:49 -0700 Subject: [PATCH 11/34] [DOCS] Alerting PagerDuty benefits (#63652) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [DOCS] Alerting PagerDuty benefits * [DOCS] Fixes broken link * [DOCS] Organization changes * [DOCS] Changes to meet template and incorporate review comments * [DOCS] Fixed formatting of bulleted list * [DOCS] Incorporates review comments * Update docs/user/alerting/action-types/pagerduty.asciidoc Co-Authored-By: Mike Côté * [DOCS] Fixes naming and other formatting issues Co-authored-by: Mike Côté --- .../alerting/action-types/pagerduty.asciidoc | 133 +++++++++++++++++- .../alerting/images/pagerduty-integration.png | Bin 0 -> 88629 bytes 2 files changed, 128 insertions(+), 5 deletions(-) create mode 100755 docs/user/alerting/images/pagerduty-integration.png diff --git a/docs/user/alerting/action-types/pagerduty.asciidoc b/docs/user/alerting/action-types/pagerduty.asciidoc index 50a1f31e4a9ae..da34c6e0855d7 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 0000000000000000000000000000000000000000..0a270de866b36e6d602ecc06d07bc0c3780d9e9e GIT binary patch literal 88629 zcma&Oby!?WwmwV<1WRy&yEX3a)<|%7Yb3b42XEYiy9Nmm+_iCnYaqD0`;Rko@12=* z=6uiBfArIP@2Xn0R;^mK>aBNoxT3t|2ShwXC@828(o*8eP*5;sP*87D;orU9Ddk*f zc)bv?5EE0B784^;bhIps~4bi3O0Ru)xhK2*9bTo*LZpvX{k;;aC-JRW? zC?TxjgMF~kZ{7$BQ@&@$!}4zF?}X1|I`}ejuD4~=@XI*FD$s2tH5>aH#;8p4jV?`T z-=Oe43Mw@vHf9ntGcy&`!q65aEQu>pEQ2zPGiI#7+g|z5?{oh1(x}&OzNf{Kn3Fsp z#xlfa#C?S}5qeYO5+B<;6*sTwCF0++Ft9v2Kk4tUA`M4bibYAX$kUNi17H)#7wy(w^t~t z$_(7O9UC)&44o9cOc0;{8U81Sv}&}V(YDNh0GGv=m+T%~oOuphoM-))mzSIC*DuCb z`?U9U+W*Ff`VMV~sDFm){c6Z83sp^LO*vU!kev;qk%^tLDWkiM{i~s&p!nT+Uw_(| zIvbI=+gRH=@wy9;{i6i$>+e5qGm(+}qlmMW0GXzoB8ixtqbUgoBReBAnIIww2?@WW zi5ai5IPia}zg`KDeRg)X=VfAYb8};KV`H>)G-qPr;o)IoW@Tb!Wq2*Y;N)TJY~;>h z>qP#~M*g=QaZ@LdqlLY*g`F+QAMF|$+qpOkkdgf%=>IWvH9MhprD+!&XexmC;d%JIWS`%plQp-q7HIu z|Es9iwmKygge7Jq{Ez@p|JjXybua@TMpZQve@?_a_is8K5Rv5%E{k~ns0j6UJ?7?u zFDc>WstgwssgFp1&nypFX)oe`;gw7xTPUFU1gp!ixUAs51 z|10^=V;$fW_-Zz%4c=Y)ztwmG<5lB{RcORNSN=b**?ISBww->LpIU#baUS)n#(5pE znEzjnva-|9{ts|1 zLaj%6gWvY5QZLd8=7*RPRn^qg;xjWDw6wG)ziBWbAR@BX4CNQ%Hz)ZnopV6yg%Uv( zQ4mnN)$y2%z{)R{(JMDzp$03H>kNgergX-2gU)--!}fl;R#+2!o6zNtQ5Qf?zzQ#IY4DvwR+Y#D*)xfs3hZ=lALt1o z`&MwiE3>tBdpdU?irtW?Q&#pBZH94|GJz-4-*5gs_aUl{4N!!)F&#C1s@8*VSDEYk z7=og=@)Oqnh$ACC|Bj=U(+hswse$Nv-Bw>;GYF-TtHJhKLrX4h;*xj%%$=wzw8d)7 zX^2){O5K;CGF(dhdoj#?=KH9Nnz_E~gL_MeeI|9)C&tQ8B7+DBC+noS0a@FEjsn>o z>2=eqfnz5NTqgo^{a#6PaW^JT2juv}oi4VOPj#1GE-rNhJdWbqBdO#~O--!!zlBo; zeVT)Uf-)#5wfBqLR+a2kRaK>=rNhIw^9YdO;qCE9`#`v75`(`hm9pGDJUDpfi0mW~ zW2zxd2@L9n--+i=c&IvdVyt>oqr^8nj`1MW8+mzT0ra$n3GXNHS$EZ&cA*_hY(zZO!USZ%It`8bjSPTgvbElwn!A zT>65%2HP~=Nf~R{A%ry6R1iQ~pS4P@m-P_3RIrWAIyUICVIfyH2`92B&c5E053TFk z9v0H2)~g1l+ZNfRG>~Qq&*3fdM-y>y44RPqVE0vP_?6;3&zy>`tEicu5|x2%1@qj{ zIYoo8kOFS(A;VnEMG63(yKPa)LtdVyo&}Ecp?GxZ@m&7Tt#AuM zSW{(w?i(CbB{jwO^PGOkf+Nz6tJnRkU^7^j6V!_`;PmO zMR0KN`BsX-P&Ei5Ycexip)S!pb}^c+;`wU|6yAF(=aO6^VLhvJ6rjAjYw zpkrW|UhYn;A1t{uDT6UFG2uOqSK3-cgdHA_f3>8$goK82tqnVEk7Zf~l0+bJTxw!mMoA9;RyNatuoWUJiy<+hqMG$hmT^ziU7JYNl> zq@8Xh*i+MlWMd-0tTfK-DPni3x5xPF=R|8V+!wb`@@RAM(B!bJG+!KQKLn!8=u z%;$D3bX$->$hQ?O^)La=-|OZ$iIC6r>%rK;g!0DOwV?SX$rkoQ?&JQimWKDZti2YqE3BtLy zzEMwF=9FKOmL^A)GJ}$raa-X#YVawvsjyAgJ2&7h9Cu#)s-7JkT2nhvr*D z20469Q7q=@w#2->y#rx##OHrlB$R8{$p8`oAItqeYd~y~&4dz34rVLZ*29g1CaHLM zv;g+?HcM2WQW!bDgl^QF5fHiEo*I#7-QvEk_MyhTx{|)(z^*PpwOA-JE{(_=5$PU5 z8rnwA=pF!diy)C0THi&+!Il^J!`R*KM7|shG9=e}W5?TWeB&$<^Ij!6%!b%- zcd?W1;lw!B&+(ca;aLpUaYGtFI%AcqDliVm>M#o%nNJr2keubb*8E;_JFg18n5`WP z3yW=>Au8(C4MC56@RvdSPX`%ZD z6sW9p*~_5_q|=|F>>WbB)T-!tXHbZDqD`!Ja!svw7NxKG*JD1LA_y`eki8Xpd70ed z%ca_lHnSP&>Of$&vbf`0H2-v2Z2NdhW(?|h`EWK!5t#2 z<-0sr(Lg*eQ<#x58?4(Av3?TKcjM^{oIn-AfFMwix=>i-$tn)L7^&E)w`tJq{xt2~ z)i>Dl^E`BEkDo1!TN4xJGOaO>YXPWxZ#Wq$Y73=1aR)jn?CwtB#3%&I#%vu34US6K z*Ow$sH(+6e5``N+f55knQO0Dq@s7g{W}9doX8zpPzc%cB8L?65*qAT&`K&Ek!Vd07Pb4 zhj0#kM67!_ZW))Ne(?BI6&abv<5UzUj)_T-J!#za1=b81)5Mhki0gB`U|uATZHm~3 zi@ybL3SWpq-rnqTFiU|r^y@a!A<;IG#pmHV>04#<5c4L%SI_>>#iQw5uTxGhD9%cq zVxhfptmU}*6ZG4+OB6+i9%C9{9>mmx%9b&4`ZK<<2xFVUc*^s=;%q#g!8DUfoB8iX zj$26@c<)B_+{U6N6d#T9H+35AbEOG0`8{fyDl4DP-G)ekQw~Sgm=Y$_sZlpeU4d|> zyG8GogbI0N7NfTx!CxJhEFx2jo6wKsG-k+Wn*gLY z_EX#Lkp+kCFCdflI`??O9A!bXC~~g^Omhfv2MFw4p&1_?mmUWNhPf{Jhn0{D_?Y5R zL=$Vq;rkhr^KUI_z~PmOPrNmlI9881ZBlEDhQzJk+JuqoW9ytmfFhyOy^7uq8I6%w z+!vJ4y1k9JU{#jz$i(HSdmHCw-6}~%rslxCxnqt$EO$xJQ4GjzE1MSY{}ug1a%g;N z0>JTgvM`u6}XMh%5pkk9Km}vNDgh)da1 zX+HKF;c(WfR#gVy+`z$zm9T|N5k7e(P5`I-sUe0m1o}BKrFE6}o03!eQ>QR8kxatpWkU8@OEl*gDLkmy`Qi)Ul6B9ye9eV6>Hs0o+Gy2eTe~yUN8<~el}J-% zC`bin(e-_NtcB0p1RupvM2qO?mdH6PD2nY57;jAgd1=d0pa+ZNB#W#iWU>z!H--x0 ztV)$U3vhQEo-S9ct0#ic;h|Qm+8fQOzVoo9->k$+fCRD>!7xFyh!+vW_i_5>I6w7(GBaR5aki@tJ$7TRV*yV%~wt`IuX7)bW@B4eIoN0~}H+ z(zNITacj(m8ru}jQeZ)LcsBcX={^?je7)vxH_rsjN*J$s@;-k>kA4f znqC8#+_au~RMivh|6v&a#8B*yF+M+b{8(bqka>d`n$Gkgac3KA_+*ulmBztGvDo}G zxJtUmxS7aJ*$EMbub1TzwGqa}A39EMGdz@i$+%lUPUN`RQ{q~s=+RJvjvqriybo^3 zk^fV*yc+WTepKFmJ+_dzvrqkQYNz%y2Gh2eKO*f9P z*deA$mX?8F7;fuNPq`WQ%mvWr5WW2JJ9i(Cr(=KD5L8`MnuOT?d5-ckHn*LIOI*l1 zeHE*S^$6uC-XaEp_-s0b>uR*RbO4%`Y6c%!^K~75UCu{u72CX&j8#gJbr$d-QhaRG zdK^toYgQ{<#^?&)YRmKHwCIilL)#ATR4u7WXPoh%2g(_L&DztTSFFnTO=1Z6d-i4 z=n?8FA>X4htnieYy)Q<&wri~_oBG8OwDu-fSZFM8So#wAhKP^Z2k_J$bNO@Uje0Ru z)^s7ipTxR^9k$bGA%Zk<0D+91GlI%sA@A{ijafTmJc0wDgS@r(7XR1hJ{Hf{z0iH0 zI7p82dCcK;a;D)V_3g5o!=Q|BJ#xRS^@MUQVX1sb<&`Sk)I}-8o|CrNer#QcUiR*C ziDya2@~-q@8491!ubd0$yGn?U$NSo}*S-)lkV#Ji2JGkM<&CuU3ewQiAZTHBu;EqF zRS{cNDT%VCMP;7}Zr)BzOvsrSn~#V*sQwA6`n^o`lT@jX^2{i!)J+VHqZZ!9lk*tZ zMl>CJHb^xU!2Fge6ir+ zOcQ8R4Y<7k*b{z&y4#o`8YI9%9(ws9huC4D4W7O2nyaz%C$Yz~ATltcs2V;*N}2WnSpUk3yw^ zYWJ3dyU6$J+B{V%7Olt(dSV~BsjVDci6m18?zJL;R(9ku%geedC7E$vy77${7ACR#f;LD(W}C2x<3aUBlO+alJ!6# z*l3h7d13a^z!Dx+#{~p&<{2s4JX=nZzGDVVl~u0-ZC?*_>@!Jcq`m-~@DkX|`43&O zvT{>fUyb{uaOQy3r|0X+d)fxKX>l?h5*+CGdz3c{Nb#FECsf@b0S=}#Qe zVXTNCanx6sMQ{RQDVcbzg^EVrgj=yWb#hRAV+>Puy590EUs`q01M~rYrG3iXY{X1U zEQ>N7(Trd8rR9GDYbobsFuLp^GsZf2QOPH*mR4rbbW8dFnravK23p{+ap z=`ki8K?-_!cbA%~l6zwUmumypESWJ#9A&N#dc9sAGZ+E?p*SPI(J%-lzhr%VU6zm< z5ff;z02)PLcH_NRa z7n}X~cKi(JKV5#^xBU5-VF&Kb4KOdMNd@{l(Tth+8U@fRjc~j>t_wYeaI#<2OgCC@ z()UI^mtG`oUTp4NYMNR!2|>81%q$N7X+!-%cILbMLe1S415v%p&ih|U=g&i|Q%pz= zEi$kncg_-vxK81jak8HI#OGfpYk){L#=roTZam2&phIN=KEK* z=)xuHtB7})e3Yi~7yEo|CeIsNmP-%s=YFOR5}mL|6nBeKj;{g7mlRfOR?$I0OoX4O!wdTJD}?WNmeKb@tel0w zxh+L-&G<`qxL%)xEmzt{Ea5qam<4zF`b%Q)hQ+!rj$>0zHmvz;yf6>sMWl=>`JeiN zDQjLn*J%+~r9>gu?o7C_z}iE7RT7RTR6)wT2^6?i)u&+qFnvXbWG>-s^^OB{aQb>a zlL`KA0n*pG$;u!Ok;^~>ZIm=mL&?G;DwX7jZx?^TqPEm<$CI^^~ z{K-3xnV8m<%3})}k}DT@%-Z0K&y2yb=V2Qv;5wI4hS6XUt7z1O>}J<5%A@(nNf0QL z_Ly@XHxGP+PY(TXxd)m*7hHdWIE#&094|Lf{YuDg!pLH%kn8yKQOWOalHV&*KWaFE z+=Rfh%i>jr#2XTBfSmOOFy#viIM7rkP#j+@2mPm{n~ORn>;vQdj={u!OXMs2@D|)Q3IC>nhy6Ro4OVy zPb%n>t^kYr(dMlf$jGCvo@wodYj+j%GqDqjn{q&8jj2O`cl%DYkCOS|c`MeWr8}{K zRwU+FpcF#+7bw1@{=|M}_?!xh`BapefdyLTlPIrmn-`-leFIB$rSsVQ;qOC&Of0w5 zm*1Z9%Hb_A*NjW?mrs3nAn58Olqn)DVF11ZReNoSg?S?ZF=tw5Op8!kH%_VSQY{A_ zLaS_?DdooOj<98Dfv7&`gVexnMS8_p=H}g^bX!(5HJc(H|RSzp#h{=&=$vJqSqgD?s!FS(zK>(lu`3_nTCCj*kEf|fI$6nf*%Y%I4}BGqX=l!Zcp%5*{`_tcfJu6z zhf8tbx--l=jfMANuzNi1x~~M^E+Orf%94qt;$V`rol2t0=9{=DE_8I!`ztYl3RbOK zmnNE@g3xHnx2JRsD#Nu&Oo3c4?YrDC0Vm+;*<~>L~1{jF^l&k0!yLk>RVyd zy~9;B?USi(u^&L`CZ@+EE|Ti0Sm6?KGMGfxjc)-?rdDCJ3oO?T6t_n1tWT3f~($uK}udh|F(!vpN~^m zUsI9(o13~Zw&Y6!`PDx4K`R#@>&y0-Q2`Qps^If2=t3q{*dsnMkwT?d-lpWqnU~C} zJkl6uVt?ja6d;#A7jvT#6OhPvoL2;J2WWk5-l25^5CB4P-fTt+m$mGC4}a6Xvx8|Q zd$*yjg&h&6R$1y6xoc!HUanOeshHPD7I7WM3lyy;LN6qUm~^tf?zyvv@XZbw2cb;S z{Bo!}*!*_kRhe#O{mB{IgQYDNdH_-6x!vtVUxgt1!bm)>rGH~!fnzjwBf-flKTDH5 zlJA(M+va?8Fr4aCdS35P}i8Z_~1C0KgH?Cg-IHXkWQ^x zRB4QvoAG8_c5<>=B|`o-d)iRa_vgz0+%+>2K@(!Ckr{Pmowwl!ZjGOO93)<)iAO_) zEhkONG$219DfH(tR{J+7k4uUxPw zLLlNq(s~w`m_6IM-;FbTv?ds?-#}~ERM#+YY{6EBc@1$|kcxJYmM(I#n-6iG9|Hz5Ug3;1XW$aCTQ0~ic%J1H zr4}41X2&NFM^rKgpC3s!xW)w}PD{<08qcVA>w|TqY2z72Brn>+XaN&v_G^LzCxyhSf(& zeyNMUgAoc~{mAul@E`#wGoZiF@IopitU^+O4tx|)f~#m3{F;wKC+1<`K(K@p0P*^K z#$|f`CJzRZ_tjG>XIRvM`2=A|b!^^dFVTd6bCBE=DL0J|M17i!Yy(R<$sS`z*5%H;DBsf=aZwYsPRlrik4B7xvhkhhD;5hs z6E=;vL&;4qA!er4n%BpOleYnolZAmV@0oCeSnM;KqIGrM#^PWHh_le>6GCq}bfhws zOj?U{cMPY$82?DFJ>!z00uM+BKG{F<%ia^8h>V#NQ2h4OCAL0B05?%tqdKUCJ5OiD zYHup!4)ue$cRt$}g(3FvE_7Id0$f*80-2dsisb|LJ*~a`%*B!V;0qB8MPy}Vi?Nc6 z40HT*e&qfz+(eeZg%s-=YOheH=o%}Z0LZL2S^=7`*;Y>&H3iI6AXKVEig+E)k@qABnf&H5Tj`8RzJ&z_LM* zePGm!-L&AO$D`x0!&@YmneV>&NRLDW5xf)g=oLL{1=c%wKil?^shl;+d%YhAZQ}ct zXRJ37#6-<4^GH5qT~N?*>VMKlx|U;XT?LGDkHwOT$r7?v&P7|uPYV6zB<3^xX;bf8 z6yi(TBTLHpJw`f|7PU2cF#hw{uM^FsFf?IVE6{Yb+&4#8{jkgGZCA?;HjF;#ar1n8 zqZdrd$aRP7&>e0S^0K@###pfOz*&aHSn;VLA&m^+cmUhq7oS*Slyn;fNW~%-GhQEg zzK*AhKXr85Q>W~#XbD3W77@o+i3TKSfO%2_S&#kF1Ep!BZZ9jp4g-+8veqGUQ+%+y zsrh$0^HYheZv|!aKU@j2AO`ma|l1I|GHh?y)oQH75ol zW6GQ8ytn0#emYX?!*BUG1hrrk}?_T#JK|Aw27w+kt<{eI1 z=8#PxAU{M+asGlwAxj~Az4DB^G5~Y|sUyxB=TS*>kbRc7FfNQdI3ozOa8&PZj2_(J zK^}KQq?+4sZ+}|*T7TxrPcBa>{!%MSy$l_fWwd(Ms%Lj4e za`Tcqyjo|;;Jd3~+HqNbJiO;Fs$FOw9Ps$%9(_>v`{vq{d*|R#Vf8Q2!5YSIC-G46 znHBPx^C@gZMsLw(QOcjl(1{^p%HCTLF1VH?U7NijL_|7V0PGK%KOqnQpoKN!z$>aY zg%eu(?8){u6I@09QS7G_)Emx8!o7Uz;6@Ij$$dUV+9E-VKW{Gn!3eWwLElvr$C|47 zI$8Ldr3L_@Q9Gz?;`hK~iJkrAmzjUw!2T0y{YemjEU8-U@BA6g@|2wm?Me4f2QUBL zRd)3SwZCP#y%OYp)1$v~)IP2q{QPI*|Bdbz^-8GYsQKyk_a)5ZUb%3!@CKpkf5Pzo zMSS~Ak&{;z7u^0|4e<}D+!23J6(w$YfBbj7UkeG7ztZKR=>N_#uO&LQUghl5chBYi zJ+Tt+mD^`&lKU5u(7*VCV`Q&#%+cDBEB-Tw`?t{YQ%YDujU376&jg5nvGnXezRKZ^ zICc5_cT~kcMx3?`PivR&?@QFa%9+s$suKIN1OIEJ*k6ZmWp0}@NI|-|wJOF2jS7-tReOb#Npgath_0aJr)tYZ%>Hd<_ zC3fDlArlKnz}xokxTCC z`)0xa)ZU)x4~j66E=8vC62fV2D}hUi;Bu?5zRpD@rdsj`RP6b*0Dt8X;a;4~ zZ)i$kqq>h4-(NJJ^9Tlb=;u(fs8&<$W5nSkExN9dF4GQb{%ywo5gqQtU-i0ku5+$r zK+h>y7rXsrWj5GtlP%NnHS0W|jwmT>LK=M&|MTR7_MvcxDm3<+H^=r`0cmPl5)0>J zT(zdx{SuRWR1&_Pclndlyb`7}680#&fAZMQCfPsu#ojK?wnt?4KW@K{EYyl`+v-y! zG{+>vQG0)iC&N(m8d7o3w|b3+IdM?IGb{tN&psY*!@9W1{Nh9euEi9=lwm1FeVkt= z{}OgHl%TzRZv^wnv%0#<%E}7ri{4#T3-nG+skABv23IXI(nHs~UqT)<2IzEFQ*hfD z6JBs0a(Y88%T|M33SBsg6a2AVn+`wX(cMIMsC*IqvU&HRw!m8~_;K}SmM`wJ2T|AERMz-wljY3sS`etgRm{-21q#DgI0X1Vm#P zT3Taw7^Wg<)ls`;kI3keylp{_RjL;IQKO2#`dSA zq)^wccz&w4TVLg$2&e&Q=u_ zeA*4Crl|C~3E8+D?GyVg+-2n91@XrHWfL12LrmkV?+YZ-<%e-DEDjXbGtnotWP_FR z*BHJ^1(Z6{ITqse4fvooVXqN8h%7q^>IZG52Az~TIb>|&BnadelY^e| z66Ob|vY^AvQ2?e}lpi+YH}K{``Bl7D8l|3h%Ruiu3p)PKy0z2wGIXu%S_G%@Smsq! z+$9%^AjwzWx>f5cKC&$&UHg%crh+EHCm7DuOTVlDt8jP%EGmr$)eGA% zx_eijKgZ_xS<5BW+ubl%byii{SvW}cgykx6p;0HeEoNni+h zW}1G=u=ztrqMIv2@*o0+9NtkdDu*jLMMU8*&hj3e+f->Lu1TF_pO%eH<@x+_s>W?x zf9<54XQo(z_-=39kFm}3hO_08=fSM)+jFcY8X6i^VJw=OfQm8&8GpCO*Y*y0ct!1x z!kyN`i;UGGXsj$otuXg6uKPnYIe-t2Lr!b&`6(xvMh>I5kH}I0Lg%!In|^NXlm_N@ z!6k^v1~In*BeEIM8wX_V;4<)5<`3BOvpBVQ90KYW0bUtV((lP*0gB8{EP_J zO#kTTQ5;6g_AzKmSvOQ-kFIM_q4JoeP(n#fNhKU4l)^s^JV1*Fa8RRa%}A#gD_{F% z-6fUt)Dv|NNFK{JNh^!Sb05iDwBzI(s1oDi{L*RXZB@G7K1$GEqlpFW_<4egp{C^j zGc@|6?_xrm>#8cT67ae!KJP4;n~qohQjW$`OH`z3^Bg2!eX7)v?n|mIxxYog7_rjU zP74YR9oC%gLPYqmhsnIS*YwGM5`fW?ctpql1wNLhJ9C(qZ$H1LUOCy^o3vx;F@u11 zIIdXoMYS?2n|`f5S5Elsxw5=MR8=jUfNnhD5X|r4G#<*z>WG?`rc9#CUx0Z2nxw(d zm`H?`&;om93rUEorG<{3f@N3a(y2QZPf=71v+9!bo_Z)&n6xKlY73zz^l;$>6YB0P z&O1Qa@pS7{^=@HouKf91fG_7H^r^eWafPO-j5{n_`51OczX^% zI|=6n(A2ub%{3tyX;=1wV#Km)VwWIf=OO>h^W1T5yf_3LNb=W;^im0-9qgik#)B>%rz`G0|+s8w{f^g60{iU3cA@+0c@gH@J zUN%_D$@`pJKv;L07`+sNY~3hMMsIw81rquCi72y139PtBs>*7XCPplI^ZaMTD7E$Y@s&W9GZO^3jm?~XPq)`yfhMSW(@?gE+_lexAmOQ*b0r~zywAjlatjoU z8&R71cqWQT)WIb30|&NYHA+~-jlZH<~=B>=gu`fb6!uc*J2KS8w+Ke{na;CIEs@iWBvhIC5 zWPIvQ6>{1wM(2vZ86EhT3xDt#RKYkqY5X#?n+?hlW3wB}wusgh)9KGyuC43fCe8wl zZ@Ddx#tOMIm=O%{u_3!Jg7}(0UR%w6;jrJ4c!kP-E)DB=d;5%NFV;jyN1q(?&l)c# z*1>j@s;++lF=s~?k0=o_7sfdHmnw|aDGDmCmk->^__mzANLzOKVlYTJsHp1uhh_7- zP4Vr$vdhBy_6&yBdxkgnZ)vD~?1X;vBY!N;7o0-Z^1rOnh5rD~fti+XuHAXxRP#WI zj)CfxD>U-JJ44nGakl%vL0We^Fl-!Y|f~N*N?Zb zqAIOCw-;GWrN|Fdo9%?Ds zOVmsL(DnL?5~_k5g)S+V$nE!!*o7mHfyBd378ix@ySFQeQU)jU4=CCIJ@hV`;=~>W zF9NJIWcI2x?8_P)$P8ruh7N@7s;G1~JI{|=L_x;x&K%nUn z13w88%iIlfinqAAhoOF=+|6|}5t?a&EFy*JyyPC;W{=n~1+p?@;V1rCRMX0f{*9%r zzF#lNRW%I*2-Cy+A=(CWy#`cj?kCom8nQRK;p}Z(liNc?1DzO+RP7}+9QhC8O&{he z4L&oGC##!yGb-5l%)r;AulEm^f$-mBgF6Lt5lZ5&aw5S5pUGfOUe;4;7~Hr*x`hVm=a$I>|-LsiAX~~v2Iqh)tWl=;YL53$M0%l^2mTyVK7G2y6RfmitwVU(}aT>gI`N>fiQLK^>E^7`KE|ycNZ~7c3&@rfKpAIL<#DR2EMgx zxYNS--C^i-xYz3!qd0yVUHW%tzfWW|!Z6K2)04JRWsdMPvs%p*W)wlPCOylV{w;2w z6>#x*I;BeTO+Dga5oQGd>a1v_^>4e);V8x+a4E(vw@_ydi*{27OyPwE@0fBKm=ArKWn-fBa3hDg5DWK7GjgQ!| zlec!rDY7vw&}uE*Yh()lhT*yWxQpWx6Mtk!i<%>xrM8DCcYOoPewDd-e?~w^TY%!S}&R5%@b4KFj zJ>K=4_@Y<@E^<7-aXgB&+KO&DX7#D#WEje)hcDBrH5E7MB*L5zg(= z*2I)X;lCv*P)0%M=U3-S&j~4G~mk)>v#aa1EPusKwE2lEZR7-#zSR z4{ud(MEzbH^O3#rdOQ%4p(+TqQJT*@+3p?Q5(<5Rx+?8HgxDJRpn7mXDapKXKV403 z(Ba%VPK-fjj5|Ot;^4`;A&E$W3FmiM{#yFBt#)FMvjVzO@Fk%9rKq z_||6gO7D=8g1zeet;HZZ{ASZjTsQWNk6a>TvC!NHoz%cZ)1>Wz_vOhQJ9JP98qjT@Y&d zc^8lHwJMt#X65I-%VsBw`1OJx_bC)js~`Hdr@MNvCpL7n?sg}d%YorWrjY#UMDYym zZs~fCRNzyAxu$}L>j)EN=_i!$+9sQuv#kQGbVuY(Z97+T{b2v^twdJv-F0aZ90efk zfu1m49armwNJ-44muNrbtWQ1~$>x=PLW=t5ZO?8)y}P-r&|fa$Eu(Ejday zaUR2(7~RSr5w@9zG%8xn!IS_l3Taxy2OFKkhPW4mTPq7L!7a5g2 z_-oBv@&kki3s|MN-&*Y!T`sEniD;h&i=^*_`rw(YgZNnt7jH#j*3>Ruh}1L8<;xJM zm?j(@df(uQRQo$6ITTW3nz9)wlA_LPj)V?r9&Li~p}KdOHFhbxjL7-I&QgBR+g zcIbM%W)+MJX2aW@&;-;-bVwpznU-W*GE!y5Ap&NWfIE`1UCQqzA@p>e$l#*q_)IB$ zgQkeJnX`n=(%eL7;m45`Bs>3 z+bkp)}UXhY*J)j;F!?hcJL z(9pQMHSX^2?hcJM?(SYVjk`;rUq+=`@~>^&WTU6mQQDT=5c7O}uVfey7s1(5b9EG3;#=Kd|toZwBOi=sRC-&6A-Q z6Vl^prbzoD62iKbh!yS!2ZqvcwPZ}`Y3VIWw8Z_J?A{^6V`5U;FIs_xTAVT)`^W`F z_vg!GOz$Up5{KXoR7@X(&mrCBpPbs9n%*CCwi;*SHa_>9Kj--p%@s}Gel>(AJD~2l zA3vt7`E~~d1urhIb&S_~GLF^#YXu7ry4NPGFt@@}vn$xFxK%RHXb%|=2p`bV`X$$w zi1iOFl-MdRM;Po(3(P1}sE-<<_8mE?Z_LQYsh3h4tcx}EsO++EX@Zi^nwZF)u(Yn6 z*d8-Rq%~5$nTXdx8srHqaRsdf9XZ^`vc$ZIIk0v~4VoNHPY3UWGTq}l8jXb0JRh!Z zxxARx9g395dbORSO?8~{Q6#P!khqLi{u6)uY0Ig21isP(wbe#|U8b*DYkUwr;m>#= zbwPjFCr3`b2wE`>XVEiSe0uLbj~}1ZRaMot-YXsWoTM~c2pli>KEipso%_k7x20o#WMHbAs&(D4mFb- zr`O8u)h$WS@gjOpF#b3{UW7b(PK60bMEfWHn>WqCJlnaS^l8mCL06U3OLbwg)DJ*obK9fKeLEuyQj`y-<=U zEGjO>*AkXHUKeV7KV@MF%+~VYL9+tuFyV}cX>B$X3UwK)3jXUuR}-X}TS$@$erA{= zMQGB#g=#%pgi#`1w_!;_R?P;xJboN((;%fEJ248Hp2P{nD7i48(}!SiW=glJ0ih%%MQUW{gYN!MNcw*$P7^i~2;qI%$=I#uhb{m-L(44*Tou z+9Q98AxH>J6w+{!5nTFU5fg1ihkqgY`rfd=4--BhNNsB)l|}nu`WQ=&1Xea_t}UK7 zk)-J;0L@Fnz92!**2k5lT1O;u-9i`3Y#SL3Bq;UFpBbx}CacFHgmRkmbF#FA#>=h6 zUkck(8vmFL*i4e8h?4|3MP0*72vCki33%cisc}i@fY8X2$j$I$;OkWGAP2R5#pLXf za=zIoqXm&3$KkDQSDD{1A34fYiqDjwOFz~hk(|9o@BBqwoZ_t7Y?Q-e8RFnPMF<~Z z#ihZ$YhQvy*D?9J)b%)e=MZg$AK8wuBRqRNROV^>LGDNg6G>cnnr4gzGuh#z5H`d% zQ~FMR{s{CiY^v2;$bc+X#X~iiein*;3g)lBVOS2Dn>RAyp_ENr^g&jH72<0KIkpOba8PnYtJN@As&VYQH<{pZ4c2Tl zn=LCxA`ZkHn4M=bdG@|iTvdB8CmPF7UoaFEZwD}J-U%kS?!BdiW0xT8d_T5y_%tmC z5RIzPM2ZIsUImM+&*{_lYIcjz5)R@o&aqL56M7x;vHJ8uB~F6y3t;kN^1~Xl-=D_W z5}LRs#B3|yEgH22oy(ALn172iW|2LyVp#7KN)>}cAP#j$Q!)KU5>dD`Bw+y_2i3lb z1gp)NVYOMO_!@8XYOENFk$rL3TEUQH%@<>OLQF`bS+VX#1kw5#O>p#pugNuCe+@#EmU7Rm- zh#;>55}|YEb>&IKnUHwUV8*Kj_GvU|8GF9l6s{@wg@yNAiSI?%D-;&~R&VQ;^TP+W z(#80YsTsJoeF{S>mcSCtu@W}8K`x0OB;9}y>j)qG>7B%E1)!Dj=0{lW9cH{94(M48 z&tJn&&TD<=%i=<4-M|ML3$VM5!KQrE{7qNIXJd^WSn7(i@~3v4F}VmbNatrDSy|)u{VDCV*rr2JrzDH z)UfTOIHIQ3jSM4;h#yJSvr(LC`9V~WIO$Y?-+rUzn(1B>lkkPlh0x2aumcQyZd{y* z`xJ&FO|&qm`FvzrjFKp@&~@#(kiFw*`~mT1^7r>QhkjYE(ZU~$EYk{AMwhM&3x-et z#8O9@``$LKOfGBIfZ_Y~H98ZVf(kD0E+K@-2-@5jMI?~ioYdG zh{e^A*>tc@a+jexjsN?(ucAABw;)!4GD*3je*am1VyeY)o1uAwb{l>B;KkxLzRlDgZ9A?gm#RiG~4^#*>k}*b$X=CVy?SC{|Wy4`MtUiP{q) zq*qR{?}mwCX;?tsuhro>U-1NF&ZS!y<|-X>rtY9TVbhb?r4yV0qq;e*FoPAbBZQ}l z&6k(7^}9JjP%~R$5E6nsQ-4};a5nJ7yI*jz)=bWi7^cP+mnLlWroTOLd$+zW9)RRO z(-F*!e3u|jW+tJYEc{bTv_WK2VtGpoHHk8p#v9ak%0#UA=M&#&#f2bY`9abN8|EQ> zYYHZ~QueSoh3WnYRPn`*{@gK#!p>UG*=3t3{p0T}K_^;5mOJSCx{T72;+t(SMl?8+;2Z53q>jGMssFU)Fy)yX5qx6THlvE<|bD@ zU#Ul}<8TcVaS1WW*?La{W^(7OE7n4Hj$~ks+bHm6(RCIVQ|#S!B@AHRZNzY4ta-l2 zD*TIwngvc|uH&#E92M{or4JKHeAMW;&$FK@KS=5*IPiOA9}U5F)q=N-Ng|aIsJ?ib zy2^3eCTLOHvoajRa0R~r5rs(5>R1gKhr4EE`;-e~<^;R@l_I8`u1W?TgzG)GNB$~e zo-$LO8n0RrD*j7`tD!?X*7bez-8Q(;P_}$w<#M~W759j(-Uw>kfYl=O}eV?E&s$dRh{BElVw$dx>7qO`4%QKJ)@#Jc;P%mvQ#VMJeW5+&RxNx?VQ(~BT=aBRC zwF#@S`d8kgn~~8Qi5}pUkS-F1DRrW?eJ__9<(+>3U5h7wsO4o%Wt}R2NNH56FHo_P z$^vUW!->Oq?i*bC=W7xRs|mgajQo+$@nTU!1`&mSWWot3HjmvaOP7@+4rxra;-8{r zG@N4Nm7>=2_TWSn&h&`I>-bzih&M;1-efN$DW}%ggf1Ui!H^p!hs|iLe{jdSyA4VR zoSX2fcR8r<1yKVc%d z>Pd_jXIcU4xT&~#Kz5-GqSLV^-8fAyva%yoExi=$Wi68-Q&g!ay_Ujl?GRB4Xy8cl zx;$tyzRGOM`Y8E9W^qd$UaSxwvE##f^+clP&HI!|Au?iT4p#B1#LIVrDDL?QH@e_- z={l^5(^_dPM>>vs-+NYc zU#QSIA&A<>Ev6i>g99)G2c#(tBY`Z=F~Ft;xAU6q{wQ^eBlXF|yV^D&kPBZ`{*ywm zvbgK+(#ZkiN;FW*eIKC4A8PqS=4S#!$?V`^9j(}$LYMQe8$Y|R1Y9C1Mb*g-9f31c z7}qc#&V>pv%of4senP!AKHmi2>qs3dwK09ez1pYFiVrZLm9#RqV=|O$A*QL_2x0>^ zr;C5a2?#+#o?bybNK4qmAo!SO25haR$0$gPDmcA;7-F|9K6nvt?h%_{LIal}rxzR3 zrgS=O*v)@s)z^Q858=?B-E|0;klxfXzcJRC)xDZeIg51Ar9R!b_ulSQXJi`w zaP_o+2~fscoT0n*t5_@BKBe}JnW#U1J(j)eZzpRC?UyMSB48!{xae_onQSUOs!5*ZL2vH+1q49q7E+4ZbsAED(oAFNL z)o|?u379WwxLmq~hu)F#@f}TP)*2K%bqrLpLr!=xeGksiZ>y`Jgw_Aza~@LrHwpPS z+e1afl&bwq1~bqM)LQ5<85&?X(gGj@_i8VEr%wHVl38r?)F^2P*sGOVnTjax{3bHe z(sK0$go7wWc!(iv0#!Wm{Yl>uaElSHACJo{B&*sS!F=g7YC0?&Q)v=Q8TUYcU%&3J zk)&bX%?sR2LcT&r5lMWmH+BM>*t;w{V@=oB2y8;G`J;rQBr5RR%A=Ci9#;c*@`X>1 z?~*#Vy&MO)Em$$=_-ljXz`}1kUiZ`K(Wsl@Bv&P(07^PkFB+vpreHrmG4_=_BQiEw zdUV61QUjqlBIL62S+V7rjgW*TG&F^4?uZ5dNfoO*M}jk#yZl%VJAcl6HU4tf`vy>Gww^6b1Hw%o*&wgrRT3`b2k@~5g`5)Bmk znK@7NIYyhN6#|X#lYBFt0eNt%r3hMt$>zVGYKah3Nz`M3PkvDd3BURJqEG%=o& z=Lt}r7*CMtXGLF(nzl{S&(iY5s~%e+!z1PyGZtHfIC!_RCHnVii!2ufaeEe@9kH*> zc5xj~&nFJIhYAK_Gj%3o<3F7r1TMjFsQJ?IHUbbX_yfs@IWa$U*Io~sm=v5~HgZlL7N%}uh+ zQeK>M(@->#t!?T7octRy3Sd=1}!4t9J*8&ZD|FDWj)CcKzqVSAyhFGbkVzl zyxgq&YkaUQOdVqe0b}yREHAAlBR(_@BFEkJmY|0YAY>q(-(JWP+tkbDqRw@?xPhA;?AA%Q5{<%Qt#92wWm|JJYv+BZ_` z5Yb@vg*YaB8X<7q2p3tK{$m1ReH7Fef-Zb!?(+^IOGqvY;Sfe>T5vleD!oNTy^U|?a@lnJ znVR0_EGhPhVyKkk#&1*AYy!#T#q1|m%?q#8 zwhwU*C<$i*Y4#Lo&9jLgcvwwb{b`s-5@Q$*OZrckMP*~FZ|H5U_d@pA=34bSA;^Ys zB6@WV3pU!QY4w~CkzK^O@%etGa^@UqoBFs*o?{tvmEP8{w>aA<)|>j+iBBWAKL1!*9d&R zl;Am%LZrNVWkbb$a&k&(a6~MK^ek3_-(%x;TIa^cp*+kfwDZTzg>Fc_8P?Q+daH)h!qbDio1pX1RS5syY%)i4xa0&e_ zk<8|B=%te~*=_`dBdZd{t44IH4X1HL!F88j<(tYIqBy1aPdAS(0K8K~$xar|V9$yS z*QQ4~P2Jmbd38GP_vPi~>+RF!lEytIHrv+a!s14(D?4e+ide|}D08{;%HXuV*67O8 zW5e}gozl`UVUPYopgr>1#KllLo5}NSIYp7{1*LOgOZ}#g7`=CwLs7@a#X}H2Z+nW; z?wXbla2=T@E5)a2ZaloP;rZ@@$#ug!HA*Z(Pwf=L3=cHkrgIX%Gwdk%_J~QMC81S2 zG?i{uyL5K3o##5zocbyv_*0AIbN)+O-+H!(Q1%G4%Z_@~mVtMPr9x=9&h@9 ziDTmUX1hoZ&fV`@6uGVz|HI>dM&NN`*5gcKEUp^=Q;_8|t?eSnW**6KZpfy`Wb9aw+PIn_k4D;kYR#G^x(5{`C1IsOI zZ(6&qF*dLJv68|yaStLD^ra0QMkVAA)CEMdQn9c=pd0$j&b(#d zh1QkHa8~Fsc|M$i(Zrg#fsn_0^U_3Q8B{Gm<%o?Pm?dfkthF(Sc_ z_KMzf6A9JE-3<2o;)|r7VAt$9oxiSnj`YxyXt>Hq(4l5 zPl)!A`SRt9ja)(DD6fa|eyGkuRG^abgtw&dDHhD)!pDNGeI|r#AJTo`b+|R24S;96hH5$?2q)V z=MEF83Lt7U>Jff*X3!H`mqNw5ktBk`w;u684*~DAsy(uu3x~^33t$$ z#x;D*E=cAGvG1o(vpz^4T! z%-Ng<8vn>(;fZkE?JA$(FX9e0TQwam+p(VrT8Z#ggg63!5J!@d)0T|K;p0Db$R8%WMT#k9drzpCq+Y%~_y{Rezy@3E10sxnPTz00XEw`lvs*pS?}hPmLOFzT_a`vmYjz%zv0( z@Nbou)irg`(81@qVt8Dr2uJ*ej6k@rjpb!KcKQx-9_6~|P{RNIJ-ar_duXGbnmX)u z!?QXsG;ZTYU@g3_|tQ|L{1q zUu|$t#BBLBnPq~vU|WO5pvAi=K?vlZotd$EIM<>6sHyLhwn*Gtk>%87wLsey%n@aMsGUQu zJs(8(hc}B6Vxu7aNyCMlZqVfq#p+PfS<}<-PrfYo;hC*_t_BS~ytVw?JM*>v9kOF} z?lt3NF=43x)_zl>b6e-pLW!WchD8QP;)~~{E#V*rUfP!lZv%XAu*2Io-(ENLR)~Ba z_|!ShY9U+3tyk;jb$!n`+8?@Jq=RV*C?4NihzcjSc!veYa`|P(&jr({ny*CFOPAJKjkh#{)Sey;d z5@Q8j_;6W;UqkLrtk+i>!4kJDBFk1k$d5DOsus0`O!a*bMDJ<=Lhr zC!ag+gN{3QCP1IH*!uk~Wv)1SbvsT~)yZCrBqc5FaOE(u@g=cu?h>7>!n)msu}M+s zRQM2Dkjc|(d9io6S+>22^QesO$4&khyurb`aO+A~h4U0G+eUP1+o44^D^LspfPT%76u7Jhl+m4=EOO0 zeQuYRFV6jwDy5c{+OyzlX0k=P`!d=N!eB$G-chBylH`OavSHa{t@ z-K-7bY=-Q2Py^lBSaR*cr~q|oZb)PKIDMN9kojqY4x{yDhIUR1ku;F1;s)C_Ip1=| zbI&c0?C!>b%W=nyHyX4JeZarB&K6b1K5HYA43On_uvx!=XXIt3W_j z8;bwekp5|?ukey3*PuML|7nULNQv`kg|}2H{)Z9z34uF!=f9QyAEx+!jR5L@jo^Qc z;D3!k?0=2m|Bn&my))gFu|k1?_?0Ck!(a|R13UX%Kw!~>N=Rl5y8nuZ-vcL@Nf8jR zCTCsReoOpvXrLjJg>3WFmno);ZPu&9I$cs6Y#xLh`;db@0|5pr-37n`oL*&`) z6Hk+aWtR>o`OLE`T(zp>4dOVfVfRGtcAP@HWsvs`|rRI6@tBE@#EM zhQ3nj-cno@`a+{~iTL-qu6vb}zMSpE7PMXU8gP6i^L<{e$!po6Sv^Q6SzJs^9T-Dk zJuZ%|QLb7^&(GhXiKW_*G8_nvfc-el!osLm{Z$)kh8HY)3+yBcoa!8SP_0lm@N~Io z0?S%x+#F1F#@@C^Kn*(#-5zmTuQVj)Ea!_Owxe$f-5oCq9ocU#gr2gU@-3J6T3MY+ zbvq&;AXvRLS>L~VUbJ2)RL9_d1uy(`u?N^UHv^OFo4_@&w4l1=%8ZZ)-=7DBKFkAQ zP@7h5y4QF5`pS}H)w&(Q_4K;hyEAy_!{v>gNrvM8I;a=n|EvY&dM~s&U)VJJe7yMV z_8--4KMwhTXWZZv))^DyXL^zAGqSlgZexG07=A5Eyjc!eW0LjNNus(q^;aXA)%po7Qp6kH^xiADVt znTREy{iQEJB$LfCrBb7QXyX_)UQZvf*_Z?#Gh3o@_l9+AC?nD7HEE+GVe> ztQ4SB@OA66+C!#I*YZk}rJBKf3!ISCVcbL}-;^Q>Q4^(XrsgF}Ge1K0DzV{2F4tzU zXhr*Os8GXXHeY5nv!=n2iQtw%V${V^QWC(_y8oi|7*5xuP5`7!*b`VoqvvG+Hqa29 zY*X)-hvpDuyqwFLa7UtRs}LXeVura6ScmHFXWF1L;+OgA3n2>A1yhEhtW;eu^gfVF zWWevQ1O(RNgW^@bm3qyS3za&So87x8C!a`i4OJ?o4$HlSd7fscDs($N7V1r>5+k}B zi*?FM{?k9q5{2?g>EI|-QOChD!044?dgnVy-oa$ol+s3mFuRN+nKV374$gVg%vKN7 z;d9}3?FT?g3wRAE*ZBd_F&~R)2te*EX$OeZF;_$^LArHc+`A`A)PdMC3hp-gzN- z;vd{LlTOuhQiG|Qfv=XqX>PdMZ0m?RgrPsJtlMq$;dGQ%bJ)qhz6oIBd9Uy*qMjR2 z`UUj(Q&hApMC6Gi^7gI$a3|7wCpobKoqp}mlJ;!55>SZ_g!}!j?TU{$? zlsM`HfAhO8^XNM03S4t2qb>J(=jhXao(kOG;pX`B%J6HljHgWkBc(FTXP=2NDNkGN zioyp1NMSCn!tIu&Fk;67+__>IgbB5?=E5T#%WAn0d$cmBfdP zfA0gPVh4AELV&_te9~!UiheDBkPDGqX!xXUV76)Z`j&Uiwm*0aRwIs><1v=1jotnfeX>_ zO=x0fyDtqJycrsMV#JIGhH-LMPBmlIRw}G@3O+93bR->STNbct8NA<8LRh&!ObHG% zfV?L4x+F*g(GCca+X3oU@FlsR9N?4&S5s*CMg1Dit3-1Rzj|O%pW-pK;8eiy(9Odi zEZi{@0?&G!_IS!hX69vCd&VS9ixy?VDZ`15F^(4jI8? zU>nxXKtZ}wAYIe&C2=Gy)jK5}(|&=ju_u(XQMOAuF^1HBcC^R$NcFDsa^$oPlC7R8AiT0_rwU z6~rx)V?cFj7$>Q{rGC~Yo|6fpn9hdL?X-TDIP2RXwuL`U6kO*8>)P6mNDP^eJYN$r zfoiMX6zq4YM|vkX5Fvce4N2u_lez0 zf&J*Siq9`FH^%S$u~k?2tLfDbI@|M=w_NA;K0_BBmo;`viU#t+h8yrTq*C8=>VHkt z?T0llb$&-pZC=!E0-kL0hu)cN8f131UAh zp9>FD_W?(BpRp8%vtlh9`@0O3lYwMASwgQ3_bRzIPssh+j~d4DNJkQcZm()5U1&~x z*T?+RMpnk7?V~2eRi0=RR zW=V2e17AI!fY7%(Kc%}eo{Ky)yao%y!|rP4z+Q^k%for#u=3*b9g#kER96qY>_$+a zAv$)>=J+FwO;G!KVaLyG?jJHT4clbjTY`zG2(x7HhXSrPEE%!48nU+!sd&?rl1$tQPyaI%v5LP@lKySw>?Tptxn^XCkF+ zcCbXQkM}xxs3|$sMPu`ZSAVN^O;$7>CvI+o1M4y&7G{A&tP7^b&o1?55L&Y#GG+TR z&oU=;ZG&p1`Vl=HW3F59SmIsQ(*&1IU0R02fb4k2*3ntbHb@H`u&(?j+QOXu8Q#t6 ze1+e0zns3^U8bROQ$>HQd2jVS2~tKqu=0B*b0$0c%nB#+pfg8iE5bReqoWnajRTK;|y-CJq=tA^1Py*yD3Y$#*S)k>%&dQ&H716 zcc(Ec@}%vPs!Rbe${x|)_G-kk+vvsRY~DU}(j?SiX`Y3aN9f#pK_T^{0|{~N5fUNPckyKVRGb6@p&_oiIQ z4&-|!*M?nwf3_WVh1vAP1)C<)UeAC4sW*?iO4m)n^Y*w+S^nHUgmvqub#d2^ zh6X3K&0iClt#0vAcw+I@`6o)cnU*V_fP#@c+Yg^fk65gDr|pl8Xf9rb17+(1^*XTN zzH9Ey#(v|DtN3U2&sSXp%yxcwMjh_so)=vwE%#@u*bmRFe0Ge^v#m}McB7yi$rvS!oZ!`l8 zX+lJ#f8hpJ8#1f@OAFw2_2{`e@+rhK$a^NU8Z5gvGGR--RxbxGWIN|^f4+Zh5)58O z(~FL`CQL@#mhF%k`>5Qj4X>+KP=(;J9R(qM&^Ps>qAk4Y?(?0fna=pN2l5O10AEBn z%l?pT+pJIpv;zI1+Lw7XU3){03%v-XHR4dlrjBM2V#XN^8_*e=6#iWmEU;_TKC4^R?HP1bECfw~@C0`^{C35w1il`(d#U;Q6i@4LDZxds!_eYcO zZ#;d?b!v!r#o{q}Ysrg?B`pR{gjl2b`#D3U#tmj0tAKj$!P1Dty4Deyn6a-GdfqvM=YV+)H$Pbdwsb z(5zr%v6gp`ow(RNeX-W4^=s$r?5T6ic4@eLsNLRn*PZqV_C_mF%PV*+uTOI?OqWo? zv|Z(TF!j}CuWqQ-*|%qm3=C@W1H^I8H4#jE4BCkuCPeY8i_xkjoPe_?>aqmF%W__6 z+d*Y66a7|psc>Wd`+%E|v6EI8h8^u%9cTScal}Rwyc;Xn7N3x$-1WW?^=NMW!Y+t-z+D&Pri>yGnrwx;Av`c-pyC7m61jc4_`$?OvL&K!a!n(! z+h%vV1jhZYzwLTv5sMQxRPFVJAmVGPGuvur{(WXes7?5*kgeZiq(S5wD~h)L*!(rJ z4U9W$`Q2^3(#YU$J4*?-sK@<-iBXq^c@Q|bx+5OR{9aj8fL2@u3`nbO;+9bOWYo#+ zqQD&|`6wukgMjJe`ubWwMPDn6A(lZh$!kRND9=7NIq3kttUX&8 zoSK^pmu!>J4R@=E&vF#Ps zX~Hn>wK{NPu5ddIOSIB%S8qgggsS%Q{@Wx$j_1MG)f$7&x19~oY;~#aVALxVk*xg3 zW>gVJt+n=!ss?`4LF}&}#?x++-a}CwU)p0K`iD>2@6>fO`E$)oZqCL6IB~fAakOBkvWwQKn%NoM4a5}q{DTqw)Hj~R01?r;+O_#~u|H(a8 z{#K*Ttd@_<(lDi7=3%sWeim{ql|ie?&StJb6NGOixKpPP21;8gP?MuNh=&D*BlevB zRByBwlvIdV_!SukYt*9SJi+mk+jy;vrykU@tD)L{f2O^;)?^YX+pism3jgui44Zv8 zJjLVcwcQHRc^e71`94IF8EyH>A*Sy@j+r85peOZTDW~7pP!_@R`y8Q|^o$r6qQvro zc}6>!)uA8vXp~fJRs+NFlh1yMf!Mje8?tDGj}x{Po>PS9rMOf`Z&y&4%cePvpsSlp zAwn9pp6xwr!5oC^aAT%{^Og@T)E43)FIKOkpdV*>upbBGhn*hN)<)NVa@(jpFd4hY ztGW&}>8*Z@pFJ7B>e6!yihAJ*8dyeAh04(HQd7nOYRpQ%Ek^OD?K>{!R@eGHVYEZp zFNzA9zh24tS|A0CLXV3Z0axG{SUfMT#+1wbtT1wt?U(Q`I z)dZa%#?LietmVv_w73QByt9xGC8s~hnWF+$J~I_ATftE-qQusz+J)myv#ps;-2Po1 z?8{s61B+K!jDfkC+3qg7C7|j5)Uyo?IhsJa9@6*5=uK_83|>= zp)-;UgsXJ5f4Ea9L1jWB)+X1u952%Jy*nUk>yX&hT5!cO))Bp(l*ookx)9Pg!+eV~ z%->25;f+4WHu!L~CQuifhDKYk1T<|(Hcw$2_t(*`t%!!Lc6Il-h~ncdQ;ojck!knT zEEJt=J)u1jdI_wcsji! zsSB7<)?mc+`ytsw&9=(`myrxn+lyTmRedOt$FT0xl&`_|3M#}}fpp~9)Kopu`{DPW z#CvX&C$|U#Vw%@8tig=#uiJ*am~Z+=JN)ej&nH=*#Pa+Q&&w$9b=vY~6r-&3#`tb{ zF~d{3_4=OEQv_Yt9b++>&DKQ-0jgck*FV}X`ywlTG*?ZYMP>&5T{(>5yKqVgdp*zZ z8IwgWCjG=Shy)HIGSm`e*CQUW!#=}dihn;RObZItj^+30#?;_$sudLQycZGL*zIce z@rQK=1XA~;rp}Pm%X(FY$l?NYoiPTrHD{ILcGku$`4}>}FJ4X)<*xkd=qjpK zl+y&d7iq*cJzgH71K6OsHPbrp4l;lGndr*e3thMS-L2QTV;%%!o~EqYIHHmbXWWRz z^k!bAC;V4-UHA@ejy5j>GfxWca2Y|%!jlnjR`Y^RWv}!{#yJI?nqmG+C40U~EN@dS zNFn#i9ltYBO_k$;ENIJIf!5TDLmIUy&uO3kt=?6zG3S%3a7K$`!U((|RfLmg9z{TE zi{I9)C}ID)k>Lq`Giz9E^+`_MbYq~sIDcM5=%DVKF3S;1i5?eWupqT z_seis%|e4Day5ADmFI6U*{u?!fYqsBoeFIW(1xNW|bHVCzHi2{UEZ* zDqL5YK?w(qmiXFpgg$iZ_XLsR;y=i-$KbmSp!UT`hiVLW0=#C=Ax#z7e=D^_UJah)*B-9?B_DTvbr>PVd)vuG(p}lZ`2ERfWQCRZT{H0Ai-dVyuV3>Fsv$m;_6HgmzLrSICX%N z0h|o`hqoiSSfL+RR3<2GrL1JJ%oAHF`0*j60$M~kE3tR9Kb{*=>rZst-7HYU8fE@C zAi6M8UOZjJu6Isd`O6({dOireAn}QEn9nS=&LRg{+Bvq|T>0>Zv4p6`f%7n&RL1UC zD%7N?L@fBdi$YC0e6Fp~MA*hopLbb0hlc0R>j7cHS3ZjGad&S{fKfz)l7zJ zcUorZROs4YBj~g+9&+=<_^iTe`ztHv;E@Cfhk9t$TlThM?)2cHXgEY|fS}9d z)$$qq{5Ou8A#ZN+(CTiAb8f+M5ve-B{7On|{zIq1Ex@;*B2!;|RfX3Dw72d@j z9PBeyMz`Or#8QVXUlr6Y{T3{80_jZhE0LRd&f}!$<<`7$^9*dX75h4-)4bMfRcCK! zwfaI7ZitF(){bncWgy(j2s-6ukWjfOiU_%R1HXyeu;Cn_N~|;PVpDIqj6(S_Htewn z%3(D;Pwu!;1|)A>IXVaX5ppt`Yi7+;kFzKDu@4cm>#`$Zn*+(H{lZ9x$NuDzSfvVR zaO#?NvHTUIgV^w_i3(VAH&1IWU3R|qgsgVN>#Z9^(OIXp0Rx`AOR@W01BU~5XcDAN zG+9xCV}hs4R4v!<3|>g6tDFo$i{iJ8^}ekdExih-55Mor0(u`!e~)9uOTie&X<$2`P-@L7oav6BhvAL4s&@u@WqT-XZm1B&A(Hy6*5eMA80}i@`%wl5MdeSk7wywGDQSZbh z$M3-~LUv9=48fm}&_a<92C-?gJzlM}>A0KvT-^$hI>za1ezn~gTFz+@-Z6@H6jI0T zjm^aTp1U5pLmkRkbGy!u7A`;6aE9W>-AmVJWSCADD&dO6TY~^f@rW|TbZWXi4h*A! z4LS`VBG-Fk#@oxvIx$nu2uHGd(=u+>IFoBo>FBGPbi><(59f!6yrifUButM0AQw1wX{OH}y6$`vDfqOk>ffG@BgxC$RmJpgLM+!SEuYY-zNxNRA$0jxWt++DsvS)OW*M;azTD7?Xj zAO|QW%AALx8eR_{yw<&WNi@5d7xe(^iPL$`zH<&a#}r4f9_n-n6Z*=j?kM4NanR!) z=oz_f8PFV0yKy3>SwvsUoNrAlZDJ|R~Y3@K>gu-I@% zqZA@Lv8stJfY3mEX|2!NaxDPB$VNrjwKZ-iFW_23haWYy)KwsEp1lqyo)Tw6Ji$!6cn6Xw3gO*`E7&%N8DVLYsx?!;Qj z$%xCDgupPCoH5nPkroE}?=RbcZGnfMrAOX68xDf|pP6hJ@e*T3{A7<|3G7`hlOse6=* zK^bM0YYAUz!QGS3JmZBYd18~CiVNNqj~U_?LXl8n+SItw&(i11XGnAqBf&B=Ci$+8T$Z8 z%g@hPIvzjNw}furU~Z?~4q32XZa1QJJwb>zkK*ncxlC0ft56)U;s@ za{A^qR~p+~XQ$igDcKTH%kb&Z*vR?H&=tU2j8FM{%)>-6m28z@q zYnCwt@I}M)BwF&RB7enZjR#L1`c5DcRb4QA9`&$V=<$?HX584TCG|m&LfP5Tv;3Xk zH~WV(?8}R`r1|=m!yQlEgw;+==G#Z%L#!rNB{m?mv8`6UW{Dc}@viND{tSD&GWfaG zp?JkvG{dC}#C7g|hEy61K#fSGX?u)?!C&0Dr&$XrgsN?KihJ&HJ;kkgrW@NafbuWVR_R##iNS`azjP}Jn1Ll>_b9>@68 z0mC<39mUb?H69QQepen*GM7uH#O@0oggM zUZJ&(HK2#TQ06+nHEY;c)Zc*VcAo43@*;l&>`=?M@qXG8^#>Wh>XsXjIiYtF8T5}W zIMX$BY;|)zJ+F5^?ct3&rTFGut!sUOQIFDO^P~4G&7H@PR6UH~JCB#BfpF7dVH~T- zR-b8Ve`55H7oHgFK|CBbvUrknW$AVvYRH?+#os!i7%dfUu3b`G z*?&aQ!gU2~hDs5vHFzSijp=VYOkBzxCE;4$Zc=N~c=~2`gFxP;O)pf`hHI4oy7O;7GVEm2_!Ek|Kh(7Ikgv(c6* zM{yJqt}qXwh-Tm9I`+N7b#4gV=t6mx>>drAg>Cai6f#v@-eFE{4#+!g)lDfG=I|W< z9-Po@bUS`CUzHSdyw(-hpI=zW3ko^jfN1Ma!tBvGJpwPb>=ZmsQEvz|l_p-r3$2V5 zEER1O3O%QU(JCt~n_obTL=Yaz&swdwXHu+p97uBUMi?x6Is^EfUjr=}nyzAiJQuk# zw964~-n%4wL^0MA#~0nT`L(Rf9E6)gHxxmTbnY?*hStPAx;D>mKj~JvJ;xX!w99ad zG_}s1-&?(8OIW?Uj|0DvFUBUvt1Mu&1h|skmzl!v%ml1DZ})u=`=&us@nK6^CO(X% zZJ<|wGtyf3%W4WwC7yQmtQAAj%t@9X7<5q+7;Hj9$v6 zGAph9jiHkgmzRTaGCQyP&Bw>-3l!P5cG#d9T4LK!p-zMbm0L% z!zHA|acRJ%i8~WJ@>3s}aI&n$w zHxX_~ZF)V|NF*r{-w=HN#l)*Eq;qWUl;NwEPISZpo&ia8MZF|4GI~9{?e|55dVy6H z4!wdf%HB7Ez2ESXPOqyk}XLUO54<@JG6s z)PRnZo67p^^~XMTe^fD)Nn(uDHz#O(@XL;fS(nUlxDB@{^=vt2PS!Zr6u?_{yEb)6iOOh2 z&+srFb3ths?dpM@lVhK3)&MBjH8sq3><1egOiBX!E81VD@S;)GH63qtFR>H)lRUtw z#O@IsF5Q!OjAR&sEl*0F6DL+pG?T~a*3xYl^wtgMlI~}$-XIK6Gqui(Nv5KYChcw?6-@D5t&yfg4KvMD6 z3F~*xQ|~@*_dB8I+WktkBls|mmArnOTVGg+E@L?RFnJ_x&NI7H3U|;N0OSdAS_E08 zWf8G7OAm6doG&fu-F94kzNYvo9qEj#K5YGZEu+_Kh_|;ZU`fQGF>Q`bm*{fMrSJaZ z(rUFuQrhP0TC2@_rO&IjS7-aQJW;vSrxYvvnW#(IKd$p$q@o9_-Z&R$PX z;q2;=4rC41!a9DZJGb8vxe+{BX?z9h;W5o0rV!eEH!jCeRVBmKRm4*m`r!xU&(e*4 zb?IFsRQX)GS+;8=+AmX}L-bxsdD3)bL2(jm_K^UBEVV>=4UdKKL1phFO_i_6+t9sY zpgX8z4K6tDxHdUjwik|%1c#Q@?&5t69ef-lWp%_>;Y5R!*3ZH8w)^xstM&P$x!mJ4 zz4)mh08wtEahPIrw35Z+!vM#A>gABPo|6`}{l`A8^UsU~!xPDSb|6Jv{u0fg1s8$c zXAow<*WQBe+7oi8^*qJyJ342$o^CCOgLPflVGr;u#!7st2ys=mUg}KDAS6+38C$GO zA;>nZo@@N+CgCjvS4}C4c>qKpG+yKFo%;Fv{9^@2$gHYE7JsPD^|kPi!JvU||C0@- zYOW)H%uh5WJaF2(?nLCa$D$AP2#&1o;p$w63-u&U_I`9%Fb9$o!J^p9$lTNf^Otee z+Qg;0!RHd7a)uB!J&3*tr?Ej@>swCL4P1ETo!?Mdrs)Yv4JomxsAq+^)KrI%ra{Eq zW}Mx^ZDidE0XKLAd$JFs0o$cEZFV%R1cel+k66OF{HkzELoAlz(qF}599hY1BD^fR+;xkT9$^Dg&D=vD8GVxrx8&?d5( zOvKYGV+wl)E`C3mkRr9aI%f_-71Q)>K?a4wz#evzMKTgYa)G&zzMYUvEutqAEjqfh zSMfL9x4LadjfbSLxtDHVs>5hTQ=8bca#1Tt0!FV%_!|ToVVUmEJwgR#l9&Q&S5`@6 zP-5DEc9^)Pt8-pQY?7{7?AGXI8+7>`$!`cCNBnJ$Kl$fc=?Hf{B(U4$hP6;sMuPb9 zKHBON?Xk@M=9%i4=_~xe0IN9$v<=M9&)N=o2_xp?&N#$JkNQI2?Vejm{vRsmMuj~sa#3$n$vaUmI!AuePAynD5&6)X32 z^roI24gwpsivdA%ert3FP1sIdMEY4cNrD#j@{=x6%LCX3uo}r;luYZlcj>jpQ;%)W zfuLFF(g#SxO+LtO=k5~wircf68nsNp>kS0gh`K*D?%I-)?PW(%>9K-Nt-)2j$k{FE z0+}}#Xabd5t>(2B3SvkG-$EXfp65eSkT63kWLE7@i6P_sq{bgh^9DYY6hF89#5Qzp z{rrZlJv}hvx99USsyajPm+++U^U*}{_a{Ap{JV7A|QJ=Xe!jIcSj&tRoml!w@U|sA2`>M zg1PX76jMr4nrux>;^&mG)Ru1MP7hNaPVh6*Hc=srdkZ|C^}+Etcc*HX&576laJ|Ps zN#5nsQTpc?D`asvMRtE^u0jmp+ZH5J>I?+ed`lwr86pPQ-c6OIH47lR0OnJ?Gk&uwO|!St$}bt6Iuo=D_jpX&-v@R);vq`!^4@P@qj9p>2xjvz8Ufri9Imur3R8DE`PO0u7L z9?{er4H|Cg%q`=Dt4>!LhxASjn#*%fFw)5RS6+p_d-x%CLYk{lN#Gd>Gqa6TVJkA_ z`fN)T4TVcxpwu`%g08|h__3aXmYbcLAwewOt(fkhx^DqUEehP#6k{^Gd2B3fce)IjQZ(T~H=XXMV7=#`rn?_xmCP};QR#`#1w#FB^JKi+H)S2i-zep8|P5*r(av8OzrQIBD#&&c-|*l)InQsbdPRdBzKwUR%kiJo`^TT(m;n?P7Nl+GCHT5Vw-|Xj)u^KA{*TMP<9E#uYL^66{I&X8T^IM45 zW$>Tq_}AI%UQv*eDxO972#+B;I;dNtfRc}|nIku7;5+uG3i#)S5p4DHF$W&N?X-pj zqM>8dgEu2Un{|R!=4YH|46v@I61xe31VdfOTrBX#u3M69!*oP#zx}Mcb9(D(|`_nM{l^KMk7!o8$TvH_K#$@WvEa_Gq@N7l(FX1;*!uCzdvp-*oVyK2LrB^@XKg$D4SeS*|(=x%2iLq9y935Z>Nikvg*T>}#$sHJ|tZJ3YQ^fy(2 z?-K&Ax{xO7HxqJ6gbf89_O}b=r05oJ8(_bX3_lP;zPlSHzx4f);17_ksm$Gb(_ruK ziB`gt>1@#cp1?j7RBHCvqjQ1ah|anU#p5&ldPQ?(+j_k*$itLb*F6(um$|@&T6>8V zovxVzEj&^3fn5A;5zk=E{FYMY(44$Alq&w5HKAmP`pETq7NlDoCjl zGq)>ze8`y`A0IydZLsvj?GYc6HcpUuBgxY|OOt51-#0ZFK*s93u4D5(lwR+E%AN%u zu4u4+4KK(pxVMv!8Oz#Fs;ZI-Pw+DNJhRzQqekbAz(Q|P*RW=Si}#uCMmezN<2s8M zZrSy^pveOk3f>ZPyc$AyXLkVZxZP};#_iEkV|Oh3EVR|-K!lvO5p?VH*c#$$gEeSx zHYm+i6TaRR4wU6*(s1>j^8K1+A2ZDEYsj_Ykgz)FjYbe>i8EX{oJF*Y$r|M|*{@H*gO=OdRC~D8z>ktk2wbs!u)wIt z@8iuqleaS$y2u;s7p{rOMLk@vEvwVEj3OdfXz{`=HM>&04&ryU!wHQ=#HL2|M3~oW zc8kz@ug7QLivt>%mU)0yLXGM4I&Jd%m0NuxOtCI4=m3+gzNpzS&UGAs!c&y8u27jq zQB}lMFX>dz@~t%&bN9J;300=^Kn*+y^;G+aMruwVvEO1lEg2Cqp82kMSv1wI&!gX4 z^LoN9%5h-iz4H}NPoK~dc-S+`(nkS2t@P$JQ(Qfc4wgB+7u5MNuScKEq*pic2@RCx zR_=$!q2MgmC%uL4Ev3(kRSio9TItI6f1Dm zX||_*Fmr=NhlW)$+ri7!eQvgdwf{p~e+JfPX-TyVHs`g-7zZr5uH=rxvBRN8q<#Pj zSq<*Sy9_AyM61wx6|xh(7oA`nwKKw&Ujv`x#*sj z{pv@y1iurUAMdb2!-`g?IRBoH~9eqp0!#Rw{ zG)mmwM_2pZCLMVCC*}7t@++G=GDmT^@dy~CN2G$4@-5>4w9MH8+?BypZJt?>-j`kR zx?!1cx&90$92^{-#aqG(ZVA5G9E%(46M;VM-xC8GXed|;45kXG?;yGutO}3++5&ii zMF%apgb_)8ZTsXpMkD{aA&mf@T;7Dv1GJt1&8!sxH&(81#VXv%bJ;od14X+Fy_P1R z4d$zGW`+`V5;iqh2uQWJE_^2=@+jx}inky-yosGZlqW&&#fggok*rDKCk78`!smv* z?knp@NF36lnBtbrhXJ_F-)Y02dI`*!-TnNLaXyO%gWda0nL5)QU~IC3`^i(oqgP4{ zm;YMvUS(Hp$KC;1yXQ zNc`rtYj=LqOT%zFAJ-~{75y0KsuX%;GQy$*wyd#28^^!itbK|r6 zQWG)v(v3Wwl{YWhwIK5Los0OL!gf~Osqb=L>V&*Rri*3|1i4Cn!%4 zG*&KB>Hn;dP>K9)*N2*hD1pCCjkH(t%fuf1kJ5v!q<(u*NgT(53!3+B`YoHEtvcFE zG&5~_PNohS-)xfIXP37Jtf-+vsF@ry(8e-Wd(rA+Qu5}&|U>1KQzefw$ zp16H6AAvk8!H*^I;M(JxyEjdOFDFO-rIS4SV@RS>#Kt*pDopGlV*+2uL~p;Mxj%Lsi?SI-=+miRfqOJ2km6_->m z?&RHU01(!hDIYMC1Eg*KIVrPb7-we=5Q=&+)3`~Y^T|wlojOydv$>Ofy||!uw8T^n zBDZoRCR=#G&slcT(uZ3Fn7(%?=Wb_gRUn|W8XJM*8`$=v z1A|D(&$TzVQrScA{N#`{)Nt(>KHBm0NBA!;k!%8w$_PJkxvQ2luS6eepM5b12}o(S zziT#7sj+`9v7}07tzr1djPKDU)ODSuBUEhnlHgvL;3KNQtMa` zrzz>fceJoKO1ctOyy`0$Z>bXX64=jGmP#fwhkjCMt}6i?@x%N5l@jghurEzol$&%F zGB{>dJRTis+YEvOYBeHpt|U9)K}6ax<29UfVDq?(f1#=Pq>8VlJ3p34SE?p0$bCJr z=D!B+N*s;!udO3lZE9bqc5heE8jAN)?j*z_O{>7*e&357aq(E<3h;<0D0=M;iFpp-Ck`ZR9s>}unN85Y~ z?oC_7brIUX3I~gj;ckWBi>k^sU>A#(Q9wa%Hj!El;rpGg3aIgySN060l0>MniU#2T zGQ;qgrnEbG&pOI-?}(>%Q*to&p{WT z1IS46(YX&ob#y&1f2{r)jsAb*zGN#>6Gi{l`F}Tb%F`&wm5; z_}C{y;dktBH!_b4jJI$X19jj{n*rObFpjYWBfh3ho1L)Q=oK&{_y^X& zf1}bAQA6>1W@1slng1G3r=w9mTeI9dARr(yCB?P8S_vXALe5qC!-GOU zGwlTf0)g9Q`Hs5%lDmI1Q3qVO+9Qt@iVn_g5-!g#m&&NLMcQO3GPC$5X|LymphJg!| zOXhmEagBy^Oj=xod|w3qc~(QK&bu`C|)I{KWFe`JXN34XK| zj>E&8!_Z7Vw{xs%ep(Lc+M%fcb%UiS)bKLSKQM@46_=&2BRgp-+e`K`&>4bh~ z6+B!qp6ok*p}A{=P8~EEENZBJE8}FV&UBXMCB`Iq9{4%>|Kg7xQphUc+~S6Y`Qsz? z{Eq$A36?w)^`PH?YIt}Anpkj*D`LMkR$A<$M8$6b*CFIrkw_|mzriyu3nfr|1OBh; z5{7FFgDYlI;eB6)bjNayS9>Rm`J>gIhK>Q_FK_Q-A1{0bwMg8+)W5|IdMq_4-O)dCCycQtgl{i zg;>APj^Adk74=$6*T5^HxP*b~ZwT1l!`sQWi2E`5u_|g|!B0y*>*(_qCN{Al za*SiCPODm?o{E{wbvY*^OSxKPOwKw!3BED-jyF*~-X=goVrGa|kv70r!sUH3)DAV> zbPHB%!_dQO)XwF!A$26+&2pRUgYRGdOtSn>MUl@Ej&OWLMMdU(a-oOumB~2M$U9R> z5Rcg`ge|XiF87+wBz@+W3688^<8T!hIRh+O2e&%TP+cG9IIB=Ys=rvm9W1qfB5>co z5CQ{%WNd!XCip64RSPo~RySx3D}gl!`*2z2#@HQkl%ie7U0krorhe8n{zW|Psfzjc zn*+_qU>PqPKTU@lh%D=WKG~ng=43v_-vw(cJj?rV>*<~xES4rn0T-Sc>eRQj{@9qk zCFBpN=*5}jTS@DLE}c#B5B#koeA5awWLNF8eKmwzrLpp?^)}B(fZ*WGg{;#BMOKED z3YgE0pAtVs+tBmwL*3ctP0H^$L4W;B3&QlvC7SCkm#AzSGS=BP#6$$_vD@!rr?uE6 zZsTsPFUK`cD|LkWa;m?E!)ZeL*f&H!Ql0*w9Zc0&jvkYPB`HFUSYeMPRPx)2n`{kC zo^YQ))t;L(Qyu;?crUj9GIGbLqqU-g=_Eq-b0cASqzLA=s1i46f76*G#X)93o$;1w#+voOg)8x4$32MD zl3>`-uz4`vkc=5T->2Qe!o*Ug5g&u9L{D%u-~&ycBU&+iL=8h-HYLQ&a%sUKuoEmR z(zt=E@qyW-c&Loncm0&;lO^PJX=8!L*vL%ZtV7cBtRc88C1#M&{}2{!YNA`m*xfA1 zL?!SRZ7#Arrm>v0$6qSv2-8{MFf%6WIN9ayp#B#O4ekI_VcO<|8oua-;z8l!w)8Z7 zzzuBa*`$k|4hi-dkqmf*vu%PeA4>&i7RJde7~Uf3((77xv4RitGn?s4&GLU==70Cs#EF)iHrqLUAFKIZ9^apA zG9iPiU92L>Gv{{GOh56IOP!i5AB8brTHdWV-ZJ&D_`<7`HL%KqIS58jjwU1XEY%kT z-Cq(Xn|g?s>de~tzqDGO6OASyEL&-Rk>!p4f`Q0TDf-H6y0iRHaF&nLGR(sIy4!1R zE%mrQCZ*C(9+4v+sC z_VF;nMKX$*QbUwYHMu#)yq2C{HP@c1j=}Wv`+-6z$+2BS5;?uw&vYT}NoBU|?sO032<_nunuMdlG9NgYo+jzga!l5nyW<&;()exMh zI^ctAS@kkX>Xqm!-rp0{a@WUMiK=8bgWRH1S-y3$vP1?2cmCKt)kM&(PG7q{`qp0G zhFK5T@)YWMGkbg?PoSUFpX0Qu{#@BQP~s^1hA7bOZG;t^fCyKOs*UN(M6}Ms?S!nx z3s&JLD;&-F5An@+1J*%ymW!813Iq=$I!Q*sp}22So)dIfYH|+SYTC!#R<4bSzgz)k zo1&IOEhcIWh>#4A(`yOJDh~Yn=X=J0rQVk(*Snv_6GFoi8s+)^8mvO2)@(O87#5X@ z`)_(-nPX*PT~ZSCxN6*>^@osa{;ifo&-5bO4syzj*U)6N6T_&E)IIN@RpYp($Xal- z`g~2pYOj!BQX_u4?b<+^1 zZ|}H>2IeDhMWzlJU*(FTC?ZJF>2p7-{`uQkF5;EQ4AlAsF?-!g)J1eqGp4L3a!*P9 zoQKclM@y*%k!i+GFYX6<^(bz2Z)>vhEED(hPZ%1k%Re?hrv3O_Bew{e77W0AA6CqN zue&c^!OM>i)*7HCoHZCFPnX;C@X_&?9QI%O0}Fs-?_#%fgM4ekF>=<%VJ;7TNe$`K zX+@TG$xWGae}r$tv_LsM)6(qXA)}Dp)$uerma(?6pVrUHFmkxIstw?@Ys5RgQ=zd~ zbKBvz>+!pauT~sO)Ke5Z&?--+@06l@PM-`t`!5zdnvSi5qaD`W9eabW@ycx&mKgav z4UZt6G+;9geJg}&Z&p`=L~Yy0ZpTpADP7D_LEm6bAcS(*zX*(hZ-%?xC(M)92n=Zj z-G`yvhphZ^=EtM_yXe_7v2>0AWZ(HhHsB9Mw@x(>|_j_)MmaoRm)eDMi}z*vO&AdB`d<&JrA_sv}1k(&UmV)O6w zpB`IaL3{hXHtVON!KM_Ixtg2e30|Y_eC#$ zKuo!5883rc>qb{-ncHl_F?I(g@b=5D??U-8Ylie*>!Z^`9I9UJHe%g^f!LM1}m4`43}(;_~b})3fbSt!8)c=s$Z8aoP;x5>RaA`1V`0Bi<9~gVN zxJiPniXQ$e=j~Jde$Az_uv%kau&N+98m7KFH?Aj?4}1q%gGqXc3Me9>}`{QFMzF_#>^y@kw&$e?52=R%voFP$nXc2oTa+QJYpjclWk>Lkb zAlU?Sgtf)+b(uXSo?qv#SScUMjYv}udm`?kcX2$NGp{BIH5DsD*v-N$WjTepSz>I4 zIVc8%k1$;H{=uZQoaB4+I^TPOVeHvEq{uch7B?bvYe}7pA?6Fu9xqLF83xT7_KBG& zLki}$g;^_e9KJ{g>$r?{w|bb3SC`gk))OrH&9qbrX;;WLAJ6iMfE50X#_sBV(vqJW zx9X@IqmBc~CBtgSHI&aaT|yF7+&jBu(CFIfjho%2U&#Jh;QikPMo&PQno0gJP*I9O?s8)<8Qu$qWLkSJ$>86qO zlX{O%6RF4#jPA+6+yK*Qi91S|=FVP`(opf1Z&?}6VT;RSIA&(>lFt00vgeqUbY7#r zTIAl&UpW;Fmra}mH|Ujk3~X(iz!?~J2gNLTR*0qkbS6rcniv+jEx8XvD$PFu=Hi5v zlOH6H=xq8$p9(PJRHI>&n(6BDf8K^ZWusEV&SHoNUngSz@JQjvRBkPTQz4_ifUL?=TZok%;{^Nt zif;Rv;FQf@rnSE>HC2m|uZgPSGg&5fZUhRoeHVM2bt2NPffk4Mk4mte-00_YYbY1S zRZ(bI25?R(hppB@#+&ij)7wnR%AQf0yGiQPp>6oK#IrQuX3^169ASbP37X_c{S#De z#TJoKNkRp{w_qnDT9_biM^G`117LrH-s)?D;#;N+j>^L&%!WnwF+Aq-2G}(xu9iYm|6+3Su`-Cs`O)2U%z5EPGnY?e>4Lg*=X)<1q* zrK;DE8BwX_rn3&?eJQSqq6FYaN)p$cdAJBegkmYMyQ)dy8o%m&t-;+;!o1Y{B2m^A z>43zXtb6n{Q5Q!iJ--ffVMT`<4_^XNpFRH6pZ-MCnaa0vO49;$Y3QPuIO`yOwD{+X z8V3GQ@=Y?E)X&}-cWAz)Q^lpPd%N*s6Afw{_N~wJr_a6B~iMwH+XDvP+p@hW4s^ z%u*MD)vS68YT{YnbZP)|Q60IWJE?dnrN-e%5GP=HH?XUi0f%xv{5Wjf$X-5{AfB@5 ztrfPWNN|{=Rlf1Ws|FI2ZBww~k>x!4dGWlHrn8-TC)RP`JkF-~=>it=3kI^>F@m%< zA3D`>(&jyNu1Z(#L;vNMh-)`uKrf#bEw!O{k)Rs7henijZ_UbygHR!?U|76>s-eC) z209i87WY#7RT`2tN#vYTUw}4lHef!p9l~{Jz5J@y2T#KvXWFcntHseqV~?{YBB(x5 z(=`JwUGSF(?YD`@(;8t6O=0YGbxDBk;1|t|L}5ex6}hW5sSN%NHT!2y?nYy%*HK|) zw(Q?s1R}m(grShLFxS5fsskhSBsINckJ>ra-hoSCyNo^G-CQ6f{e4L>A%Eh6QA0<> zDI0N@?BI!mfDt=fjL^8-?1>oLl{@0H5k;_RZ?f!NaPwOr1P1cRJwE?5f}2EC=G$Pg zB$__e{E0i5G0l>{)V(DhfxF8X2`)>eK45eOGyqR?BglBleVJFOjNM%<&Xxm!W+b2D zZQ87gmy~O3*aQTK$A>!uRt~q9_YCVPba}VlAm&}(;B+bYiql>MRq{-JjPE1EOj|O1z!_w#R75A@TN0qxa zYR8Z0=wIr#(_zPWeLG*6TE-y;QjC&xXt$-TuiUx3AtQK`WSi1|3lr#y09*G0m~K0! zG{!5%Yx)T^!|YhHdS51JVR19dyS$q&ScZO|}eiXkwYlN596WBhU(s}5;AaZ^Au*5jl+_YM!e!^yP zxbl=D9oyk*q~30;&Ue2J8R2Qrx=#{oOU+~xTLlq3aNign*6qT}`^*uAt;z_sLY$qx zLSlc0=tL^?Tf6&T`VG%9qUtKNH{AF-G`m(C{7j#oN^BY#4r5LpD?BIh5!4^a#i-4s z!heg4`htQS&i^YWCXA>s64IYvYAM*P2VDt}&480t2gY$vM6Sml?~1$A8a!<#4GHF^Y+Iu8~Q zbL=Ur&Rz{PDT*xe*@uMKJD4P@w(q2ggvq~{^%t@O;=b6qqncIZoMh`SnwXs4GF5bp zZ%LJW6?Jvt$6f)Smrm%<2DkCDH$@AJjp^}r;5oBS&zW(N#l^kp0K9m#b{SwIY%UG4 zuCc$b?`}5@91=Sc=g0E*MDdQ zS#GPlI!S{(%JDf|woVCL3U7S9&^bCPLEdq7Sb4);h`8#)aY@c7MU@pu5nh3ELDckP zQ`bREN-sX(FN*xTR{Ka&l8dQQO%UhRoK)q9El6=$iD$lk^>%R=*&o3RA6y#zvm@oJf2R7_xtWEvj-R`{EPa%4m8e30| zEm*u$mN{bd3FBWpCN2)xY3u$+us>fq53C?bXH$Au~j=YJuB@w=}7epqlL<< zR6LtSY-R*`YBKZiIGQH4^MkG`W9M`NCSA~P-c(j{WuKEA>!lCnG@G@tz9IkVs{c#M zfxV+RB_VqPpbww}1as|HIJq<9R*k8G-+s zH+VPfGxw#arG+(~z@KFP%NU^>pMA`qs`3j(b-`PinVHke%Ncz9{8PE#!ls6ygVo$f zEA#T*&gTAdB>eY6QwgveRL3o_VKzs?P^f0S4z02T1t8fqcj0oA)6&Y5{&mPutZZq? zWzKzLMZI6xeKrG}?bl+kBK$TUqgsA>ISaQM^QmU0nnMK}tg1rs;D#z*7{hBgNmiH> zJu`13%P6bQpQWwJ%D#g)&KFqt-N=f02>xngJ``fViK*JZn{G9t+@%rV{65YK-iSXtTxFYF zZ6P%jD(DB#w`o2@&+;Ss-+nbRL%FLW2~`x;=x*iLG;3{F3M(ovA`B_kR+IrH8?^8L zMl+wV0P#5}ld1kU7?ods!LEM|{>5VY9*(^#AAY43{}Lw}GYp5yOXjv*mS3we)XgbU zLoZQ9jt~7CErq_r|CLlm{U}zarsA5KnkFZ|$xK~r4=X!6J5$Pu{4G26Eh*W&9&V>6 zCu3>k(~~u|w5A&DHebz7G`&;>y~B_=X`+UR3Qwlp_42LLHw|ITxbDJs^8cQ zy8HuCo}i7=uJnGt0b4BA-rk;V%IfSaS1cIU=65^v5ux~6jd~4>MvRh&`U36ayk8eh zPrC7BzVuT!%XF|}QVvSsS+=>2jWcP1Tv{SP)^+05X9){&LC3^LD>deN6*@5nFXMV9YNN+>?is{CYu(&c2 zgb`z}?7%XkPv&#*H_@-C{!a?bOjX;lyQT;vsG;62kR`rx{m8k&9vt|;k@6qjPLd2J zf+vY#^$;q#2FLxgp91ks_GygL}|3i3Rpd+~|I^I8> zy(A_~I%?5hIi-Pv`FjN6GIWp!eYU(;FEZeHipjE)|JQqEBfuFI6~AD@!~6TE;)7oD ziqBHw)88uW|Np3@21VA;{UK<8Cz6A+b0Kv6d27AY@Yd_WB}z(j0uw!NBT!W?o+f5*dPyx<`##qcOw zI)J9&oX=r@g<0$Sir+8o^DDrQq$LxDa|`d|X*o^_9pE6=XM}CbV;CHt)Ii#jW#^UY=YOc|L*A z3=gf}*`nRW(T3A^2g!Lk>A#=zl(^+r!5r#j+PNT#yGhvH!{cj=;1nCJO9zSFRE3>D zua<7))b#WS+8fQb+x8?qv0LhhI4&1(IOe%a$&}e?F7RSsbKZNekss|5kA53E=a=}bvfKSGGMJ3wcb;o- z>t;@Q%tBbdU9K`^87FQ;Vq>QBPdkBuXisVaTMV(=U+T0&_ge-1lxk|h*9p16Vc>wC ziKlhl;eeR$8z{P`BN^ViB0N}3MS##R<0jsmUwls{p%I4u*T*A5P5T6T5QHU%*U^vy zX=u*7d0!#lCDKd~xy&PoiQoBZlXTZv^&s@}>@2MOXtS{7>)Qbl)_kCn2b({lRrzK|aLK{Pgp@gdM?Po=FY7ReuJLRs%1J|V zZ`OU>c*JY#=NkNn*F~69NY^Oc4JwTP-LBC%zQlRA#@Bwq>AvnV`Zry)@A4>2U=014 z5)$HveSFVejA6N@iMGG`_MQR^!Fo!R+C8{izn2j!y7>({%^`jMwdgM9MQds_R+4Fj z4d)@wI$AVBYM=O@(0WXmSMoi>e9dESdAzk4Mt7n#^XIekF1vM&9iLsh_q(m1)gJ9L z>S8-9@{NmTV(=~>ARTCUK;qU%FF5thZo}cgM#YHwq0KYUVEy8H?i6}0leLdWkBcg| zTBI;gGdm!YXFotS$@OO38sPw4~g^58*LBaUN;k1%{Wv7^-&@Y zN$&ye9Rjl<5>ruClky+9>y>Si|9u|@9?v?XP(lK%@|m4f1_Sk^MW~SsA>%FTYg>Id zq^j2?jxk^ekf!WwWr}cZsE*Ig>rdrv-_VQ|WR%3E>}yds0LoQ#>%NIry870BBE9}R zR&=GE?y6nPv4ypQl2n>&M5w1fU1a1iTf-M-X(-~Fet>W#qwLYbG4Q+E@2VeemE~@3 zB!OZIl?HVA+%AL=yFGc(9F3opPt;K%Cw_2{3J^R`6~sS!G`C-FVIJmf)W`drBn%VB z_dcW3r;$$s)~F361S2qoOenUL$YD8x12`0hg1#L?|5@!(?wr-g7x-ea;?9 zAitn-7v@VAW#C@Se1;;ptq4@75Tz46vL#}IYlgU9&%7PXM-Oan-|H>YcUkO#)ZEMH zGp~1T^hpkL_BE>~EFNT9mG<S`yv3E>{P~@@iQ7)Z37BG>v z3O9$0=gqTJ*Cx_uCjnT5s8$giKf*5!yLDT`Kda|9u{f*^@+SOmuYINE#89ZRIN!eH zh)nbb+{I)(nsmNLL%WmMG0U-H1HG*yq#BUL&~zL0323WgjxGbE_^j#iRkI$3{xo`~ zzLm}qQXWhryj9oJ&DWnZ=a11?y8bPO`AL(o@>W zPqY}LIRIydb7nyVR}|^yGc2@eVGulLtreI^3=7qlJ+f)dn zs>jlwmqn0L3cnm^NkX5RfFxzVH|wf@?TdLAri;rX7jvV z_P-*^1jG+Sbq2}euQ4w%FDqn0ywCTGEFNO+;ElFtx&KMCxln194OhA!3GoErSeS5~ zw^Zv-PR$n>i#A-5RCVr9vQt_HC_>B=LW<3(jJu-&;p z4-!dob0Y;7#${y4sZFO8(!eHA@$O#2T_k5+pE#?9Kv+Ivjl3nf44;wHsUx{I+1!cbb#z`#&qyty7aU%k7o>Ic&yx7pwQl14uX3qt zza^$5uUjE{O8kxson8d_yBQrB_E_{Hwz^QN)To0HZl6|bW}MPHV{~fH5?>K1Pj+1o zGDQbf#u;Jb6#Vjr>!xBRYK`X7L8}Mox%?z7a_phK*-d`;uQoF(uD*2E_tbpe(v+IK za{5&$198L0J{&6O?<_K}|hq(1et7z6KRUY|^J--SWYEWRgF z@-m+VC&?zwI1|quE)Y5S$h$rFzD5 z-$l^UFFmsAkoUS%F~kCJaugF`4Ek;xlq?YK1CcmuqXrG~!MfNKMekEj@IZOtIen%2 z?Pp=qU4W#Uc8S4AUM+M^Ah0az-ZRi1`wIC1g=c_Cw1X6so;n_vLy?{jg-1{S_Kcx= zpT*iDyz3sRw3|5ui!E-zcOySV)Y!H>K8gWI*_YBxsR0rLX-wcVG9#1gOmYC znV}M72$EpPJaun5J2*Cc_8vEq=SB9K1$)b@0C4vVZ7$g}{6@mY1kRCll@*3HB=vs0 z^sQ`)9K~{1ju~&Mes~%F7jnw`v@3MHqD&W5<>cTqGr|LYx~vGl7~dc{3^(CzVuala z55&x0QbIONVNK?g(3ngF2;RrYhwKcwi8BIqAi!ufjHkb4Sp-zsda%jdbrY9E+E-(* zQ-SY!TP=vK0{S)Xee06x@`}J5$irUEOo>gw8^mCF4V*!>^|nOCl|`lmQACb0nm}2e z`h>(&WZ>hSHgK5lSRE^2In@>v@S{yfo(dCWhmy7)4kTIip1;iK6^*7rXjOeHtE8mt zqL1x^FSNGw3$W`08$f=jHYq3*Rzfn{15iW!FV*)T6mNKzFi%bSmz9-7jT0#fJx5#K z8om1!VcT$QD$ju&jPj4X!QEv1X>MijgMDJV$$i?1LK)zgIB%j&qG>^2+SY@HA2R5k zYWe6tv7|9u3Q532@9so2Q+>Ngchj0od(kx^lZIE(=^;-pnA*pCQi?v;CrE(Z1`C@9 zLjumUUKT@S%?byR(Ev@^o*|itR)ck*WPqxV?LtjCL)N=#Qz z@7z!oi6l$5Mps@KHZF?-0&C^IE1}4M6&Fw^k6~+)s~?m56hYgdo)9=H_bNMxuUon} ztB~&_eag3}E-Y8dT3JdMUYcMPiQZmt0I}_3%ykcr^2O{9A24N5M!4mJw{C8pK;}Rff^*pLw(3qivKyqco%ZIMjkgr0w$>uRE>PFh-FMXHSsLIeS!s< zrtlVop1;6&X5~Nnv6cfZ&#i{B*5%llh|=)!u~=5q014#9-3>5^F!&V|NtpOpw^FOc zGqotI>p>eJG$pAHm{rryo3JPsKwl8RZtpvR;XnB{_ygm~YgqF<46W>I)S{H~7-s1F_* zM9#c&ZObC^jaCJ@YqG`Hu3}n?%;Q^vj`7(5ph+GwUY4w)7CLzj+nk!<0%G4RFl316 zLSDj_4%~|k)Iq@3B}qHOcmdb}qBF}wa`T#mAWJ-CfjICrjDoo4tN0u1hHs-^DY&jl zugoH_A~?zEh7OVENauJh1cw#b05{?>aA_r* zD4RQ5h~y0%ctRF@HU@DgrjJ5*F zWro9?Qeyx&s|h_}t~K#8MIdh6qXPwriv;$0h?&V_KO?>cIW|JCiq?SM_SH`mVfLE` z!>H0_>*ZA@aZE@lCZQUOzG^s6I0+)x(JwgsTK;bFf_4(Zm>^^68wOJQ<2-X>0+E=Z z$e$xf2A*$>qzPY0!W?3S7o=`-h=XZjk9_**C8l+I+QQ#MUL9v7`@nH_Hy-SC1!R<% z4hrxGy~FW^$1Ws*f=HIJug!ryrw^cqur_})ZBA~EHlI!H%mb`AUJmnb7EutKdK6V? z7mSe2>g}Jt?x_eHNpGWe3HIO)Rq$kFcN;;X+*#-JeGLxme-q%o(4cG#_Wjs;GwI-r zD2~p1A;9}-xQy9CxS}&mYsmZre*}|X^aIr#MVXT3$*NXayjtZ;&sp(f1_3DXyb|%;P|$*9|BakZGRI|7Ij$E z78UK4%UR^}lqujXj_|-}i%#q#6QIm_OZy+}4hsZ*&x4lNkU0&Y8-%T)kWHHlWr2#f zEM<<}@)31E^qff?1cbO1mjzqsGX-ZydZd*GKl;sg9Cw#$FbihVOnS#a!Cn@_@rNQ1 zgD;vJ2#3irJitfD`5}}}7PSR|VnsDwy8KBPR#31O;zT*n1H>NR4#4pokO?F_Lg}}O zee}rcggq>LCHJY*kL++6JeP&bg;Eqd3X_cf0H8w!@3X?9_H)8*_MubX$wmYVtw4ps zgZD~|A*A{OmdRYvrb)Sux|1LZtzf)P8%fU$jEuLSN0b`-O%{yZ`aO|MAoe>qd_sa( z+c~KP(_vgrJu2&ogS3SAcW}|gd#gr=%fvEP^9nP}$EP7SvtoR#$~$Vg5Kl1eMi;@g zw61*fvBiJW)z=IGSLQcyYp>R=*L`WmCCw5@(qzH%G(gn)cLePU*#$|4K7#AdIf5Co zoE-?18T{lJl}jQ$M{-JGOKKdITE6ax{6l48o9K-xf75W4OW5qgHaEf@ZQx@b-}%x? zDC2hF9vHNOdTbfRC~CgG?)fQq(My){2h3cvNaO)_3+3bG{0BUgqcA*R9xy9?tTkhh z=LN5UIm*Fp0ppi=LTX9~=X=wuC5r3LrB%3BZ*QEd#+{Jnk+}%1dxVem&!NWZ;=7^8SfYHU7VM3JP4sd+?BbYO>{~{%1j`3DeIq`noTN9B;u`=sO9sYOIRcXtj@}4p ze;`ML1{~QYZw%B)MwNv5kD(_6Gb^TmK8s5_oTpY)q%>}ZZm#6k36$pn4VEgrhI;Xj z7`75rkkTg4!}c*WIIW0zO|oD%=;>_oSr#mY!z zHsK(h4_nQRJvOg}Uc+E2($OCkc@s78QYAnG`Y<>l(j!*7V$8SYA>XaxD$9^>CcFs@)?m(2>LMtE1BKDH?5?>-DDI2dr5~ge(Y(f7A zsZ?cRX7|P3)WSlJ&d(rQzB_IQB-0vY`?vI#02DttCAU(McN|+KX(tDHsAVY=9xjvQJh?wz)`(u8Q^a= z4hPFzS{(mk1M?nSauS}RY*{3s>*t>WTthT(x<+_lluMvr>1;w7 zJ`IQ|GPKO+^4J7-o@QmhLd{|D{Cdwnb3k|*D8U|BM%+$n`Ox&t@=8{!9h)ZQ4NO*w ziRX!xYj|snO{fcTpD=46em=S3rtk9~tN?Tw2GK6;Pb335xJMbVZRr|vW#dYYF&W{r z)=td+IfecV;9e=S4eOKfw^Y9!$-EgAA5BmansrOu z(g4lyVlvPT#UTTEhe!VG`JIZDba`g4P+(m!2x0)`IVQ?M;e?@_fp71XG1Kb}b-%8p zq-Q^uqA)27q|YpqFoVgIu(WU=xbttlWg5^7Iq2(cq4-iN_-rV=HHsoItZ!UlqPPW% z5#AXi^#NZYX%%DDH2q4zvl8blhoFhD^Y;T5iH-&U@OJ}!(JQ*wmvZMaLp_>5@d_Kz zj<%3G(ABl<`Ad)psDjslK9iD|w~yyZD^Dk-7VL@#k12)sgBc`;E0L510!($Vv`oH_ zB{6-d`4}`xscAs4tl4)XouRP4Xx9OV2&9YSn0mD!ugs76qwwCE@ez~vA{E7IBb?M_5g`N4Dbe4=yy;is1Q}xmKfv5lOctry0ysrpP`tB zmy{@#SC-n67ZIU@09rBDIU}OiNlp>?4IrLiCZPmxgx892rn$m^bc!(%!lvOzas94n z${xbd4`D~>WHZc`M!NnKU98KZ0wA4vo|_v&!v$TseJ5U2u4nIPA~U%JG>D!2cn#D0H!gpQ@|QSG0Qe2B8p z4$r=M%VQq4VHQW;tb?U8uRfv+z-9|6_Y$~wvPG{JX@eJ|PS|>NB{!K)7qLB6fhBA-L%75E zE3FheWo@0x;z9i7pXCVP@xl;N=!RH%`p;>EE6}haBqi>5M;g2PA^Q=2Bnv^^j5v)b7AD%4N4QgLN)Rka*Lu)h{>2g(V};oDo@bjoVsM?o z{}6#;1v*Dy!9&qmSF7amQK{XWQ;RGca}4yR_V;YQcGyfm>9yLVyWOXo9U))#M0l(a z7*_6E$rY>Y27J7GU?qJ(_Wj8qXaw!O#wT>~I*5uOBIrb%n&gR#5!S_Ub`?*Udhjwn z+|5lJow9z&L>}t3o1nI?vaW^TPPf(n*1r{ll=_-IpM5yQ)qqM6hFMaOUp$fbwc-@z zS&I$rdMe7`6FQeRKE_7Gc_ytpY|#QOb1Rum*z{h%%H| zGq+kopXKeN3l2Z!jFTRch5B5#FxbZ9e#s))k*8g<$a2be^}HB;0FB56?-3}I*nw6S zNWe95k_J^vcNxVw)`76AO}*yLiPc^?&%1PH5f~63VovWY8-;l27aVW5dIO(q>e98f z!o|(BJbQHz5-siV_BSnB&yr@(&q{7DO1KPb6x5mC_HMKdL0$2=0am=0SZ~7@3P`TW z?to69eob)|ICJ2^4hX@uog(EF1veko%98L>N zPqdIs@<*A7O;xDvLH-ELgKsyjQPnxS3*!I{}$_ zQf6t#4!XJ}vjEToUjGYTaM9B$k@#qK){%?EG$gA8jvkKrE)(Ze7#Fy>$fWEc+%p(d z9KTwmnJ_RBfDtvuIGoLkgpnv?<50o@n@L+TD2&Xjo)BeA`O!cwLoj~ljocI(1nVks zD{HtKiG0O;fr7{Rka3kRWQd*z902Ct_G3}Xg{^g_>9A3BmY;jwfg|o<7OvS3*AV!? zYlJC_1A=bgV;ky)5;#8A=v6l8T#9zMCpqpU7h;{`!<>=y7jU#rgt4NC_;}Nkpr>TR zNK4hX#;fUKtQ31(kdmOsB}+TiIub#A6v1>fPI1t)(UOOIyFQkE8OuERa_&9pw-}T# zc=JuE()88S#?5b9=KYm>P;u>t*H9(JNt$q8~ zAar-Ns+QKa2z^t_r-*yylm;z<9WYB*axiHY9w>Dp)Nk{9!G)ltE^15shRODBw)+eD z(!|H0{PC0_*X=*Adj?1)GmjQU*HL`8DA|wl$}CE{2jg2aNM2#~e54(~wcMgh)b!N0 zMy*TZ6=~to6X~;=)eEeBp^u7kP34c)2304F7cEfq7HMUD|Ml+`Jb7psi$ESKvIZ?8 z^ra-3fC!RM5=w=hY`!DAHve_>nQqc%{0sq$-f|mkM9BT0H~+Uy5&j*(mIY#ctaS&%_&;9pw^i;vv{dC=WgbTT z6T$!8294UW1*@{^73n9S;bJ&4((eE1A0{S3zv*mzcGfsM_`|!=i|rZIB1m1&jakFr~f2pNf>y3xK{5MQ(p476{rxe@3Rm$8r+iKoyZzD z##QWcy_6>S6o`g;MU4#)53`xbD*Tg|E1sL1Tg}>LFh1!@>!)_Plsh2>mPNOq0VcDW z0xnA(Wix8WYUpbMAPk7}jQ!VI5vAWTF%%3LcDY`;?W5EodF%fV$)g_yypI+>4tD0# z|AK!0THXKFp8u{1)&J)yW(0Edgxz!wr?~F&V`kKPzW+o40tM=v_*_=M+@ZS#Y}X_M zTvNWEiH{=+lL(W2Op>_2zBPSP^4zOQy(z{=bUgXsY5v;Q%Je|NTQ$TQZle#CHDlxR za|^FB^_8^^St^%c#9%d@ofZqaop$kall&u#FSfa<1HSnU${XjRV^yXaUlyI3bu$jH zo(sjPK}()rn4urH9;XW}nG!7wTJxbsff{s_2Tz0S+cv+EIwDP&=MFZs=9*sT{ng1` zR;7rcv}Yzimas=t2iNuolYJg=BDje$sWq? zIWbTlEimQ{5k-D{A?Y0*0X$f?x@q2a#q1wm(Pgj3_P~Wg+`mwgIl_B{Y zK3+3wNyoHlXMa3#_~15cMM!#xJAUlvozpXuWuP|@$^mzUTgZq0AUPK9xSKAd&F-P$ zk&6A?eQIiY^Xc%$eRXBQp?xLV#4MNm*%@d;IeESFX(QRoPLApre@^|$pQbQTzgCGx3~){oRut^NdjU9dJD(0gnGsCFB_9=3CE#M& za}ejaBcqqUVa^FfnYu;Q#l|o(tuAWrM^%XFGX6cLZrwT0QkCI@op(VGtT_NU6{PMs zOHnz15_?`@(grYO!o7l)HsVCQvEy^WYK99kko<~2IfM5I!Nj=^h?9zwf0E)}G>W5i z(@?{0t<74f@rh5hdoF$zS4{|API| za``Dd;&|?Y`Pem8s>@#&;E-e5N^$glo_?(Ta8RH_fd7EO=?VFHE1y3_sU)Qad#8ce z$ArPdsv^RQ^znx-IV{dCTpR&1$lkL<<;L?Xl`o1p%~B;6o`sTNiJpfIv7LFHmw-j5 z9>Y<8;_}c{sWi}oRV`didl9#|IaVMqSP=%oOelzH#{%JH$xt2{eD%3 z=`71X|tEF2U&(!1Dd-H%^T5kE^yPJ zx*EaR=;dUHd$7tmjpYajriwx_xQ8%^P}#uM!|Nlr-g^{0IjShR+uAUqQs$Pz0q%Y#UZwfUY_BHxpD zqZU(-&%_)(hhmK{eg%b^8rE`DJvd{Qi~_>Ga4Ig|{F}R%X9j34yQ9$MTELfzNgr=H;(!Y}qB0e+{`p8~ZkTiJL z&5p46Xt7)*uFYn~B=4Wb!8zjh&h%Bi0dsu*e>|L18q2#adJZ6r1T;0-^ME(*X*QJm z5G5iXTRJL}&;t(w?irbpoNWWqxLW?XX2RU9y|PDnJL~pgIo@Alf_pSV2 zcvXZ0@L94<%s9yxl=_rD{Nyt4kpkTZFNg9Gu~vR?>Xtr9oOy)3v>)M%KAZP|9kzB^ zjt23D^4UaN9E%y`*got2Aoa|DrMIC?myxT^6$&+ZIHZW$xuzd^ zU)?If9Gb&yP4(r9f@;E*`wM1HX%zJi z%+sFYqo>3d?x-&%-xOc%)@jT>Czqp_HrXgYZM$LaK72b)S^LnDJCXavQ+qAvStAp9 z?%^U;v$TTGYkccg?wkz8zeH>KdlaDK1G3-Ebmyi{1(kgOLcMQZjFT!#2Z&FG|Nk+*>k`()!Rk$G7m)ewzTpAfJ$ypar+Aj6v(% zRy6dt&=atffq*Tr8k z5H&bI*V*&st#saT-AQr`dM!WmeP4r%%uZG}Pl0CRy0g)GCz`^ZV?HHt>&W6o?CI1r zw;5$L*##{GyDfR@Ns2mEHo{r=B>qx%X5 zaoxSg1)BWzJLto<&k=)9C4XONc9b<=m9MG{GQl`L;&U9j9gZiluAH+CuXC1tm{&g6 z;-WjPlBn>fSiOi}3*2HP^u5q*;n_TSf)zuRnNn?-t&WTop4#S)g^s@HnxbpAb$M;j zFyUbQ^}Qu3G9|wmp2YeuqLelxkdX}iA+Q}-Sbyg$F#xGE(bbKQX*$GQq+jxOLw<1Z zxICrk$xl(~9q0T*v3F>_T&MpPMz_kym14>jbC3XbL9TPhmXn<72_ED)bfACdA0Y<2 zv;>!YO!Bwbs9nm8I5)78ed3pD~?8M3s{$cy+7}_vXuaE?M~PLrn#X zqpTxOwYZjC&sT@p=r%}jV{Vqvukp8LDkU%X-- z@YF&F6nAm-P)I=U?uV;bzEXKTqK?1e$K7QHIZ4CYP%;yezq|k-2Ik~sJ*X(!k7id4 zpcaw$&v)=Y#Aq--m(q?Rh>_K;xC<5ElC1L@*VNZf`x~*ph`VRHVPDQ2erP{Rarwi! z(Nzh(cC^f;x%=K0glr3|aO)?WWl8D3@J_Q8CbMVLtXg;nXE8p-o!Nf_o;=3Vwx(Cp z)@!$w)zt;Dv4qW`dXz;M{ubs*XnKeG&!0c-&}y^haQ3rt;sFfjm8?s;e?iVYmW)ip5{7KRw_+ z-WjFNBzRZ)tJ8lLXH)>5@CBtx*uKSld1wN5Nmfq6{lRRWj8SjeIR!b<Ao|4y>>$&j}FDf7Sn#7c5- z*{)TY;N5jsau&~w|7Dd!+U-}3Oq1V=*!w(O1b?WgR@akMs8z+1Hwop$D&$&9h0x0- ze)Msu63d0Ze*Mxg^;Xfjbp{>(DA8)lgZ$`O&Ux`qc=7@Gv6*k5l~0nryE79EhIpBK zlQ9Z9S5`$mfw?MwU+In)yoeX~w0Lqi;Dz5;|Q}BK^jfwjUn@6KQ z!`}17SkR}Mg&|ygBKdE?%U34yF6ysaq^;KJ-*d#Ebb8+*W-D+s7_L4>8f7Rg{KDzjye|KWi)SK&tF{TK#Sv zyEr;K6T#`~kTQ+th>Solx3#N$n5kmao%tIRP~_d30uM9Z!~S}M(f2LF)0O=tc4zOL zO?{8YHog~RxbC*M@O)TqAOS-8OjJUXx0|f43fKJHD=-$0QNNL1fd&I?$@?$3eg@cO zq$~-Xl4M0->P~G_b%WO)@jYCEF1;SfX;?B@Rx%dWALF5UE@5jH#~{AD70He)j?zs# z1&ErSwNb!~b!FHLew=wPJpWI!hU&d!G(G`?F5`RN$dUyY9CC?WG_&Nut(UXM@1&h$V4^tf6{b?HPwVRz>(p7n@>}Yd(E(l9MzZ9J= z4qXeNpZXR0{bQwRUz}@U#}M6;$wLDgnCPPDt%2Oo6Uw^R3>1*l@7fEM8grn!Cnw_d z+{Y5TTM^$AOV&>FPtB4cDuXH5OP}^sxb;V=$KoQItFN42yp!+yYy0vzn7k+a&){s8 zZX3gH(~i~o=!4qo%s0`D7!CA+YAQQBJKuA2+bwq~xJ|a@i>l|m*cV;oz{k|xgPU0b z&oW+dQ{=*PUwgh&rtS3_9v;ZxFLN~dB&yY`c~0?t8VXxD1IY^;^rbUWTY>3}AD~Hc z0UEAno2Gjy>K9wog_3BZonj)ZhPtDpV-wY=>G{h;9+qIK>?z@?@B4)}qb}o=s*A`?!baH|Jed(SCif5JlFE?I7j`T1lZAFZWpIi#xEy$9VLw?FYhQiBj_^% zhvg`YlT5re^4-?6!@g>3h>vaQ2ng~=?(@ppOrRZ)ZiaUwd%tM|ql+RO3&htFnt0BwYj-9zQB8N>|->x25ie)zdJ`6&x&s>Qlu0dIvaiRn9X6np4Cdc zAh&Iyg}J4*5_7!#v_txloD{_G8C%j^&FENja>nmIEKCRSetTSko+Jqr=$*5q6;Tf` zbZocLfORzqG?aW8O(Dx9l(<6uK=-2>O*~T*db8M+PE;d2n99rav-#%2>8ytxH~TS= z?6M>JJZ;~2Yr*)ho_$Srh(7`;&k2bRJ`J@h+A#3lPa6K^?d^AblFgXG4{>YP{#~%0 zgEaBK&K`I=*ra+LeR7YY;t_*CtzGgGTU0`1{A|iP!K80lW1BCE+NaZ3ZC_cj;pI{O z=xEl(rvmIa7YU2y`)B0)!TwfrADC5!(LA9Pa?Y1Yf-`U0E6{Y%OOeM(3efVL8#GOg zb%nifeWe8L#h?snqVBJCs}v>bPn&zQua|w^wMHHd8yefCMWEUWpM!NHW>Ih4m3U|{`GT6t%V9I-%e5(&h`2&RHMa*3G9UP6a8*kKHv*fuu z(3giiAOczYxh>`#wNcM*NZd|GS4bE>hi!%W@@)Rzt^6h9O1R%)pp%LH_)(?r<=h=a|)Mwrm)X*(bcR7xM_x% zG1`YAH32>NefJ3#)C6n>XoNUu-?U)dQ3oF>7ps09s9nqWyj!zO_|DTL%`);l7~c+E z*HbaQoEo3e3@Kv(5{|*MD;3Tm*Dp(gB1Qz3}RLhgv;SN19sNbiu=A!}?C=Y(Bu<+~j zP6~yf?Qpd4iQs!s!27+0gf*?o{m3;8?)>w{>1hqPG_whw00EWz~2?!Geau86wb~CLMdx)rAPV`JI>Bw3I1R zj%!_V@7(fkOFi2(nncBE>-Rc}=AII~NW0feG50&+#?{c<>mYyi`{b0lUqH4?oa6NH zxJG*V$yAlV=YkhXx}Cu-sWJgBxg;3QPstyHeUG$K_K=-t9VJF)91AH#4!9k?k%xs7 zLoL!J(vEn*&)N3{NiO&tqNeP^)RhelVK|PXH8tk~w5yhw zO1tsWQmRCb%8@;wql3vySZbPVTSsO{4cMBY(k#`VWfQ?6R-XK&7S+j{zWYX3Q%F( zE-TzS>dBF}Y&E+?6N}C|wvI+LG)4+E20xh{geW}P?N^FvFqVQHK|8@~SDhx@-o48xui@lVH&G}oWA^Bc&D1+@ zv9p?qBy-zC9gW*Ig#zJ^6$Dk=jH!pRHlGe*HeTmnijM{hG}P*y=9Zkn4{Ah6_}Dyn zeP*rYyXI3{9U?iO-!7=|&4hRD5&|t!=H@bP4u_MPZqE}|DTThN7nT!$!wQTaaerZ) zfByp4q+Ef{x5D}MvxI+>^Px$;SqK1*;kJe*!HtSL$tVjr%B7Hs*dM03`UMbwT%!vY#mJIOjJT|2$?@pw7q8uW_p zY(va@J;E-~2A`-5jVmV~WT_(Zk~tv~`?Pe8SDow7b98@ru6nM+|JHlID4AJGQ*%tnA;)8BOoU06pO+!*@)a4J0Ll>#afg(*Vyffti06gN$9Xgl`^z1v zPzZi|*?e0+g5M`hpHh;(+ju&+k(ha9g{~%6`iLFhv6n+HGjFffb|WIUJF-lnMLysG z`*UdmCF}51>n=H(8q2~?-(wvr`pmWJ8M2oAFX^76g()c=3UI6ofeXqyCKE`olQ!yO zDi(&}EUa_eQolG;D^xEyAR}fstXP4{i?)uf0=b54~P#*o6vr&MV9S}=d-aO+~!2ups-+;%xF*x2u?OL3~rV6&K! zp>&EE&tU!=&QL=2v{QXWD-QLU76>V4l5?9w;f$uiL)90wF41K2I$T|v40VwMH0%7- z;Z!R3>}BG5DvWNfWucQksP%{KiyxtUIGi}_Rr$s?YkG|qm;01vLRFk-Vw$Akl&dce zEj2$U!D4e>F6*q#H11yM&)1+_#I!~3AGf0n$K6`&Mpnl`QNu|dJl=RC>q0J~W@NJ9 zOB)~2)q8Ij1>j&GbI+HfbBR@@zB_=zKh_5LIlFu%Qjxe)udm9FafpNT0H!eu)TlDQYP@!}UfCG088IpBW;#Z3w)mgDPB*NxXg!Hxsg# zG>ADU@=nFe#UgWz7GNR=&b?NQyOi&elAv3g&MFCRd4~I|nPO$gpNL%UT#;Ip#yvM? zyJ=K?CT^kzpoJrM#O7|PK?w!fF>lq3*_<+m(XO?EJY?VXo!5G;b+JT=qm7WS+dRJA zTQT+bCx1TQyt@wi;?WWIQ~4!qGr#xym z8n=Lr5N~SVBSY%1Pea!SV~E^Ee_EcTt;4I=cKT)%R0kzsYbIZ6XaZ9UWx+UKpOAjo zaZOzpJ)s#f{vUOH1yq~Qwr`Q*1q#JVAV>=ow<19c#R`Q|+}%oWx6l-a02N$|Q(S|) zyA=)Y5{kRM^ndPq=ic?s&H7eWRx+>i{ z7*7Bc&5RVkKIL^#GmM@U-J2W#+22}BzqT*7@JpvoEWLJN#80z&Ck4{w)(J&yJj?|#0uR!Ij3z+-)`CpUI_-2Y5vS$58iVu3X+ z=Jv0n1vwO$f%87_XCWC{)Z5gum4Ya4|28DO1?SVeNAq<4Q^7w?;9O}*04{6W+f%9G zeBaVyA67b7WEae*X<-kqY=ih4_*@&Mr*DRono0VcEs*V?BSMHYTbketF`qXey_@8g z$cAyaWb?gYd?~W=RI`RECNB5Mv`t0Fdh)`|S;{MdE&aNgmhRd<5a|%z^bcCWwV4Bg zgS??-kAM~7Ntkw3fFSv46kpbozl2x#Nl*qs*1QK&G$>@~YbZLm%dRIM@%iQXz!?(o zS!;1exrgc=1zUB4^=m#s=8+hgT@7B25O=F&*PCd9S}>x+XBnkwDrp(GXOzJ zg@}dWM^81AnRdm`V%I2VRs~Tb0Ix5j?YC8#C3luSfpzREH~cN+P+dho`{_k>r)u1J zUDVAt606$NVYv##Cr<9&b!jm_=jShZ{SJ@9+_)^OREhR=cV#3pQ){^>%m|$}KY{M06-==yy_IYkC zEYF?=5dFX-P`>4EkH<$JS!*&JN1a)LEUwp5(a+ za?5zMY^A&8j!G0hF&69_F8C6!wOA1?O?CH)JbU)eDPl-X$l;R& z!nt<%G**P*@WoAOVO(wAv?@HbdDeSvVhQMA2IqKY_F@p*-AUBs1QR`?`)`PjfEKMW z>oCd>RgmwChT#=;6xPZaNaVLI-F!yPNEXcOY6iil;8db8@-5$+-R|CASh^bam{+6V znpg4LRSSE(`VPty;5}+CO2uOQA%Kg(7-n}C=zSLCJy(!7Tt;2MP1|I;_(6@yz3zx* z2^ULPlQ5@A^6;NCnf4s5SX-%&B}XdXdG}3;QN?rZ&3s1tKv>sPK!9g)3Su= zr&I<_lN2&aw15?oY-y!|)g99k;NX{yL56~i6G4JCd3%5M9542`XbtXOtrc+^c=+eh zoX&t71rj}`JVm$)CpGQkva!KkVHf}^Vz#E_!!YK_bO)VXeq|BY%mz-gM-5|#R|9s6 z6LpqfO^xoZEpbdW?85wPHYPnZv(GFA^45mFR&^lB`M$o+<5Nyt^||CK4*&?y9|vME z*T<6@@vj`pYgQj}oB7aswvzS_N+~w%mgMYc`v?Hk`zn2hMT;R8CF96F|4z-V^D+%? ztYrymiHpSPskBWo_i-rcIF`V6vsE{n=UpKc%@rPj9_!y#H~O>6+r;IVd)iPo+%F{B zBcdltb51=pcWQODjAozfCkVaPZJDMGJPhW{f4uiC#KUm*b-!5jIXLs}|3179MmeQV z-*}%RXT`t5;hO}TWok>>I%kX=%J-|yaYE181Z~{R#M3@qb(83&Xx2JT1cVdA|U@G zq5y??K~{HJm&We958Z7IZr#aYHwCMcFroy~FM=(vZht(mk{+1Um5s?#=Y#+uGg^JK zB;SdA>BH@yVBp|He|%p_8-Z31iII+X0N3aqx3B1pZVeX}d0*DvafhN6He&7^MkRL1 za3KpP4yzHXr_oy*wHQmis^Ckz+Dcx*GdQl4Q+6iubwx5G_kM59nr@Zuv%hQ>Z|cY$ zD$&Z^${G=Os9)NO-WBr*uTR0aq0l>R>KCnJiEe%nlv5U<6e_W{THH9 z_R*E0g8TFreEfP}b&Qdj)s-D~bz@g4AYrsF>>XtR%3;a%Fb=e9!*?@V$=puVSjRr# zAhhqXrMO19jGCys`p>ZE*ai3&=TVp;He0D4=VqW~o1!2SL3Jm>ajqe2=+pdKJSa9H zc5&tS$5(V!D*Rcv0UgSlcKm+M2ag^%ZS5EOQic+5s@XmAbfE8!A#-;$*PH7hl+Od7 z1i!a;^y+Pn3)y2o6)>2FNohV1Qj7knX^StmZ2&LM_q-MyA-d@_;wH*N)y`Iw+Ns^N zkNsHSi?|2ZL1}U=1CWVZTotjIQKw!vpMV(DH+wwpGCvK5u|g}8<^AXN{jbL9Ifo`- zT3B#tJB=OWK>Q`SB-UPaEa^T1Z@G@Xp&wmFxXaN*$= z<6{LEt0**oK9?cL!1;lZLvZeUt4I~>=PE2@7>~VshwN8RkHx1k~XWW(r3~R^jG2M_xP^q}xZ)bSn21&aj4vfmmitEbWn3(#Dd>!sW-&Ij#S@un_Nf?(-UI4Eh4}=^$nh2Z2&pU%52LjFPnl!mzD(DoK z4=5Sw42`Xf#{S4@0PMQ?1pho)-XSOjUztU`IQdGwKl42ROz&~r$+U%!6rzahSjbC4 z`*v)}H;At7lHGF$H}*R>qVNGKi%-6a2!LGUmo4;9(r0sW?HF}Wok51@(xJQ*kCC+3H zXP!{BT*^!z9CfqyNPo*4bCy5`vl6bRSs?a>P_=sbN#OLMuE#Zld?SZBi;I@7?PsxQ z!Jk#F3=mU20u5x%bLH&@rmqIvB^rx~?;GsLk^}^@p6#@N%yNl533#iq8beVbBm{Qx z5R4XqoyAqF#+LvYB(Nv=dduDKI{{}EKVN;FL{{n8}T zlM;%7t!xiAlr2Ix2{fCutuOvrrqZNu+_kI0C-o-TEeKv8kJQ)(KS#3K0(gurt5WAS4U^XbC4N~MS zd<%a37gWtJh@~<2e98ae^e>bG5q|Sln$PWk=BV+{Ap1_v4@HT{X5eBt=|w(Yf{<-g z$!3sVoa%$jDIZIxABU6lArAvn1|Q6}jm^^oD*EKFaDVDjo=@-kNS}rcwT*-%i(M# z@>XeHmUAfYhM)5>%FWwg0vu6;2bI~?n@V!lQNrV~ypYpmibR|p)@J&vLgCu^1Yh_g z?gy4ww&*ef?7_6RH8WT5jU?J&TDcw-GSOVCPT)?%+z67xa5;yL0qoK%`)5-hTemw( zf@_v)PO`i``aKD~0kjf;TIGiPxx>myS&lFch^IZ-Js`lGye6?@RN$OU5B?TLUfr76Crur6inC5XL& zxIHWU{OjF_#6u%>YWTi1w%GIf!vrE26sc>h1m!#h`rjU%HXZnJ`JoVt-l0$!V|_Gb zXheTmZ~cW`psqfgSxvi++3<{NG-2C~obp5&o}R=jpuuT=AwgCk_w(|H#%a&0f@Q1G z6@t8_#6q5u3QtuvT9Zf*k^mg~Pp0fw>FFtPC1rNz(}=kG`kTr;5Y_SnsU3*9O!HGLgm1#-6LTngqd~Sj#+Q0kr(epq*@26x8AMBL%A6Zev=f z+j6T5<+^5nZcpNh+TYVZ4c;T&`0|}^rIH`^e(s1)>XiP@DKa=PPJJ-1jR~Eah<_TQ zf*Y}|m^wcVX}YmP=-ItI;#0^s=4Sh$ zVH|PJ&gq9V0f%zuEip0sE!`#?H!oYs9SlK94(F1SJWRDF~BW>&bS<#vzc zL4Ir)?h>#D5-N35`r%udIh_S2}6 z=4+7AsSRWUeGLzBVIAQ@++h~(Qn4IhCNkP(6JkKTmEsbWPBW0uaT(wgejO`X-(M}P zJC2u|F*zboh=nubX7`!pIm`7L)*A86qVV#OE@H8!2W}TuYUY?m;ds!5! zXY(7q(ES;9Vfq$+z;9Ya^_l&FUSIgwp23ck6BYO~mQl(pEUk5yQ>P_bhoGx@E+f%C zY)9!=tdtoiGOiq$Q=*t<_fv;*{j5)ZbfQ2fe)KFcVZSBOR>fn~nCBZc9W;H6qqUhq zSX9Z^)MvUUT}*00;X8GfAAbZR*v0Q-wa8TFa3VktNQ$viI`{v!LL&6!wRn!zQ~k*= zip#1R`qp{#4bMdhG*YT{yyaG#3ubl?!8kF|pckmvg)|v9Q=9a>ATQyf;sQWFy*0lR z;2LoLp$pp4ugRG6kU-`N7fpBWfAvSWt4=+|>Py}$QYzSOSM0gQtIe2n4L<6mc4DZU zxNO1Mpjlqa=P_#$zQ&)_i^hmCc!e3oD4)&;OCBn&8=P%3u!L<*BpQb>QD&ICXeVJZ z!URcu$|=spVF&n3<-8~ZKtjHyhCQa9o=2D%Rs3=FB2r25ml4;)KJf9b^A8L&Ab2`D(qb)3PiS;u ztTXtEX@+Cb)U#MMH?EZ)DVOB5PZ7^?z9uTP8F*qfb|7JjzvZ>Cc>iMl(Phzo_GS4H zu3-ho*K~e>ay7>KktrL79Y;}q5h|5L7*hxIg)iHGFqO@{Vhmr$obQ{^ydAi8UL zP%ElqxpBGdGi;dj(oX&Q`L~dMXRUJ^;#}hLpP)l3{TT7ouPoAsa@SIW&Y` zRt-Q}El*3QP!NJRTKw8tV!mla6;O8%+Z;gIKa=>h_O&U+opB<8SBd9mAk6^cDHf3; zux}5c*?O5Q`o`5bHugLFN~a%0QrI)^u&`ceGT$#WUmn5g5Q`?*)D*c}sFMtFk{(O=?O z>~io8H<_dvBEXFmJo?p6L0A%hvu_E|0v|~fjcQ&NdvP8-~M$3l3S(H=HeeZO5+l>-T(e8>#T>cd|>VP69UE~Bo>m~ zf3c2&E=%KG^o{qbHNq>~#VxS4&8vtkorII>V5+%8zqODF&bl01tqQJmG_up9JkXKp z`5K*pf)W9g4afa0w;tIrZK*0k9n_m9+}Z1RNlR~{HxXUKO~g*_ec)+Y=U|6$a(9{Q zjH`n15BNzC%M8&38k~0YLq|{vL%9zqyam;$b)AvZ61=!dzM! zuOF#FOco2nq5W_L4N*#REYbaz&4XvkNd9V)0m&fEdDUYx_YaNMh=-C&F7uJ3F(eS$ z|6}>JiB;D_lev@k%;^IcjmRwg`8i~z?8Ws-mm6w9lE?gd7D=#OO9|87j+CVK6kU09 z){f8hwj%nh%g*n&g=+kXG49<_$gm#@DEMaea3kI*?QaT|0|gN)JaMhh2L-Bk9gi6R zB#)K29*e$~`+nHJ8x85+W@*@Fa|(fa!9)zsZ>i|IkAyR%BroF;Ut~dB5+<-VoJosiC~lb1Ul-<*_*M=d@Nv87F)RV3|PawySlvKSMk-k`!7N@Z2V24C;_f z7bE-0FC)kHRyUgnu(9vpd@vP34ZQTU{CP*KcLddd(C^fy2g=E7YV~E(19}2*5%@@9 zZZ%`$XXkmBQNr}hnLz%&J%W7AmVV60bm_q%q|5B^Z&=JV+)sgM+K$_Q5-xL(N;hh@ zI7(Np3ge8W`b3nS)C1l1~4y#?z|xR{M>v zRjM0_5#02V`J>!hR4CUfJ#;8d450<3q`d0NuGUAQ*Yod;sLVZO5o5YIA-#ZF#}0S^ zV478_ZD`^+#qJ4$P|Chru1 ziACIAGp-ZwCs2Uln~S-(24~d07bnD_Hypy-Nj4qAnw0m8WC!U}n&XDFlFU6Kx zU#I#*@|%kEoie#U7Qhi68V+*bR5($MW8eZzH`H2*0Z1d4}?^SE#ij3_ESu(z;Rm9wtjA?(HR-Le9nr!hj<-(=+ zioao_kb&`x37t8F1i_vWQZvm4=N^eCzA77~Kq?ie6%m1*T&)E5fMQg5_n-Eo`|+VG zZ|SH#p0{FFts+@YS2CX#cp8C8%t)+S3{kn2HYh)5iO-e?%d-KOkyY$q=KedBJ*{xi zbdg3~C?xb3EZHrZZToxlx82LNXcZJje9?3kBqU_2;gEeo*u8kr80r#;N(ZqxP`RkE~h*)7}zf zd%iBuqcI~dsfteA%K#kNSA;sEJt(-a7`gq0o1kyYzw}`yo0=j%;aoof;Ixm(A6!;}s}anH z7cr@0XPI@6Iy`u)5>0G~*m`KMC6e2riMSVbSYn4O#JyrQ`*#KM<@R+xhi$bNGl~W~ z(dSgg!(20Mb6r2xRalzP4apn|CXjUf*hBk?TxpVidr=O zl{&+;Ow<|Zf2f~ob&lkL&Q1W0nl$lq)Zlb$F0}$A&DKcU1Twr?c9GSIr^ONI`=&fp zXZ^`Dxn$Wd@oZ}^?+qDS3KE)I6@Rsi!7s+BLt~zs-n%XrKjWRtIsF@CN{VbN)oRuV zQfnl7(i!0%0ad`$yF)>%lP{gOof8hTnLKVTc%-mh@(xsJShB!g*^duEGxV!rGq3^y zd%({yyaj31MdDrMT^AYM*XFa=Wl7yx0gW!n>DABOJmv?*N9+wH1w}_quI+q4y~BDI zdbC#EhE7@`EnA2gQ#%EiY&B#}wDXV~K}h9)oMW+rF2e6ebOLGj3^u`Sdbqa&O*p*Y zl5hvYduHP=1{2(x&WHlYP96^j4Pldi)-m?mo!q#kn$xXT^hB0zM4hBTuH$9y zd9s?|uS5qRX&TL9ErjHagb#0}mcB2y1ng`BvLO*ez{|O-sPos7uB$jFgOWX7`ko(; zie^4}rtkD$nZ4GQU+UcCr$jyrY_*pD`Lv>B%r7|a>q+N)*2ytr*$g$}wpQ-E5=Ti` zw1k_5$Ngi;+b5nkvJ} z*XwsZ-BcR#dQQeBXsIJfacB`3&+%Wv%!zvm$;r71N#YKz!O+WLx(xZYH@sh;L?A8A zR>+c-FmZv@;@wi8aiw%5D|*|XN`2dS!b8VkHcNZUv&6SBbd_44ckS*rxmPe<+OXtv z*Rp$IKY1s#HE+~Z;8WiLP zvGaaAK>7_EY{jYnNcjy}f=syZ`HwBzP9%Kth0HwC1n`9{4a)_OQ0UZDDJt)kSZWnHgCzh{U=D z$Tm<&bXphjtcMgbI0dqgC@~W9?=-T#O&U__(76a$5;01tii(H!#@{zzd5jwue1B`) zk9!ETWad;h3@CABkB|CJczU`sv1rna$}Db=vR#{T6;#@|#DbnT%Jne#hFZD;Gmt~U z#H{>={Nn2Upo8yCt_W;33`*RZ<=^rt`i>{=7C(9AG}ga<*XG(V&WY+VWAAGX=P}Q@0e^YD$R1pVHW5H(kP{|U^NXrtDNaWC$ zbe~N+n*8W9s-;bVslCmcsw5o)aVFU(*d|$^QuHj+o{KAFnS=k9`;n7gLTV5DiH-^T z^?lL;ouwwF1OCVoC9SuU<6UO4&e@d}(N%e%Kd>(T;e(nyxSkXbgY{k<99yq{_&nLp) zSmObRE^EmiJt7}cc>6|O9U!43yqP_OWD|FlnpdUc776Vl@?yI82Y+HO4Z0IgzLJ)c zGp^@LWsTk#(^*b1ZR>ofK5lHnpaF^r>}@1cy{)5^2t+zsmRmD8l$p7csio+g0>lP~ zGatK-D=FN!el;C^tjID_W!~U+v99{H%M?{U&C>*4bS05Z4j{Yna9zK5glYGm3gp8{%PY$k*Iz8d zcj``STEk>)5pue6W1@^Bj6Om++u&_&VgJf)27Iv!>lcLixBA1WfL)%w@U8V4N4Eyj?TlRrNC4g%VVoVdqIbH&+M z@+L=>;cnMS5D=BBQFRznEl&$jMx1&plNi70 zR3(=JYu)q(&!iXB1|4wQ!9Z8#!v6QZPP@NfJ&b)D5&nAea!cMaX_JucSSLOFgs(;A zkE4q+lEhM?@#CQX&?5QejlXsgWOH_PGtWOmyWE9)D-)-PrdKb^^VWN&H6ffD0-Gi< zFl!a=U^E|84{z0uJ_4+%+8?jh7ToOiu62pSPAIi1?s#0v05ysCMJi-#lKn@$w8Jd+ zFShuCDAO~0K3r1@?EXv_JT>5%g(-Ta5Y-7)Q?fNXn1Q>eVU;$F@Ji18%puCP>(4x3 z+Ci0#HTmuy09*I|+4%=r5&MKDm^XKpd|L$>$xvH3r}~()yvo9DW4-!c`8-!EXk= z+XdUMc)RudEUII-03%lkatEG`H{axFotS^4um-nnNoB>-_)(_-QTX}}z9)#A^F)<+ z+`cbM=5Qtqsb3iE&5W*d-TBz|-mH5?sL^x&0OcoHWOnmXvzwV_?&gv64^bna@mi`$ zOcBbhO1##?<=&Ypw3ks-Gyvg(pyJRU7h_4$=*&-tUn&n1Az(mIlJH*kpw^FztDNVG zYi}WrCh1E&UY)cXX8Oc@KD%(bWEC;~9JTOaCtT`%rop?-+jep+v_ab4=i4dgT=u_p zN~23aM4*li>?yX6r=2u;7&pa30yE313g{kA$07py)T1TQ31-C{%jP0r3}}ak1N%fw z6lozx%*OPbR|h~>RJnIfKfTIkQ)#Gs$87*)J$1J_ylA4Aa8gy=lS7Bj@CfbZVHHz0 zw02(aA~GPnCWv@ja(i1jMQDYFh~`6+OE!Q)Unr|qXw7X;sDuUa+k~DEG+R<0eLr-H z0;z|$zKj#m`UuUhjIAAiEU5fjgmO7@|EZ~ej8iY6Y&p?|o9!2i8|}{0SG$-O(3`^f z>{r;@owrZhsWd06&%bY4^fMGMWq25%QKSZz4zTRM#5H(oDzYjnG0r4bQd2z_9q>T_ z^j$1UK~}%gE*!PkN%asy?Gt9HLVZfBWceCI|gY%?u+f09`NS2^t+Sl)xFIGkbU$E zUTWCu+Ql-rPVt*T6XAmH!So23oCYN_tS#U;p^`-C zr_4@S*>cs&GEUErW{fZaazdG-6oSq=HDt`>P$Bs2Qn-OARxkB~c5^_yyV4%wZu$4K z+;-K;-`qLx$OA;bM{=JRe+1}0y%kvjz9}Eo){aO?kI4yRa%~~q_TX|-~ zZ%0X;I3xCH@F9Q7Wm*3|`ez&^09uNiuR8;`K>zf&1>DrNOzEfsCt;F`j?zkemr=HpRnYt-C!q6L`wb>)*1koZ3?<~D=e49ndr~r~q5RKxl9=a@ z(1Z)lTit1w<_Sb$@OC!*f@Xs=e14S1Dh|(Ne=}V0J)jwNx3|V5IG0K@{FR?bkg<`dCJfx0 znB9GCyqoz<-J-$;Oq(D2K0GBxdn%84tK2C3YqeOep-wiIF#K;fp^_fxlWP#3*Ki+= z*k@>en^`jvN3l-1ewwo{srLGm+z3A%dRY={{bTH<-}`qh#rrV}Bj1M<2UM$t8Dmk+ ziJ?4<##*4l^QjiH#apY~mHhrx4M>f6V#)}-ei^cxqWbC6)wh#8xRM{;Yr5sJgkp|!Uqe2jfKeomTY?*a6jZqfy%n6PHSKMFS0r zN`#!`^t$I1n&#A#hk5&RKFC#BdU8D)A4(2Ls5W|kyW8>}RiUJDCz3<#oBJy6^;}jg{OG|M? z`|bv^hkqsCF$@A_=QPSkfHKUaQ`NN=zD8blmC>pHe0=~Mj|?K*A@i^%MG4U)!~StL0FV=#ASc z6C;val=yHwWcp{QCVVpJ>KNU&i__Z^;5Z9VzCn^D>0>&X2-qU79zU-vua(bph$e)1 z)hVq>g-2+tJ3i?WPsc&Qmm1U!j&5zJiH(oSxW!vh^D&8-2Ln#)iquE!_-M8n7S8hO zhgmTrkt``G%qJ^DT=a z;+G(X;M+{EOiO-rxyy=pQi90 zlf!Zgy&Z7X10_4n7nlVUSG{~Zj$0y8{mst7Dp6zC3UT@qAy`hOqaabwFmkfbMp>ue zoAZc5LcC7&yAnARdD$e?lWq3$#wY_eAYYD1LE?(XLMHb0S!~hn*6EWgz&}PFf?Tp2 zEY#O0YYMvAYD@bS{8G1^3R%iC8(uLA?%#nulN+k7 zUtW@0+9wC>7S}|Ww$ZB8-ALMaB<48$orc8FG)&9=d6=2S>~)Tx`!JA#S#iV?>Ftza z#`Y^ca7PFer7*0p^kV&Oye)4$35t#d3aJi4j@M+497@T$sF&WS@=_=`smg&Vto234 zb~+#HyP?IEB#iFZXnu_t#DO@;Sl~aCDZ#Hy?LCR8&W?6oVWAU4r=hv=ErTXR7=+XD zAv05qF25<~28{;XdFkrke&0O)KEJIl4M*sOAHPqk+%>XbaLXdBAB{FCiD!tZ{tOwH zijRM9^Gz#GpN+yo3=V&(%8BusldBJBmOcRVXIT**XfWKn8#n|hTCudjyzFsu4tc{; z-8lO6oS0NT+LSXSHG6umEO}i`+_iF_;iFD`GG-kPuQYd%+E4wefZN^TSa{?bqm%M-s4s%VL|z{?;E22;@zN~*9R`{Bp&r}f#HGHe<@WtHW+Q|X8MQr!BWup&o0J@;Q$-FwkIb9!3)u40C>s6b9rei*%?w)Hs42`c0Dk=-y7 z|6>d5%b{?edR%86-^mFzUE9>x7*lg3Hhi2*T$Bp!Ow(qF+}z(mPi;Ps6o|4J$g7zc z&=jaLE2?DnN2rWvNwHF2QSXjUWlI#ylwhk^u}w(R?gZ*!|bekre|3dRN>__uVaiWXF?@e@b+x)XS=eQlT<*Qx3jJhBHXK(hE*n3vU9m8I&$1-r zZpgHI7P-f}+ySnl7PMEtb-av}{+jg(5cLK8&ormS^wc-^GVw1z7P~ml&&mAP-OZ$1 z?LkEg=9JWh=Ar-SPnc)`PTvI8AeY^_YTGUpn33Lct**BA=wLyV+_yw^L5IcDk#CH0 zDHfv2xJwHJ&g%P2emmrU7ZMYkQ77#lq~LAHuTs#zZvS8J+!>hdsxpIFpA;1p|I~V; z!c5^Fi(WlxvNf|ac*!KKuet~dvgD$#vE`OJMG7uex3SB1%C-Jc6aKYf{resIl%y5- z)b``iPk2|CjQ_ktVf1a}=wLZH8KHz_?#dxG1vA-baFVJ_fHlDg-!Y@H(Z6)N zf29AflJ|cvc(UXrUAqpMc=)_Rtb;?1*pC+yGbVh9E9Ml9hJ~~YfCgoB4~OP-kIi2b z`9EDEZ|DG=m(1<2$c~h^)3IpUxIHw_>?j5g=Iho7OC15{v8r4WFopyWLY%NWnmpS z>-f>21+qZxvflgc+faTI&&c$sgHTVI?R3di$S^;|@kg{i$G?Z`e-dcT(E8%#6sM84 zHVYvQNL|cgS{<&EB=M_;Oin{6cXi(%`A={EDDr>1{co3&AX;2d_(*EtG>01DO8|CvCh6cmA^kVh8H|Ct3!_$ad`cSF8_=6@k@ z*DmPzUGkoxc{F=9E!_~A}|mriHe*4#~vo6P#m@q{{AWCe<4r^ b`hZ;$PZ5QcGLilW^-_>gep@1C{N?`uD)C+% literal 0 HcmV?d00001 From 44a2c2f8f75dad84c792e15050a50c3aa8048875 Mon Sep 17 00:00:00 2001 From: Henry Harding Date: Mon, 20 Apr 2020 12:10:15 -0400 Subject: [PATCH 12/34] Ingest overview page (#63214) * use fixed table layout * added markup structure / css for overview page * add flyout and links to other pages * add "alpha" messaging at bottom of all pages * attrs instead of defaultProps on styled components * use FormattedMessage on alpha messaging component * remove leftover code * remove unused import --- .../components/alpha_messaging.tsx | 37 ++++ .../ingest_manager/components/index.ts | 1 + .../ingest_manager/layouts/default.tsx | 4 + .../ingest_manager/layouts/with_header.tsx | 2 + .../ingest_manager/layouts/without_header.tsx | 2 + .../sections/overview/index.tsx | 204 +++++++++++++++++- 6 files changed, 246 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_messaging.tsx 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 0000000000000..0f3ddee29fa44 --- /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/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts index 5551bff2c8bde..bdc8f350f7108 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/index.ts @@ -6,3 +6,4 @@ export { Loading } from './loading'; export { Error } from './error'; export { Header, HeaderProps } from './header'; +export { AlphaMessaging } from './alpha_messaging'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx index 26f2c85a291a3..f797c509bfca0 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx @@ -8,6 +8,7 @@ import styled from 'styled-components'; import { EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { Section } from '../sections'; +import { AlphaMessaging } from '../components'; import { useLink, useConfig } from '../hooks'; import { EPM_PATH, FLEET_PATH, AGENT_CONFIG_PATH } from '../constants'; @@ -19,6 +20,8 @@ interface Props { const Container = styled.div` min-height: calc(100vh - ${props => props.theme.eui.euiHeaderChildSize}); background: ${props => props.theme.eui.euiColorEmptyShade}; + display: flex; + flex-direction: column; `; const Nav = styled.nav` @@ -80,6 +83,7 @@ export const DefaultLayout: React.FunctionComponent = ({ 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 c77a50d95dca3..bb867718204b2 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 @@ -10,6 +10,8 @@ import { Header, HeaderProps } from '../components'; const Page = styled(EuiPage)` background: ${props => props.theme.eui.euiColorEmptyShade}; + flex: 1; + align-items: flex-start; `; interface Props extends HeaderProps { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/without_header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/without_header.tsx index cad98c5a0a7e1..9f9fa03942c09 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/without_header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/without_header.tsx @@ -9,6 +9,8 @@ import { EuiPage, EuiPageBody, EuiSpacer } from '@elastic/eui'; const Page = styled(EuiPage)` background: ${props => props.theme.eui.euiColorEmptyShade}; + flex: 1; + align-items: flex-start; `; interface Props { 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 ea6b045f504ec..05d150fd9ae23 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 + +
+
+
+ ); }; From 7e3de56303d39e284e94216df19ff70c712b9e47 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Mon, 20 Apr 2020 12:43:21 -0400 Subject: [PATCH 13/34] [TSVB] Use default Kibana palette for split series (#62241) * [TSVB] Rainbow palette shares colors with dashboard * Add migration * Add charts as NP dependency Co-authored-by: Elastic Machine --- src/plugins/vis_type_timeseries/kibana.json | 2 +- .../components/vis_types/timeseries/config.js | 15 ++- .../visualizations/views/timeseries/index.js | 14 ++- .../public/metrics_type.ts | 1 + .../vis_type_timeseries/public/plugin.ts | 6 +- .../vis_type_timeseries/public/services.ts | 5 + .../lib/vis_data/helpers/get_split_colors.js | 4 +- .../lib/vis_data/helpers/get_splits.test.js | 117 +++++++++++++----- .../visualization_migrations.test.ts | 58 +++++++++ .../saved_objects/visualization_migrations.ts | 33 +++++ .../apps/visualize/_tsvb_time_series.ts | 9 +- .../page_objects/visual_builder_page.ts | 4 + 12 files changed, 218 insertions(+), 50 deletions(-) diff --git a/src/plugins/vis_type_timeseries/kibana.json b/src/plugins/vis_type_timeseries/kibana.json index 38662c6a7ff89..9053d2543e0d0 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 024f59c3abb1c..d29b795b10ec8 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 b1c3c7ac6b67a..5cf1619150e5c 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 d93a376584eac..9aa84478fb78b 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 ff8d9077b0871..cad8c8f2025a1 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 0874d944033f5..376d32d0da13f 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/visualizations/server/saved_objects/visualization_migrations.test.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts index 26f8278cd3d43..83d53d27e41fd 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 80783e41863ea..94473e35a942d 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/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts index fa79190a5bf94..ac89c2b55e514 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 b8e6c812b46bd..12962b3a5cdef 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) { From 635be640d54488d5a6e2de1e3dea1a32a637dcfa Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 20 Apr 2020 10:50:54 -0600 Subject: [PATCH 14/34] [Maps] fix date labels (#63909) * [Maps] fix date labels * review feedback Co-authored-by: Elastic Machine --- .../maps/public/elasticsearch_geo_utils.js | 12 +++++++- .../public/elasticsearch_geo_utils.test.js | 29 +++++++++++++++---- .../es_search_source/es_search_source.js | 12 +++++++- .../properties/dynamic_style_property.js | 4 +++ 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/maps/public/elasticsearch_geo_utils.js b/x-pack/plugins/maps/public/elasticsearch_geo_utils.js index 617cf537fd5c3..417c5d84f8916 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 5db7556be4639..fc02e19173843 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/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 96679f0e85941..34fed37933e13 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 @@ -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/styles/vector/properties/dynamic_style_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js index ea521f8749d80..8cef78f9a8f21 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; } From 96b3fd1fe32b0fe6bb682573f368b5ba265f358f Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Mon, 20 Apr 2020 11:04:57 -0700 Subject: [PATCH 15/34] [Metrics UI] Remove remaining field filtering (#63398) Co-authored-by: Elastic Machine --- .../components/metrics_explorer/group_by.tsx | 15 +-------------- .../components/metrics_explorer/kuery_bar.tsx | 3 +-- .../components/metrics_explorer/metrics.tsx | 5 +---- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/infra/public/components/metrics_explorer/group_by.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/group_by.tsx index 750894fd0188b..3246a2aa4a731 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/group_by.tsx +++ b/x-pack/plugins/infra/public/components/metrics_explorer/group_by.tsx @@ -10,7 +10,6 @@ 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'; 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/kuery_bar.tsx b/x-pack/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx index dcc160d05b6ad..7c3e0444dbeea 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx +++ b/x-pack/plugins/infra/public/components/metrics_explorer/kuery_bar.tsx @@ -9,7 +9,6 @@ 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'; interface Props { @@ -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/components/metrics_explorer/metrics.tsx index 79d4122733c55..a49e42c9cac0e 100644 --- a/x-pack/plugins/infra/public/components/metrics_explorer/metrics.tsx +++ b/x-pack/plugins/infra/public/components/metrics_explorer/metrics.tsx @@ -12,7 +12,6 @@ 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'; 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 => ({ From 86922e80a8a1d96c23d174031d8d07371ea78b39 Mon Sep 17 00:00:00 2001 From: Joel Griffith Date: Mon, 20 Apr 2020 11:23:16 -0700 Subject: [PATCH 16/34] [Reporting] Config flag to escape formula CSV values (#63645) * Adds a new xpack.reporting.csv.escapeFormulaValues boolean to auto-escape potentially bad cells * Treat csvs with formulas differently than those that aren't escaped Co-authored-by: Elastic Machine --- .../resources/bin/kibana-docker | 1 + .../plugins/reporting/common/constants.ts | 1 + .../csv/server/execute_job.test.ts | 74 ++++++++++++++++++- .../export_types/csv/server/execute_job.ts | 5 +- .../csv/server/lib/cell_has_formula.ts | 11 +++ .../server/lib/check_cells_for_formulas.ts | 7 +- .../csv/server/lib/escape_value.test.ts | 34 ++++++++- .../csv/server/lib/escape_value.ts | 11 ++- .../csv/server/lib/generate_csv.ts | 17 ++++- .../reporting/export_types/csv/types.d.ts | 2 + .../server/lib/generate_csv_search.ts | 1 + .../plugins/reporting/server/config/schema.ts | 1 + 12 files changed, 152 insertions(+), 13 deletions(-) create mode 100644 x-pack/legacy/plugins/reporting/export_types/csv/server/lib/cell_has_formula.ts 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 d4d2e86e1e96b..38acfb15d3ece 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/x-pack/legacy/plugins/reporting/common/constants.ts b/x-pack/legacy/plugins/reporting/common/constants.ts index e3d6a4274e7df..f30a7cc87f318 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 f0afade8629ab..ad35aaf003094 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 376a398da274f..dbe305bc452db 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 09f7cd2061ffb..0ec39c527d656 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 64b021a2aeea8..dd0f9d08b864b 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 563de563350e9..60e75d74b2f98 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 1986e68917ba8..c7996ebf832a1 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 529c195486bc6..40a42db352635 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 9757c71c19cf4..2611b74c83de9 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/plugins/reporting/server/config/schema.ts b/x-pack/plugins/reporting/server/config/schema.ts index 67d70c1513c15..402fddcb5e014 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 From d69b1a0e39796fcf1131369168176ff76438ed77 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 20 Apr 2020 20:36:09 +0200 Subject: [PATCH 17/34] [Discover] Allow user to generate a report after saving a modified saved search (#63623) * Fix missing reset of initial state when saving saved searches * Fix invalid setting of docTitle * Add functional test --- .../discover/np_ready/angular/discover.js | 6 ++++-- x-pack/test/reporting/functional/reporting.js | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) 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 72276a38f6ac2..56966d6294c9a 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,7 +57,6 @@ const { core, chrome, data, - docTitle, history, indexPatterns, filterManager, @@ -214,6 +213,7 @@ function discoverController( isAppStateDirty, kbnUrlStateStorage, getPreviousAppState, + resetInitialAppState, } = getState({ defaultAppState: getStateDefaults(), storeInSessionStorage: config.get('state:storeInSessionStorage'), @@ -373,6 +373,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 +760,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/x-pack/test/reporting/functional/reporting.js b/x-pack/test/reporting/functional/reporting.js index 6107363986a40..c1a2ae634662c 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(); From 53a07528414495da59673c47ad80d3d5f777f2dd Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 20 Apr 2020 19:37:26 +0100 Subject: [PATCH 18/34] [ML] Fixing single metric viewer page padding (#63839) Co-authored-by: Elastic Machine --- .../public/application/timeseriesexplorer/timeseriesexplorer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index 5e505757dd2aa..42742f6efcb2a 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 = { From 6ffaeda11ffb5a5956e2be351669bb9039985e53 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Mon, 20 Apr 2020 15:03:40 -0400 Subject: [PATCH 19/34] [Ingest pipelines] Delete pipeline (#63635) --- .../public/application/constants/index.ts | 2 + .../public/application/index.tsx | 2 + .../application/mount_management_section.ts | 1 + .../sections/pipelines_list/delete_modal.tsx | 125 ++++++++++++++++++ .../sections/pipelines_list/details.tsx | 4 +- .../sections/pipelines_list/main.tsx | 19 ++- .../sections/pipelines_list/table.tsx | 34 ++++- .../public/application/services/api.ts | 18 ++- .../server/routes/api/delete.ts | 49 +++++++ .../server/routes/api/index.ts | 2 + .../ingest_pipelines/server/routes/index.ts | 8 +- .../ingest_pipelines/ingest_pipelines.ts | 99 ++++++++++++++ 12 files changed, 352 insertions(+), 11 deletions(-) create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/delete_modal.tsx create mode 100644 x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts diff --git a/x-pack/plugins/ingest_pipelines/public/application/constants/index.ts b/x-pack/plugins/ingest_pipelines/public/application/constants/index.ts index ae2b285c91c53..ed4bd0a42d38e 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/constants/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/constants/index.ts @@ -9,3 +9,5 @@ export const UIM_APP_NAME = 'ingest_pipelines'; export const UIM_PIPELINES_LIST_LOAD = 'pipelines_list_load'; export const UIM_PIPELINE_CREATE = 'pipeline_create'; export const UIM_PIPELINE_UPDATE = 'pipeline_update'; +export const UIM_PIPELINE_DELETE = 'pipeline_delete'; +export const UIM_PIPELINE_DELETE_MANY = 'pipeline_delete_many'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/index.tsx b/x-pack/plugins/ingest_pipelines/public/application/index.tsx index 914a4b3c57e70..778ce0c873e66 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/index.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/index.tsx @@ -6,6 +6,7 @@ import React, { ReactNode } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; +import { NotificationsSetup } from 'kibana/public'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { App } from './app'; @@ -16,6 +17,7 @@ export interface AppServices { metric: UiMetricService; documentation: DocumentationService; api: ApiService; + notifications: NotificationsSetup; } export const renderApp = ( diff --git a/x-pack/plugins/ingest_pipelines/public/application/mount_management_section.ts b/x-pack/plugins/ingest_pipelines/public/application/mount_management_section.ts index 51db91295ed42..9b950a54096c3 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/mount_management_section.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/mount_management_section.ts @@ -28,6 +28,7 @@ export async function mountManagementSection( metric: uiMetricService, documentation: documentationService, api: apiService, + notifications: coreSetup.notifications, }; return renderApp(element, I18nContext, services); diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/delete_modal.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/delete_modal.tsx new file mode 100644 index 0000000000000..c7736a6c19ba1 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/delete_modal.tsx @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { useKibana } from '../../../shared_imports'; + +export const PipelineDeleteModal = ({ + pipelinesToDelete, + callback, +}: { + pipelinesToDelete: string[]; + callback: (data?: { hasDeletedPipelines: boolean }) => void; +}) => { + const { services } = useKibana(); + + const numPipelinesToDelete = pipelinesToDelete.length; + + const handleDeletePipelines = () => { + services.api + .deletePipelines(pipelinesToDelete) + .then(({ data: { itemsDeleted, errors }, error }) => { + const hasDeletedPipelines = itemsDeleted && itemsDeleted.length; + + if (hasDeletedPipelines) { + const successMessage = + itemsDeleted.length === 1 + ? i18n.translate( + 'xpack.ingestPipelines.deleteModal.successDeleteSingleNotificationMessageText', + { + defaultMessage: "Deleted pipeline '{pipelineName}'", + values: { pipelineName: pipelinesToDelete[0] }, + } + ) + : i18n.translate( + 'xpack.ingestPipelines.deleteModal.successDeleteMultipleNotificationMessageText', + { + defaultMessage: + 'Deleted {numSuccesses, plural, one {# pipeline} other {# pipelines}}', + values: { numSuccesses: itemsDeleted.length }, + } + ); + + callback({ hasDeletedPipelines }); + services.notifications.toasts.addSuccess(successMessage); + } + + if (error || errors?.length) { + const hasMultipleErrors = errors?.length > 1 || (error && pipelinesToDelete.length > 1); + const errorMessage = hasMultipleErrors + ? i18n.translate( + 'xpack.ingestPipelines.deleteModal.multipleErrorsNotificationMessageText', + { + defaultMessage: 'Error deleting {count} pipelines', + values: { + count: errors?.length || pipelinesToDelete.length, + }, + } + ) + : i18n.translate('xpack.ingestPipelines.deleteModal.errorNotificationMessageText', { + defaultMessage: "Error deleting pipeline '{name}'", + values: { name: (errors && errors[0].name) || pipelinesToDelete[0] }, + }); + services.notifications.toasts.addDanger(errorMessage); + } + }); + }; + + const handleOnCancel = () => { + callback(); + }; + + return ( + + + } + onCancel={handleOnCancel} + onConfirm={handleDeletePipelines} + cancelButtonText={ + + } + confirmButtonText={ + + } + > + <> +

+ +

+ +
    + {pipelinesToDelete.map(name => ( +
  • {name}
  • + ))} +
+ +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details.tsx index a3e47cb59860d..10720153cd57b 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details.tsx @@ -26,7 +26,7 @@ import { PipelineDetailsJsonBlock } from './details_json_block'; export interface Props { pipeline: Pipeline; onEditClick: (pipelineName: string) => void; - onDeleteClick: () => void; + onDeleteClick: (pipelineName: string[]) => void; onClose: () => void; } @@ -122,7 +122,7 @@ export const PipelineDetails: FunctionComponent = ({
- + onDeleteClick([pipeline.name])}> {i18n.translate('xpack.ingestPipelines.list.pipelineDetails.deleteButtonLabel', { defaultMessage: 'Delete', })} diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx index 311c1c9d4c9e7..ca4892fe281c2 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx @@ -29,11 +29,13 @@ import { UIM_PIPELINES_LIST_LOAD } from '../../constants'; import { EmptyList } from './empty_list'; import { PipelineTable } from './table'; import { PipelineDetails } from './details'; +import { PipelineDeleteModal } from './delete_modal'; export const PipelinesList: React.FunctionComponent = ({ history }) => { const { services } = useKibana(); const [selectedPipeline, setSelectedPipeline] = useState(undefined); + const [pipelinesToDelete, setPipelinesToDelete] = useState([]); // Track component loaded useEffect(() => { @@ -63,7 +65,7 @@ export const PipelinesList: React.FunctionComponent = ({ hi {}} + onDeletePipelineClick={setPipelinesToDelete} onViewPipelineClick={setSelectedPipeline} pipelines={data} /> @@ -128,10 +130,23 @@ export const PipelinesList: React.FunctionComponent = ({ hi setSelectedPipeline(undefined)} - onDeleteClick={() => {}} + onDeleteClick={setPipelinesToDelete} onEditClick={editPipeline} /> )} + {pipelinesToDelete?.length > 0 ? ( + { + if (deleteResponse?.hasDeletedPipelines) { + // reload pipelines list + sendRequest(); + } + setPipelinesToDelete([]); + setSelectedPipeline(undefined); + }} + pipelinesToDelete={pipelinesToDelete} + /> + ) : null} ); }; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx index 45f539007cde3..01b05eace3b60 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx @@ -3,8 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { FunctionComponent } from 'react'; +import React, { FunctionComponent, useState } from 'react'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { EuiInMemoryTable, EuiLink, EuiButton } from '@elastic/eui'; import { BASE_PATH } from '../../../../common/constants'; @@ -13,8 +14,8 @@ import { Pipeline } from '../../../../common/types'; export interface Props { pipelines: Pipeline[]; onReloadClick: () => void; - onEditPipelineClick: (pipeineName: string) => void; - onDeletePipelineClick: (pipeline: Pipeline) => void; + onEditPipelineClick: (pipelineName: string) => void; + onDeletePipelineClick: (pipelineName: string[]) => void; onViewPipelineClick: (pipeline: Pipeline) => void; } @@ -25,9 +26,32 @@ export const PipelineTable: FunctionComponent = ({ onDeletePipelineClick, onViewPipelineClick, }) => { + const [selection, setSelection] = useState([]); + return ( 0 ? ( + onDeletePipelineClick(selection.map(pipeline => pipeline.name))} + color="danger" + > + + + ) : ( + undefined + ), toolsRight: [ = ({ name: i18n.translate('xpack.ingestPipelines.list.table.nameColumnTitle', { defaultMessage: 'Name', }), - render: (name: any, pipeline) => ( + render: (name: string, pipeline) => ( onViewPipelineClick(pipeline)}>{name} ), }, @@ -98,7 +122,7 @@ export const PipelineTable: FunctionComponent = ({ type: 'icon', icon: 'trash', color: 'danger', - onClick: onDeletePipelineClick, + onClick: ({ name }) => onDeletePipelineClick([name]), }, ], }, diff --git a/x-pack/plugins/ingest_pipelines/public/application/services/api.ts b/x-pack/plugins/ingest_pipelines/public/application/services/api.ts index 48b925b02eeb4..42a157705baa7 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/services/api.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/services/api.ts @@ -15,7 +15,12 @@ import { useRequest as _useRequest, } from '../../shared_imports'; import { UiMetricService } from './ui_metric'; -import { UIM_PIPELINE_CREATE, UIM_PIPELINE_UPDATE } from '../constants'; +import { + UIM_PIPELINE_CREATE, + UIM_PIPELINE_UPDATE, + UIM_PIPELINE_DELETE, + UIM_PIPELINE_DELETE_MANY, +} from '../constants'; export class ApiService { private client: HttpSetup | undefined; @@ -87,6 +92,17 @@ export class ApiService { return result; } + + public async deletePipelines(names: string[]) { + const result = this.sendRequest({ + path: `${API_BASE_PATH}/${names.map(name => encodeURIComponent(name)).join(',')}`, + method: 'delete', + }); + + this.trackUiMetric(names.length > 1 ? UIM_PIPELINE_DELETE_MANY : UIM_PIPELINE_DELETE); + + return result; + } } export const apiService = new ApiService(); diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts new file mode 100644 index 0000000000000..4664b49a08a50 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { schema } from '@kbn/config-schema'; + +import { API_BASE_PATH } from '../../../common/constants'; +import { RouteDependencies } from '../../types'; + +const paramsSchema = schema.object({ + names: schema.string(), +}); + +export const registerDeleteRoute = ({ router, license }: RouteDependencies): void => { + router.delete( + { + path: `${API_BASE_PATH}/{names}`, + validate: { + params: paramsSchema, + }, + }, + license.guardApiRoute(async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient; + const { names } = req.params; + const pipelineNames = names.split(','); + + const response: { itemsDeleted: string[]; errors: any[] } = { + itemsDeleted: [], + errors: [], + }; + + await Promise.all( + pipelineNames.map(pipelineName => { + return callAsCurrentUser('ingest.deletePipeline', { id: pipelineName }) + .then(() => response.itemsDeleted.push(pipelineName)) + .catch(e => + response.errors.push({ + name: pipelineName, + error: e, + }) + ); + }) + ); + + return res.ok({ body: response }); + }) + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/index.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/index.ts index 0d40d17205eed..9992f56512c01 100644 --- a/x-pack/plugins/ingest_pipelines/server/routes/api/index.ts +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/index.ts @@ -9,3 +9,5 @@ export { registerGetRoutes } from './get'; export { registerCreateRoute } from './create'; export { registerUpdateRoute } from './update'; + +export { registerDeleteRoute } from './delete'; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/index.ts b/x-pack/plugins/ingest_pipelines/server/routes/index.ts index d217fb937778c..419525816f217 100644 --- a/x-pack/plugins/ingest_pipelines/server/routes/index.ts +++ b/x-pack/plugins/ingest_pipelines/server/routes/index.ts @@ -6,12 +6,18 @@ import { RouteDependencies } from '../types'; -import { registerGetRoutes, registerCreateRoute, registerUpdateRoute } from './api'; +import { + registerGetRoutes, + registerCreateRoute, + registerUpdateRoute, + registerDeleteRoute, +} from './api'; export class ApiRoutes { setup(dependencies: RouteDependencies) { registerGetRoutes(dependencies); registerCreateRoute(dependencies); registerUpdateRoute(dependencies); + registerDeleteRoute(dependencies); } } diff --git a/x-pack/test/api_integration/apis/management/ingest_pipelines/ingest_pipelines.ts b/x-pack/test/api_integration/apis/management/ingest_pipelines/ingest_pipelines.ts index 41f285938c003..a1773a052ede2 100644 --- a/x-pack/test/api_integration/apis/management/ingest_pipelines/ingest_pipelines.ts +++ b/x-pack/test/api_integration/apis/management/ingest_pipelines/ingest_pipelines.ts @@ -185,5 +185,104 @@ export default function({ getService }: FtrProviderContext) { }); }); }); + + describe('Delete', () => { + const PIPELINE = { + description: 'test pipeline description', + processors: [ + { + script: { + source: 'ctx._type = null', + }, + }, + ], + version: 1, + }; + + it('should delete a pipeline', async () => { + // Create pipeline to be deleted + const PIPELINE_ID = 'test_delete_pipeline'; + createPipeline({ body: PIPELINE, id: PIPELINE_ID }); + + const uri = `${API_BASE_PATH}/${PIPELINE_ID}`; + + const { body } = await supertest + .delete(uri) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(body).to.eql({ + itemsDeleted: [PIPELINE_ID], + errors: [], + }); + }); + + it('should delete multiple pipelines', async () => { + // Create pipelines to be deleted + const PIPELINE_ONE_ID = 'test_delete_pipeline_1'; + const PIPELINE_TWO_ID = 'test_delete_pipeline_2'; + createPipeline({ body: PIPELINE, id: PIPELINE_ONE_ID }); + createPipeline({ body: PIPELINE, id: PIPELINE_TWO_ID }); + + const uri = `${API_BASE_PATH}/${PIPELINE_ONE_ID},${PIPELINE_TWO_ID}`; + + const { + body: { itemsDeleted, errors }, + } = await supertest + .delete(uri) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(errors).to.eql([]); + + // The itemsDeleted array order isn't guaranteed, so we assert against each pipeline name instead + [PIPELINE_ONE_ID, PIPELINE_TWO_ID].forEach(pipelineName => { + expect(itemsDeleted.includes(pipelineName)).to.be(true); + }); + }); + + it('should return an error for any pipelines not sucessfully deleted', async () => { + const PIPELINE_DOES_NOT_EXIST = 'pipeline_does_not_exist'; + + // Create pipeline to be deleted + const PIPELINE_ONE_ID = 'test_delete_pipeline_1'; + createPipeline({ body: PIPELINE, id: PIPELINE_ONE_ID }); + + const uri = `${API_BASE_PATH}/${PIPELINE_ONE_ID},${PIPELINE_DOES_NOT_EXIST}`; + + const { body } = await supertest + .delete(uri) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(body).to.eql({ + itemsDeleted: [PIPELINE_ONE_ID], + errors: [ + { + name: PIPELINE_DOES_NOT_EXIST, + error: { + msg: '[resource_not_found_exception] pipeline [pipeline_does_not_exist] is missing', + path: '/_ingest/pipeline/pipeline_does_not_exist', + query: {}, + statusCode: 404, + response: JSON.stringify({ + error: { + root_cause: [ + { + type: 'resource_not_found_exception', + reason: 'pipeline [pipeline_does_not_exist] is missing', + }, + ], + type: 'resource_not_found_exception', + reason: 'pipeline [pipeline_does_not_exist] is missing', + }, + status: 404, + }), + }, + }, + ], + }); + }); + }); }); } From 3ce3da8a65c52c511c7266712c4f44d99d58ecb3 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 20 Apr 2020 16:14:46 -0400 Subject: [PATCH 20/34] [FTR] Add test suite metrics tracking/output (#62515) --- .../functional_test_runner.ts | 3 + .../src/functional_test_runner/lib/index.ts | 1 + .../lib/suite_tracker.test.ts | 197 ++++++++++++++++++ .../lib/suite_tracker.ts | 147 +++++++++++++ 4 files changed, 348 insertions(+) create mode 100644 packages/kbn-test/src/functional_test_runner/lib/suite_tracker.test.ts create mode 100644 packages/kbn-test/src/functional_test_runner/lib/suite_tracker.ts 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 03075dd4081fd..3a66ba22ccf3d 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 8940eccad503a..2e534974e1d76 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 0000000000000..b6c2c0a6d511d --- /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 0000000000000..8967251ea78de --- /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; + } +} From 5b11434f3a57a1516f090bb87c4fd1a1f6892053 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Mon, 20 Apr 2020 16:28:21 -0400 Subject: [PATCH 21/34] Add sub-menus to Resolver node (for 75% zoom) (#63476) * adding two buttons for 75% zoom view * adjust palette to match comps Co-authored-by: Kevin Qualters --- .../public/embeddables/resolver/view/defs.tsx | 10 +- .../resolver/view/process_event_dot.tsx | 94 ++++++++++++++++--- .../endpoint/scripts/resolver_generator.ts | 2 +- 3 files changed, 91 insertions(+), 15 deletions(-) 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 de9c3c7e8f8f3..064645019ca34 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 10e331ffff02d..3201e83164dba 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 dd9e591f4b034..2129bef0624b8 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: { From e32fd2617a70b1122b084900282ea15f8c9a9ea7 Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Mon, 20 Apr 2020 13:44:19 -0700 Subject: [PATCH 22/34] [Metrics UI] Reorganize file layout for Metrics UI (#60049) * [Metrics UI] Reorganize pages specific files under pages - Restructuring pages folder - Renaming metrics_ui to metrics - Fixing paths in jest mocks - Fixing AlertsFlyout path - Fixing bad file paths from the merge * Fixing typo compontents to components Co-authored-by: Elastic Machine --- .../inventory_models/aws_ec2/layout.tsx | 12 +- .../aws_ec2/toolbar_items.tsx | 6 +- .../inventory_models/aws_rds/layout.tsx | 10 +- .../aws_rds/toolbar_items.tsx | 6 +- .../common/inventory_models/aws_s3/layout.tsx | 10 +- .../inventory_models/aws_s3/toolbar_items.tsx | 6 +- .../inventory_models/aws_sqs/layout.tsx | 10 +- .../aws_sqs/toolbar_items.tsx | 6 +- .../inventory_models/container/layout.tsx | 14 +- .../container/toolbar_items.tsx | 4 +- .../common/inventory_models/host/layout.tsx | 14 +- .../inventory_models/host/toolbar_items.tsx | 4 +- .../infra/common/inventory_models/layouts.ts | 2 +- .../common/inventory_models/pod/layout.tsx | 14 +- .../inventory_models/pod/toolbar_items.tsx | 4 +- .../cloud_toolbar_items.tsx | 6 +- .../metrics_and_groupby_toolbar_items.tsx | 8 +- .../inventory_models/shared/layouts/aws.tsx | 10 +- .../inventory_models/shared/layouts/nginx.tsx | 8 +- .../infra/common/inventory_models/toolbars.ts | 2 +- .../common/saved_objects/inventory_view.ts | 2 +- .../saved_objects/metrics_explorer_view.ts | 2 +- .../alerting/metrics/alert_flyout.tsx | 2 +- .../alerting/metrics/expression.tsx | 6 +- .../components/waffle/lib/type_guards.ts | 17 -- ...ith_metrics_explorer_options_url_state.tsx | 2 +- .../infra/public/containers/with_options.tsx | 64 ----- .../public/pages/infrastructure/index.tsx | 135 ---------- .../redirect_to_host_detail_via_ip.tsx | 2 +- .../pages/link_to/redirect_to_node_detail.tsx | 2 +- .../infra/public/pages/metrics/index.tsx | 245 +++++++++--------- .../inventory_view/components}/layout.tsx | 18 +- .../components/nodes_overview.tsx} | 31 ++- .../inventory_view/components}/search_bar.tsx | 6 +- .../inventory_view/components/table_view.tsx} | 14 +- .../components}/toolbars/save_views.tsx | 6 +- .../components}/toolbars/toolbar.tsx | 14 +- .../components}/toolbars/toolbar_wrapper.tsx | 10 +- .../components/waffle/conditional_tooltip.tsx | 0 .../components/waffle/custom_field_panel.tsx | 2 +- .../components/waffle/gradient_legend.tsx | 4 +- .../components/waffle/group_name.tsx | 4 +- .../components/waffle/group_of_groups.tsx | 6 +- .../components/waffle/group_of_nodes.tsx | 6 +- .../components/waffle/legend.tsx | 8 +- .../components/waffle/legend_controls.tsx | 4 +- .../inventory_view}/components/waffle/map.tsx | 19 +- .../metric_control/custom_metric_form.tsx | 7 +- .../metric_control/get_custom_metric_label.ts | 2 +- .../waffle/metric_control/index.tsx | 4 +- .../metric_control/metrics_context_menu.tsx | 4 +- .../metric_control/metrics_edit_mode.tsx | 7 +- .../waffle/metric_control/mode_switcher.tsx | 7 +- .../components/waffle/metric_control/types.ts | 0 .../components/waffle/node.tsx | 12 +- .../components/waffle/node_context_menu.tsx | 18 +- .../components/waffle/steps_legend.tsx | 4 +- .../components/waffle/view_switcher.tsx | 0 .../waffle/waffle_accounts_controls.tsx | 4 +- .../waffle/waffle_group_by_controls.tsx | 8 +- .../waffle/waffle_inventory_switcher.tsx | 6 +- .../waffle/waffle_region_controls.tsx | 2 +- .../waffle/waffle_time_controls.tsx | 2 +- .../hooks}/use_inventory_meta.ts | 8 +- .../inventory_view/hooks}/use_snaphot.ts | 11 +- .../hooks/use_waffle_filters.ts | 8 +- .../hooks/use_waffle_options.ts | 6 +- .../inventory_view/hooks/use_waffle_time.ts | 2 +- .../hooks/use_waffle_view_state.ts | 0 .../inventory_view}/index.tsx | 2 +- .../lib/apply_wafflemap_layout.ts | 7 +- .../inventory_view}/lib/color_from_value.ts | 2 +- .../lib/create_uptime_link.test.ts | 4 +- .../inventory_view}/lib/create_uptime_link.ts | 6 +- .../lib/field_to_display_name.ts | 0 .../inventory_view/lib}/nodes_to_wafflemap.ts | 4 +- .../inventory_view}/lib/size_of_squares.ts | 0 .../inventory_view/lib}/type_guards.ts | 17 +- .../inventory_view}/toolbar.tsx | 6 +- .../components/chart_section_vis.tsx | 6 +- .../components/error_message.tsx | 0 .../components/gauges_section_vis.tsx | 6 +- .../{ => metric_detail}/components/helpers.ts | 8 +- .../components/invalid_node.tsx | 6 +- .../components/layout_content.tsx | 2 +- .../components/metadata_details.tsx | 4 +- .../components/node_details_page.tsx | 14 +- .../components/page_body.tsx | 10 +- .../components/page_error.tsx | 4 +- .../components/section.tsx | 0 .../components/series_chart.tsx | 4 +- .../components/side_nav.tsx | 2 +- .../components/sub_section.tsx | 2 +- .../components/time_controls.test.tsx | 2 +- .../components/time_controls.tsx | 6 +- .../containers/metadata_context.ts | 2 +- .../hooks/metrics_time.test.tsx | 0 .../metric_detail/hooks}/use_metadata.ts | 12 +- .../hooks/use_metrics_time.ts | 4 +- .../metric_detail/hooks}/use_node_details.ts | 10 +- .../pages/metrics/metric_detail/index.tsx | 140 ++++++++++ .../lib/get_filtered_metrics.ts | 6 +- .../lib/side_nav_context.ts | 0 .../{ => metric_detail}/page_providers.tsx | 2 +- .../metrics/{ => metric_detail}/types.ts | 6 +- .../components}/aggregation.tsx | 6 +- .../metrics_explorer/components}/chart.tsx | 12 +- .../components}/chart_context_menu.test.tsx | 9 +- .../components}/chart_context_menu.tsx | 14 +- .../components}/chart_options.tsx | 2 +- .../metrics_explorer/components}/charts.tsx | 10 +- .../components}/empty_chart.tsx | 0 .../metrics_explorer/components}/group_by.tsx | 2 +- .../components}/helpers/calculate_domain.ts | 4 +- .../helpers/calculate_domian.test.ts | 6 +- .../helpers/create_formatter_for_metric.ts | 6 +- .../create_formatter_for_metrics.test.ts | 2 +- .../helpers/create_metric_label.test.ts | 2 +- .../helpers/create_metric_label.ts | 2 +- .../helpers/create_tsvb_link.test.ts | 11 +- .../components}/helpers/create_tsvb_link.ts | 12 +- .../components}/helpers/get_chart_theme.ts | 0 .../helpers/metric_to_format.test.ts | 4 +- .../components}/helpers/metric_to_format.ts | 4 +- .../components}/kuery_bar.tsx | 6 +- .../metrics_explorer/components}/metrics.tsx | 6 +- .../components}/no_metrics.tsx | 0 .../components}/series_chart.tsx | 6 +- .../metrics_explorer/components}/toolbar.tsx | 16 +- .../hooks}/use_metric_explorer_state.test.tsx | 6 +- .../hooks}/use_metric_explorer_state.ts | 8 +- .../hooks}/use_metrics_explorer_data.test.tsx | 6 +- .../hooks}/use_metrics_explorer_data.ts | 10 +- .../use_metrics_explorer_options.test.tsx | 0 .../hooks}/use_metrics_explorer_options.ts | 4 +- .../metrics_explorer/index.tsx | 6 +- .../{infrastructure => metrics}/settings.tsx | 0 .../infra/public/routers/metrics_router.tsx | 6 +- .../public/utils/fixtures/metrics_explorer.ts | 2 +- 139 files changed, 683 insertions(+), 722 deletions(-) rename x-pack/plugins/infra/common/inventory_models/shared/{compontents => components}/cloud_toolbar_items.tsx (75%) rename x-pack/plugins/infra/common/inventory_models/shared/{compontents => components}/metrics_and_groupby_toolbar_items.tsx (81%) delete mode 100644 x-pack/plugins/infra/public/components/waffle/lib/type_guards.ts delete mode 100644 x-pack/plugins/infra/public/containers/with_options.tsx delete mode 100644 x-pack/plugins/infra/public/pages/infrastructure/index.tsx rename x-pack/plugins/infra/public/{components/inventory => pages/metrics/inventory_view/components}/layout.tsx (78%) rename x-pack/plugins/infra/public/{components/nodes_overview/index.tsx => pages/metrics/inventory_view/components/nodes_overview.tsx} (88%) rename x-pack/plugins/infra/public/pages/{inventory_view/compontents => metrics/inventory_view/components}/search_bar.tsx (86%) rename x-pack/plugins/infra/public/{components/nodes_overview/table.tsx => pages/metrics/inventory_view/components/table_view.tsx} (92%) rename x-pack/plugins/infra/public/{components/inventory => pages/metrics/inventory_view/components}/toolbars/save_views.tsx (68%) rename x-pack/plugins/infra/public/{components/inventory => pages/metrics/inventory_view/components}/toolbars/toolbar.tsx (77%) rename x-pack/plugins/infra/public/{components/inventory => pages/metrics/inventory_view/components}/toolbars/toolbar_wrapper.tsx (95%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/conditional_tooltip.tsx (100%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/custom_field_panel.tsx (97%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/gradient_legend.tsx (96%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/group_name.tsx (96%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/group_of_groups.tsx (90%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/group_of_nodes.tsx (90%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/legend.tsx (88%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/legend_controls.tsx (97%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/map.tsx (84%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/metric_control/custom_metric_form.tsx (97%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/metric_control/get_custom_metric_label.ts (91%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/metric_control/index.tsx (97%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/metric_control/metrics_context_menu.tsx (94%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/metric_control/metrics_edit_mode.tsx (92%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/metric_control/mode_switcher.tsx (95%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/metric_control/types.ts (100%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/node.tsx (93%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/node_context_menu.tsx (91%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/steps_legend.tsx (95%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/view_switcher.tsx (100%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/waffle_accounts_controls.tsx (94%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/waffle_group_by_controls.tsx (95%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/waffle_inventory_switcher.tsx (94%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/waffle_region_controls.tsx (97%) rename x-pack/plugins/infra/public/{ => pages/metrics/inventory_view}/components/waffle/waffle_time_controls.tsx (95%) rename x-pack/plugins/infra/public/{containers/inventory_metadata => pages/metrics/inventory_view/hooks}/use_inventory_meta.ts (79%) rename x-pack/plugins/infra/public/{containers/waffle => pages/metrics/inventory_view/hooks}/use_snaphot.ts (83%) rename x-pack/plugins/infra/public/pages/{ => metrics}/inventory_view/hooks/use_waffle_filters.ts (90%) rename x-pack/plugins/infra/public/pages/{ => metrics}/inventory_view/hooks/use_waffle_options.ts (95%) rename x-pack/plugins/infra/public/pages/{ => metrics}/inventory_view/hooks/use_waffle_time.ts (97%) rename x-pack/plugins/infra/public/pages/{ => metrics}/inventory_view/hooks/use_waffle_view_state.ts (100%) rename x-pack/plugins/infra/public/pages/{infrastructure/snapshot => metrics/inventory_view}/index.tsx (98%) rename x-pack/plugins/infra/public/{components/waffle => pages/metrics/inventory_view}/lib/apply_wafflemap_layout.ts (94%) rename x-pack/plugins/infra/public/{components/waffle => pages/metrics/inventory_view}/lib/color_from_value.ts (98%) rename x-pack/plugins/infra/public/{components/waffle => pages/metrics/inventory_view}/lib/create_uptime_link.test.ts (96%) rename x-pack/plugins/infra/public/{components/waffle => pages/metrics/inventory_view}/lib/create_uptime_link.ts (83%) rename x-pack/plugins/infra/public/{components/waffle => pages/metrics/inventory_view}/lib/field_to_display_name.ts (100%) rename x-pack/plugins/infra/public/{containers/waffle => pages/metrics/inventory_view/lib}/nodes_to_wafflemap.ts (97%) rename x-pack/plugins/infra/public/{components/waffle => pages/metrics/inventory_view}/lib/size_of_squares.ts (100%) rename x-pack/plugins/infra/public/{containers/waffle => pages/metrics/inventory_view/lib}/type_guards.ts (55%) rename x-pack/plugins/infra/public/pages/{infrastructure/snapshot => metrics/inventory_view}/toolbar.tsx (75%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/chart_section_vis.tsx (94%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/error_message.tsx (100%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/gauges_section_vis.tsx (92%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/helpers.ts (91%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/invalid_node.tsx (90%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/layout_content.tsx (84%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/metadata_details.tsx (97%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/node_details_page.tsx (90%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/page_body.tsx (82%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/page_error.tsx (90%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/section.tsx (100%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/series_chart.tsx (93%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/side_nav.tsx (95%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/sub_section.tsx (94%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/time_controls.test.tsx (96%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/components/time_controls.tsx (91%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/containers/metadata_context.ts (84%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/hooks/metrics_time.test.tsx (100%) rename x-pack/plugins/infra/public/{containers/metadata => pages/metrics/metric_detail/hooks}/use_metadata.ts (76%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/hooks/use_metrics_time.ts (96%) rename x-pack/plugins/infra/public/{containers/node_details => pages/metrics/metric_detail/hooks}/use_node_details.ts (75%) create mode 100644 x-pack/plugins/infra/public/pages/metrics/metric_detail/index.tsx rename x-pack/plugins/infra/public/{containers/metadata => pages/metrics/metric_detail}/lib/get_filtered_metrics.ts (78%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/lib/side_nav_context.ts (100%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/page_providers.tsx (91%) rename x-pack/plugins/infra/public/pages/metrics/{ => metric_detail}/types.ts (86%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/aggregation.tsx (89%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/chart.tsx (92%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/chart_context_menu.test.tsx (96%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/chart_context_menu.tsx (91%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/chart_options.tsx (98%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/charts.tsx (92%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/empty_chart.tsx (100%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/group_by.tsx (93%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/helpers/calculate_domain.ts (87%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/helpers/calculate_domian.test.ts (85%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/helpers/create_formatter_for_metric.ts (76%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/helpers/create_formatter_for_metrics.test.ts (94%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/helpers/create_metric_label.test.ts (88%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/helpers/create_metric_label.ts (80%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/helpers/create_tsvb_link.test.ts (97%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/helpers/create_tsvb_link.ts (91%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/helpers/get_chart_theme.ts (100%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/helpers/metric_to_format.test.ts (90%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/helpers/metric_to_format.ts (82%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/kuery_bar.tsx (88%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/metrics.tsx (91%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/no_metrics.tsx (100%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/series_chart.tsx (93%) rename x-pack/plugins/infra/public/{components/metrics_explorer => pages/metrics/metrics_explorer/components}/toolbar.tsx (88%) rename x-pack/plugins/infra/public/pages/{infrastructure/metrics_explorer => metrics/metrics_explorer/hooks}/use_metric_explorer_state.test.tsx (96%) rename x-pack/plugins/infra/public/pages/{infrastructure/metrics_explorer => metrics/metrics_explorer/hooks}/use_metric_explorer_state.ts (92%) rename x-pack/plugins/infra/public/{containers/metrics_explorer => pages/metrics/metrics_explorer/hooks}/use_metrics_explorer_data.test.tsx (96%) rename x-pack/plugins/infra/public/{containers/metrics_explorer => pages/metrics/metrics_explorer/hooks}/use_metrics_explorer_data.ts (91%) rename x-pack/plugins/infra/public/{containers/metrics_explorer => pages/metrics/metrics_explorer/hooks}/use_metrics_explorer_options.test.tsx (100%) rename x-pack/plugins/infra/public/{containers/metrics_explorer => pages/metrics/metrics_explorer/hooks}/use_metrics_explorer_options.ts (96%) rename x-pack/plugins/infra/public/pages/{infrastructure => metrics}/metrics_explorer/index.tsx (92%) rename x-pack/plugins/infra/public/pages/{infrastructure => metrics}/settings.tsx (100%) 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 68bfe41fd538e..9494e4aa396a5 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 683851fec4d77..b2da7dec3f2e0 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 220c6c67f4aea..08b865f01b06c 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 24f05fd91589c..2a8394b9dd3a4 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 805236cf47082..e16f8ef6addde 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 54c3196ae2833..324bdd0586029 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 d581ac751682d..ff13f2db104de 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 22cb2740a4a7f..3229c07034772 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 9956b2c9a2ce4..b9366a43e40c6 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 dbb77cb3b677e..f6c707726d9ca 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 6d7d361254220..e23118c747a9b 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 fc7696ee53c9d..136264c0e26f4 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 b59ce010361ec..df82f154b47b3 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 8bc2f3ee8b4b3..271e32556ae28 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 d48c27efd88fd..c1cd375ff47bf 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 766a8ae8142f5..da5017b0f3a36 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 738fce45ee99f..bf37828ed0856 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 7a0b898d406ce..6f2791534c17e 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 79cea5150d498..cf3a06994cc96 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 39e9f5a260f7a..e00cb094d0455 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 8ae765f379add..8933de57b0448 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 a9f1194844640..6eb08eabc15b5 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 914054e1fd9b7..38709c117c817 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 0e9da32aaa509..665da25dcbfff 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 f793afee1b948..0000000000000 --- 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 d2fa49e8d5d9f..04f518aa9080f 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 e18fc85a68d60..0000000000000 --- 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 d592ae3480fc9..0000000000000 --- 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 b1dab3bd3f673..3ad242c77412d 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 72a41f5264244..3d25e4c6c258d 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 531be40d2dc43..cc88dd9e0d0f8 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 3c91f9fa5946f..bc8be9862fe63 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 ef22e0486f892..afbfd2a079253 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 f4fde46d434f8..1f84ef3685f34 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 82991076255ee..0557343e735f9 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 cb315d3e17b03..eb40ea595662a 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 63ab6d2f4465a..3ac9c2c189628 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 fefda94372cfb..86cc0d8ee62e0 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 d2dc535f6d6b3..090d53f1ff737 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 6b0c4bb41dc98..87f7e4cbff11e 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 01bd3600a1624..308460203b132 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 9634293587d49..6b3f22007f580 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 6b82671617df7..fc438ed4ca0a2 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 13e533b225d4d..ccb4cc71924f4 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 26b5b1c0af727..6ec21ad2e1b49 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 7cab2307dacfa..e34c63d332fd8 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 26e42061ed10b..aec624c0aff1f 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 4f88c1b29c1f2..495cc8197d2e7 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 0f2034fe9cb25..08d5b3e9e0670 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 1aacf54244c37..1cdef493aee4f 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 ba1f46815db20..4d1bc906de0b9 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 43bb904594c68..acb740f1750c8 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 4eb5ccb8c1b4d..fd5b5b01f329f 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 5f05cebd8f616..275635a33ec26 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 d2079d53dad71..1ef0f2d0c4288 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 56c3a205b05d4..a8b0cf21bce85 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 1a3cef591bc07..bc763d2cf9378 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 21da10a0a7650..23e06823f407f 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 52377bf4b8296..671e44f42ef6a 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 458bb674afade..7f190f21484d9 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 0ed1f3e35449b..b038491690a13 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 31f02f46caeda..3ec63d7b2de28 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 02c079dcaddc4..f6cbb59779039 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 2853917d5f683..32bfe6e085b4e 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 051b5e598cb75..db3abd37b58dd 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 48cc56388c0f2..e473aea7a1f0b 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 5f3c06fcfbba7..68600ac5d2ce4 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 c082686bb9d63..334865306ee88 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 902969c83ba39..5f760cf2f591e 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 72b46f4fb5c7b..6c089ee8d22f4 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 d2511ba7e669e..469b54b2d9d68 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 3e21e3a56a6c6..62d02022d4c93 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 ccdaa5e8dc785..d6a87a0197f5f 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 290f0eda452ce..588a0d84918c6 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 e069b52be8be7..0aab676b7d6c5 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 4449196f2fb53..bb4ad32660952 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 b089e2237c2e5..6d83017a3f689 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 4e10f245acdcc..4620102517549 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 3bf492d398f2c..7ca69dd56251d 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 dd2a5f2bdb39e..0d0bc8c82397e 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 e651d6b92d981..68166a6e53bfd 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 e54cdcd151f6f..bda2a5941e023 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 849a5b8922165..0d7716ad3cc66 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 94f97c7f45e61..1cba3366acbbb 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 325d510293135..7b269adc96638 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 02ba506e8abe1..83f5187f8a46c 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 cdbdc9bb7ecdb..ef6486eac0fdb 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 4ecf7fa15548c..b8995d27d4dbe 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 1ba016195bef4..d2a20d449d89a 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 2ed86863535ff..98803ef2e69c6 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 189c48ba1a70c..3bb6c98c8eac0 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 0000000000000..197a735f7fd1f --- /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 b485c90700145..57ff182f01963 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 d3f10adec06ed..597977d9d2735 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 2cc261df28977..3ec57e23a425d 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 76fa519ab3756..0c0f7b33b3a4a 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 43b08f45eed34..089e1abfc4c91 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 8ffef269a42ea..5c0abb8fd845f 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 75a04cbe9799e..31086a21ca13f 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 657c4cea30f3a..ba28075ededb6 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 64e7b27f5722f..ecec116310875 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 93% 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 3246a2aa4a731..bfe8ddb2e0829 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,7 +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 { MetricsExplorerOptions } from '../hooks/use_metrics_explorer_options'; interface Props { options: MetricsExplorerOptions; 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 90569854b833b..811486d355f2e 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 4b45534d41db8..f94c6b6156ae4 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 33ec2ce2715a3..d07a6b45f02be 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 ec41e90e441a4..e039d5d4b3eeb 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 cbf6904d246c7..367c472f414e4 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 b6453a81317b1..1607302a6259a 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 05637642b8dd9..47bb4c8c57716 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 20706f563ec63..559422584f579 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 4cb27b4fb65c3..6b27627a9fb5b 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 63272c86a5dc7..1dbbf97a32217 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 88% 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 7c3e0444dbeea..e9826e1ff3955 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,9 +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 { 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; 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 91% 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 a49e42c9cac0e..612735e2ba772 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,9 +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 { 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; 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 ed7a994dd2bbe..ad7ce83539526 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 0fbb0b6acad17..81971bd31a973 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 874ac0987023b..f0734f76cfacd 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 88e6d9d800661..8a9ed901de0b0 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 bbc8778545b4a..94edab54fb71e 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 b32496fbf30a1..93aacb586a5cd 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 2b802af8e8c15..9d124a6af8012 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 0999cea59731c..a213671e9436e 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 7cb9de65e7291..0e427150a46cc 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 e39d6f785d53e..15159ad45c7f3 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, From e302fd84861f1182b1bcff348be1a112eb48eb7e Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Mon, 20 Apr 2020 15:56:41 -0500 Subject: [PATCH 23/34] Vega doc changes (#63889) * Vega doc changes * Final changes * Review comments --- docs/images/vega_lite_default.png | Bin 0 -> 291544 bytes docs/visualize/vega.asciidoc | 224 +++++++++++++----------------- 2 files changed, 100 insertions(+), 124 deletions(-) create mode 100644 docs/images/vega_lite_default.png diff --git a/docs/images/vega_lite_default.png b/docs/images/vega_lite_default.png new file mode 100644 index 0000000000000000000000000000000000000000..1ce1592d0738c6636157430522562bf0bbdea344 GIT binary patch literal 291544 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 UQE=@G1W>PAH;r`4wQNKG2kW!8(*OVf literal 0 HcmV?d00001 diff --git a/docs/visualize/vega.asciidoc b/docs/visualize/vega.asciidoc index b8c0d1dbe3dda..e0d9955f0c3db 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. From 35dab183abc81c91fb8f63d7c405ac0e02d1072e Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 20 Apr 2020 15:38:10 -0600 Subject: [PATCH 24/34] [Maps] Do not fetch geo_shape from docvalues (#63997) --- .../public/layers/sources/es_search_source/es_search_source.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 34fed37933e13..aaa56b30c735a 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' ? { From 2af0c6d47e159bb5d0aeabb5f5e75f80580614a5 Mon Sep 17 00:00:00 2001 From: Aaron Caldwell Date: Mon, 20 Apr 2020 16:58:33 -0600 Subject: [PATCH 25/34] =?UTF-8?q?[Maps]=20Migrate=20remaining=20maps=20cli?= =?UTF-8?q?ent=20files=20to=20NP=20(except=20routi=E2=80=A6=20(#63859)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move actions over * Move connected components over * Move components over * Move selectors over * Move embeddables over. Set up legacy presence to keep legacy maps embeddables working * Move angular and services over. Some additional top-level files * Some file moves. Move styles over to NP * Handle feature catalogue entry in NP. Add plugin deps to kibana.json * Move vis registration to NP * Clean up linting comments. Add linting comments to route controller. Move common to NP * Add back in i18n context for embeddable * Fix jest test paths. Fix TS lint errors Co-authored-by: Elastic Machine --- .../legacy/plugins/maps/common/constants.ts | 8 - .../plugins/maps/common/descriptor_types.ts | 8 - .../plugins/maps/common/get_join_key.ts | 8 - .../plugins/maps/common/i18n_getters.ts | 8 - .../plugins/maps/common/parse_xml_string.js | 8 - x-pack/legacy/plugins/maps/index.js | 11 +- x-pack/legacy/plugins/maps/migrations.js | 16 +- .../maps/public/actions/map_actions.d.ts | 8 - .../maps/public/actions/map_actions.js | 978 ------------------ .../plugins/maps/public/actions/ui_actions.js | 91 -- .../maps/public/angular/get_initial_layers.js | 54 - .../maps/public/angular/map_controller.js | 32 +- .../maps/public/components/_index.scss | 3 - .../public/connected_components/_index.scss | 6 - .../layer_addpanel/source_select/_index.scss | 1 - .../layer_panel/_index.scss | 4 - .../layer_panel/view.d.ts | 8 - .../connected_components/map/mb/utils.js | 107 -- .../widget_overlay/_index.scss | 6 - .../widget_overlay/layer_control/_index.scss | 2 - .../embeddable/map_embeddable_factory.ts | 154 +-- x-pack/legacy/plugins/maps/public/index.scss | 16 +- x-pack/legacy/plugins/maps/public/index.ts | 9 +- x-pack/legacy/plugins/maps/public/legacy.ts | 3 +- .../maps/public/legacy_register_feature.ts | 14 - x-pack/legacy/plugins/maps/public/plugin.ts | 5 +- .../maps/public/register_vis_type_alias.js | 49 - x-pack/legacy/plugins/maps/public/routes.js | 9 +- .../server/lib/get_index_pattern_settings.js | 5 +- .../lib/get_index_pattern_settings.test.js | 5 +- .../maps_telemetry/collectors/register.ts | 2 +- .../server/maps_telemetry/maps_telemetry.ts | 4 +- x-pack/legacy/plugins/maps/server/plugin.js | 7 +- x-pack/legacy/plugins/maps/server/routes.js | 2 +- .../migrations/add_field_meta_options.js | 0 .../migrations/add_field_meta_options.test.js | 0 .../ems_raster_tile_to_ems_vector_tile.js | 0 ...ems_raster_tile_to_ems_vector_tile.test.js | 0 .../common/migrations/join_agg_key.test.ts | 0 .../maps/common/migrations/join_agg_key.ts | 2 +- .../migrate_symbol_style_descriptor.js | 0 .../migrate_symbol_style_descriptor.test.js | 0 .../migrations/move_apply_global_query.js | 0 .../move_apply_global_query.test.js | 0 .../maps/common/migrations/references.js | 0 .../maps/common/migrations/references.test.js | 0 .../common/migrations/scaling_type.test.ts | 0 .../maps/common/migrations/scaling_type.ts | 2 +- .../migrations/top_hits_time_to_sort.js | 0 .../migrations/top_hits_time_to_sort.test.js | 0 x-pack/plugins/maps/kibana.json | 10 +- .../plugins/maps/public/_main.scss | 0 .../plugins/maps/public/_mapbox_hacks.scss | 0 .../maps/public/actions/map_actions.js | 925 +++++++++++++++++ .../maps/public/actions/map_actions.test.js | 2 +- .../maps/public/actions/ui_actions.d.ts | 11 + .../plugins/maps/public/actions/ui_actions.js | 70 ++ .../public/angular/get_initial_layers.d.ts | 0 .../maps/public/angular/get_initial_layers.js | 53 + .../public/angular/get_initial_layers.test.js | 17 +- .../maps/public/angular/get_initial_query.js | 3 +- .../angular/get_initial_refresh_config.js | 4 +- .../angular/get_initial_time_filters.js | 4 +- .../public/angular/listing_ng_wrapper.html | 0 .../plugins/maps/public/angular/map.html | 0 .../services/gis_map_saved_object_loader.js | 5 +- .../public/angular/services/saved_gis_map.js | 10 +- .../geometry_filter_form.test.js.snap | 0 .../layer_toc_actions.test.js.snap | 0 .../public/components/_geometry_filter.scss | 0 .../maps/public/components/_index.scss | 3 + .../public/components/_metric_editors.scss | 0 .../public/components/_tooltip_selector.scss | 0 .../components/distance_filter_form.tsx | 0 .../public/components/geo_field_with_index.ts | 0 .../public/components/geometry_filter_form.js | 0 .../components/geometry_filter_form.test.js | 0 .../components/global_filter_checkbox.js | 0 .../public/components/layer_toc_actions.js | 0 .../components/layer_toc_actions.test.js | 0 .../maps/public/components/map_listing.js | 4 +- .../multi_index_geo_field_select.tsx | 0 .../public/connected_components/_index.scss | 6 + .../gis_map/_gis_map.scss | 0 .../connected_components/gis_map/index.d.ts | 4 +- .../connected_components/gis_map/index.js | 8 +- .../connected_components/gis_map/view.js | 15 +- .../layer_addpanel/flyout_footer/index.js | 0 .../layer_addpanel/flyout_footer/view.js | 0 .../layer_addpanel/import_editor/index.js | 8 +- .../layer_addpanel/import_editor/view.js | 4 +- .../layer_addpanel/index.js | 8 +- .../layer_addpanel/source_editor/index.js | 4 +- .../layer_addpanel/source_editor/view.js | 0 .../layer_addpanel/source_select/_index.scss | 1 + .../source_select/_source_select.scss | 0 .../source_select/source_select.js | 4 +- .../layer_addpanel/view.js | 0 .../__snapshots__/view.test.js.snap | 0 .../layer_panel/_index.scss | 4 + .../layer_panel/_layer_panel.scss | 0 .../filter_editor/_filter_editor.scss | 0 .../filter_editor/filter_editor.js | 7 +- .../layer_panel/filter_editor/index.js | 0 .../layer_panel/flyout_footer/index.js | 4 +- .../layer_panel/flyout_footer/view.js | 0 .../connected_components/layer_panel/index.js | 0 .../layer_panel/join_editor/index.js | 0 .../metrics_expression.test.js.snap | 0 .../join_editor/resources/_join.scss | 0 .../layer_panel/join_editor/resources/join.js | 6 +- .../join_editor/resources/join_expression.js | 11 +- .../resources/metrics_expression.js | 4 +- .../resources/metrics_expression.test.js | 2 +- .../join_editor/resources/where_expression.js | 6 +- .../layer_panel/join_editor/view.js | 0 .../__snapshots__/layer_errors.test.js.snap | 0 .../layer_panel/layer_errors/index.js | 0 .../layer_panel/layer_errors/layer_errors.js | 0 .../layer_errors/layer_errors.test.js | 0 .../layer_panel/layer_settings/index.js | 2 +- .../layer_settings/layer_settings.js | 5 +- .../style_settings/_style_settings.scss | 0 .../layer_panel/style_settings/index.js | 0 .../style_settings/style_settings.js | 0 .../connected_components/layer_panel/view.js | 8 +- .../layer_panel/view.test.js | 0 .../feature_properties.test.js.snap | 0 .../__snapshots__/tooltip_header.test.js.snap | 0 .../map/features_tooltip/_index.scss | 0 .../feature_geometry_filter_form.js | 8 +- .../features_tooltip/feature_properties.js | 0 .../feature_properties.test.js | 0 .../map/features_tooltip/features_tooltip.js | 0 .../map/features_tooltip/tooltip_header.js | 0 .../features_tooltip/tooltip_header.test.js | 0 .../map/mb/draw_control/draw_circle.ts | 0 .../map/mb/draw_control/draw_control.js | 3 +- .../map/mb/draw_control/draw_tooltip.js | 0 .../map/mb/draw_control/index.js | 0 .../connected_components/map/mb/index.js | 4 +- .../map/mb/mb.utils.test.js | 0 .../tooltip_control.test.js.snap | 0 .../tooltip_popover.test.js.snap | 0 .../map/mb/tooltip_control/index.js | 0 .../map/mb/tooltip_control/tooltip_control.js | 0 .../tooltip_control/tooltip_control.test.js | 0 .../map/mb/tooltip_control/tooltip_popover.js | 0 .../tooltip_control/tooltip_popover.test.js | 0 .../connected_components/map/mb/utils.js | 94 ++ .../connected_components/map/mb/view.js | 16 +- .../toolbar_overlay/_index.scss | 2 +- .../toolbar_overlay/index.js | 0 .../toolbar_overlay/set_view_control/index.js | 0 .../set_view_control/set_view_control.js | 0 .../toolbar_overlay/toolbar_overlay.js | 0 .../__snapshots__/tools_control.test.js.snap | 0 .../toolbar_overlay/tools_control/_index.scss | 0 .../toolbar_overlay/tools_control/index.js | 0 .../tools_control/tools_control.js | 0 .../tools_control/tools_control.test.js | 0 .../widget_overlay/_index.scss | 6 + .../widget_overlay/_mixins.scss | 0 .../widget_overlay/_widget_overlay.scss | 0 .../__snapshots__/view.test.js.snap | 0 .../_attribution_control.scss | 0 .../attribution_control/index.js | 0 .../attribution_control/view.js | 0 .../attribution_control/view.test.js | 0 .../widget_overlay/index.js | 0 .../__snapshots__/view.test.js.snap | 0 .../widget_overlay/layer_control/_index.scss | 2 + .../layer_control/_layer_control.scss | 0 .../widget_overlay/layer_control/index.js | 4 +- .../layer_toc/__snapshots__/view.test.js.snap | 0 .../layer_control/layer_toc/index.js | 0 .../toc_entry/__snapshots__/view.test.js.snap | 0 .../layer_toc/toc_entry/_toc_entry.scss | 0 .../layer_toc/toc_entry/index.js | 4 +- .../layer_control/layer_toc/toc_entry/view.js | 0 .../layer_toc/toc_entry/view.test.js | 0 .../layer_control/layer_toc/view.js | 0 .../layer_control/layer_toc/view.test.js | 0 .../widget_overlay/layer_control/view.js | 0 .../widget_overlay/layer_control/view.test.js | 0 .../view_control/_view_control.scss | 0 .../widget_overlay/view_control/index.js | 0 .../view_control/view_control.js | 0 .../widget_overlay/widget_overlay.js | 0 .../plugins/maps/public/embeddable/README.md | 0 .../plugins/maps/public/embeddable/index.ts | 0 .../maps/public/embeddable/map_embeddable.tsx | 25 +- .../embeddable/map_embeddable_factory.ts | 136 +++ .../merge_input_with_saved_map.d.ts | 0 .../embeddable/merge_input_with_saved_map.js | 4 +- .../maps/public/feature_catalogue_entry.ts | 2 +- .../plugins/maps/public/help_menu_util.js | 4 +- .../{legacy => }/plugins/maps/public/icon.svg | 0 x-pack/plugins/maps/public/index.scss | 17 + x-pack/plugins/maps/public/layers/layer.js | 2 +- .../layers/sources/es_source/es_source.js | 2 +- .../maps/public/layers/sources/source.js | 1 - .../maps/public/maps_vis_type_alias.js | 41 + x-pack/plugins/maps/public/plugin.ts | 18 +- x-pack/plugins/maps/public/reducers/ui.ts | 1 - .../maps/public/selectors/map_selectors.d.ts | 4 +- .../maps/public/selectors/map_selectors.js | 46 +- .../public/selectors/map_selectors.test.js | 16 +- .../maps/public/selectors/ui_selectors.ts | 7 +- 209 files changed, 1617 insertions(+), 1749 deletions(-) delete mode 100644 x-pack/legacy/plugins/maps/common/constants.ts delete mode 100644 x-pack/legacy/plugins/maps/common/descriptor_types.ts delete mode 100644 x-pack/legacy/plugins/maps/common/get_join_key.ts delete mode 100644 x-pack/legacy/plugins/maps/common/i18n_getters.ts delete mode 100644 x-pack/legacy/plugins/maps/common/parse_xml_string.js delete mode 100644 x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts delete mode 100644 x-pack/legacy/plugins/maps/public/actions/map_actions.js delete mode 100644 x-pack/legacy/plugins/maps/public/actions/ui_actions.js delete mode 100644 x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js delete mode 100644 x-pack/legacy/plugins/maps/public/components/_index.scss delete mode 100644 x-pack/legacy/plugins/maps/public/connected_components/_index.scss delete mode 100644 x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_index.scss delete mode 100644 x-pack/legacy/plugins/maps/public/connected_components/layer_panel/_index.scss delete mode 100644 x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.d.ts delete mode 100644 x-pack/legacy/plugins/maps/public/connected_components/map/mb/utils.js delete mode 100644 x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/_index.scss delete mode 100644 x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss delete mode 100644 x-pack/legacy/plugins/maps/public/legacy_register_feature.ts delete mode 100644 x-pack/legacy/plugins/maps/public/register_vis_type_alias.js rename x-pack/{legacy => }/plugins/maps/common/migrations/add_field_meta_options.js (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/add_field_meta_options.test.js (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.js (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile.test.js (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/join_agg_key.test.ts (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/join_agg_key.ts (97%) rename x-pack/{legacy => }/plugins/maps/common/migrations/migrate_symbol_style_descriptor.js (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/migrate_symbol_style_descriptor.test.js (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/move_apply_global_query.js (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/move_apply_global_query.test.js (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/references.js (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/references.test.js (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/scaling_type.test.ts (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/scaling_type.ts (93%) rename x-pack/{legacy => }/plugins/maps/common/migrations/top_hits_time_to_sort.js (100%) rename x-pack/{legacy => }/plugins/maps/common/migrations/top_hits_time_to_sort.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/_main.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/_mapbox_hacks.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/actions/map_actions.test.js (98%) rename x-pack/{legacy => }/plugins/maps/public/actions/ui_actions.d.ts (54%) rename x-pack/{legacy => }/plugins/maps/public/angular/get_initial_layers.d.ts (100%) create mode 100644 x-pack/plugins/maps/public/angular/get_initial_layers.js rename x-pack/{legacy => }/plugins/maps/public/angular/get_initial_layers.test.js (80%) rename x-pack/{legacy => }/plugins/maps/public/angular/get_initial_query.js (82%) rename x-pack/{legacy => }/plugins/maps/public/angular/get_initial_refresh_config.js (84%) rename x-pack/{legacy => }/plugins/maps/public/angular/get_initial_time_filters.js (80%) rename x-pack/{legacy => }/plugins/maps/public/angular/listing_ng_wrapper.html (100%) rename x-pack/{legacy => }/plugins/maps/public/angular/map.html (100%) rename x-pack/{legacy => }/plugins/maps/public/angular/services/gis_map_saved_object_loader.js (79%) rename x-pack/{legacy => }/plugins/maps/public/angular/services/saved_gis_map.js (88%) rename x-pack/{legacy => }/plugins/maps/public/components/__snapshots__/geometry_filter_form.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/components/__snapshots__/layer_toc_actions.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/components/_geometry_filter.scss (100%) create mode 100644 x-pack/plugins/maps/public/components/_index.scss rename x-pack/{legacy => }/plugins/maps/public/components/_metric_editors.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/components/_tooltip_selector.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/components/distance_filter_form.tsx (100%) rename x-pack/{legacy => }/plugins/maps/public/components/geo_field_with_index.ts (100%) rename x-pack/{legacy => }/plugins/maps/public/components/geometry_filter_form.js (100%) rename x-pack/{legacy => }/plugins/maps/public/components/geometry_filter_form.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/components/global_filter_checkbox.js (100%) rename x-pack/{legacy => }/plugins/maps/public/components/layer_toc_actions.js (100%) rename x-pack/{legacy => }/plugins/maps/public/components/layer_toc_actions.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/components/map_listing.js (98%) rename x-pack/{legacy => }/plugins/maps/public/components/multi_index_geo_field_select.tsx (100%) create mode 100644 x-pack/plugins/maps/public/connected_components/_index.scss rename x-pack/{legacy => }/plugins/maps/public/connected_components/gis_map/_gis_map.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/gis_map/index.d.ts (73%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/gis_map/index.js (85%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/gis_map/view.js (92%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_addpanel/flyout_footer/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_addpanel/flyout_footer/view.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_addpanel/import_editor/index.js (76%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js (89%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_addpanel/index.js (85%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_addpanel/source_editor/index.js (75%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_addpanel/source_editor/view.js (100%) create mode 100644 x-pack/plugins/maps/public/connected_components/layer_addpanel/source_select/_index.scss rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_addpanel/source_select/_source_select.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js (90%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_addpanel/view.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/__snapshots__/view.test.js.snap (100%) create mode 100644 x-pack/plugins/maps/public/connected_components/layer_panel/_index.scss rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/_layer_panel.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/filter_editor/_filter_editor.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/filter_editor/filter_editor.js (96%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/filter_editor/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/flyout_footer/index.js (89%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/flyout_footer/view.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/join_editor/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/join_editor/resources/__snapshots__/metrics_expression.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/join_editor/resources/_join.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join.js (95%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join_expression.js (93%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.js (95%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.test.js (92%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/join_editor/resources/where_expression.js (94%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/join_editor/view.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/layer_errors/__snapshots__/layer_errors.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/layer_errors/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/layer_errors/layer_errors.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/layer_errors/layer_errors.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/layer_settings/index.js (94%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js (92%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/style_settings/_style_settings.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/style_settings/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/style_settings/style_settings.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/view.js (95%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/layer_panel/view.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/feature_properties.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/features_tooltip/__snapshots__/tooltip_header.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/features_tooltip/_index.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js (92%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/features_tooltip/feature_properties.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/features_tooltip/features_tooltip.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/features_tooltip/tooltip_header.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/features_tooltip/tooltip_header.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/draw_control/draw_circle.ts (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js (96%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/draw_control/draw_tooltip.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/draw_control/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/index.js (91%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/mb.utils.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tooltip_control.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/tooltip_control/__snapshots__/tooltip_popover.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/tooltip_control/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_control.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/tooltip_control/tooltip_popover.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/map/mb/view.js (94%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/toolbar_overlay/_index.scss (93%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/toolbar_overlay/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/toolbar_overlay/set_view_control/set_view_control.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/toolbar_overlay/toolbar_overlay.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/toolbar_overlay/tools_control/__snapshots__/tools_control.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/toolbar_overlay/tools_control/_index.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/toolbar_overlay/tools_control/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/toolbar_overlay/tools_control/tools_control.test.js (100%) create mode 100644 x-pack/plugins/maps/public/connected_components/widget_overlay/_index.scss rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/_mixins.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/_widget_overlay.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/attribution_control/__snapshots__/view.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/attribution_control/_attribution_control.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/__snapshots__/view.test.js.snap (100%) create mode 100644 x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/_layer_control.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/index.js (90%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/__snapshots__/view.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/__snapshots__/view.test.js.snap (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/_toc_entry.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/index.js (93%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/view.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/view.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/view.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/view.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/layer_control/view.test.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/view_control/_view_control.scss (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/view_control/index.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/view_control/view_control.js (100%) rename x-pack/{legacy => }/plugins/maps/public/connected_components/widget_overlay/widget_overlay.js (100%) rename x-pack/{legacy => }/plugins/maps/public/embeddable/README.md (100%) rename x-pack/{legacy => }/plugins/maps/public/embeddable/index.ts (100%) rename x-pack/{legacy => }/plugins/maps/public/embeddable/map_embeddable.tsx (90%) create mode 100644 x-pack/plugins/maps/public/embeddable/map_embeddable_factory.ts rename x-pack/{legacy => }/plugins/maps/public/embeddable/merge_input_with_saved_map.d.ts (100%) rename x-pack/{legacy => }/plugins/maps/public/embeddable/merge_input_with_saved_map.js (89%) rename x-pack/{legacy => }/plugins/maps/public/feature_catalogue_entry.ts (89%) rename x-pack/{legacy => }/plugins/maps/public/help_menu_util.js (81%) rename x-pack/{legacy => }/plugins/maps/public/icon.svg (100%) create mode 100644 x-pack/plugins/maps/public/index.scss create mode 100644 x-pack/plugins/maps/public/maps_vis_type_alias.js rename x-pack/{legacy => }/plugins/maps/public/selectors/map_selectors.d.ts (81%) rename x-pack/{legacy => }/plugins/maps/public/selectors/map_selectors.js (82%) rename x-pack/{legacy => }/plugins/maps/public/selectors/map_selectors.test.js (62%) rename x-pack/{legacy => }/plugins/maps/public/selectors/ui_selectors.ts (74%) 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 98945653c25dc..0000000000000 --- 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/descriptor_types.ts b/x-pack/legacy/plugins/maps/common/descriptor_types.ts deleted file mode 100644 index 1f0eda26e7f7d..0000000000000 --- a/x-pack/legacy/plugins/maps/common/descriptor_types.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/descriptor_types'; 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 004f12ca08d2e..0000000000000 --- 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 f9d186dea2e2b..0000000000000 --- 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 34ec144472828..0000000000000 --- 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 f4e01efc05f45..8546e3712c763 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 a8e69eef7a02f..d3666025082b7 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 34f8c30b51874..0000000000000 --- 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 7bfbf5761c5b8..0000000000000 --- 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 33ab2fd74122a..0000000000000 --- 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 686259aeaaba4..0000000000000 --- 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 6bc8a4d0be5ac..9522fd12ad37d 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 0b32719442424..0000000000000 --- 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 99a2e222ea6c1..0000000000000 --- 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 7fe1396fcca16..0000000000000 --- 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 fd074edf032fa..0000000000000 --- 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 cf4fdc7be70c6..0000000000000 --- 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 a1d1341b7c4f7..0000000000000 --- 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 cc1ab35039dac..0000000000000 --- 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 761ef9d17b4c2..0000000000000 --- 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 96c3baf634a83..90b17412377f5 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 b2ac514299d80..b2a228f01b921 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 8555594e909d3..98db26859297b 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 96d9e09c1d09a..bcbfca17755fb 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 00f788f267d4b..0000000000000 --- 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 71f1a30c1fbef..0123e32b6d3b9 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 9dc07bcb5dc0e..0000000000000 --- 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 c082e0e1352c0..70c1c4a50efd4 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 c5522b7ba21c5..e2a758075155a 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 01a1ba2703cba..c152f5bfffc31 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 652bb83a0d781..d34e306d1fff9 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 27c0211446e85..4610baabad3fe 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 25c552433e9f8..79f3dcf76b82e 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 20e022001577a..d49f9827e3ea0 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/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 29661aedb550c..97b9ee4692c25 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 551975fbacea5..98a06a764f4ec 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 b2aec30c113eb..00c5e70ad6b8d 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 13cb3d5f89860..572385d628b16 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 7e2a3c827fa88..c280b8af7ab80 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 233918847de08..e087dc70256f0 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 59ae56c15056a..77fdf6b0f12d2 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 0000000000000..1eb5dac309f28 --- /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 8c9185a16ea0e..f41ed26b2a05d 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 c50ecb2b05dc0..4f61142413671 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 8735d45debfc4..f13e435cd1d5c 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 74fbf603e99f5..75d9f0e95ccf0 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 710997a9c0d7f..2dcec35960b08 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 990a0613da681..1c47e0ab7dc2a 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 0000000000000..161b3fefdb8f9 --- /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 ef1d524cb91dd..ee10fe30130f3 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 0000000000000..83042ae1d586c --- /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 8689d88297171..92d92dfbd142d 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 2d8265bae9387..c825fdab75ca7 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 06097ebea1900..28ad12133d611 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 e8192795f98ae..bff235a7d27fc 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 cb20d80733c33..a4fa0d492bf3f 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 c4e2fa5169b0f..24c1f5ced4fe6 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 553e54ee89766..8937f32d3bf05 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 0000000000000..8ae6970315e13 --- /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 67cc17ebaa224..80b05a0fd015b 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 0000000000000..41b4826a02c67 --- /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 40fdac38493d4..fba2ec05d0b1d 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 287f0019f18ec..621ce209eb982 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 9c4e1cfdb5467..0d26354e2449b 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 73600c81d221e..12ca2f3c514a0 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 c6a79a398f9af..8c83743ac4c96 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 d8bf862249448..3cd8a3c42879a 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 54ec0ac46fa3d..7c9b4f7b7b9a4 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 e8f980bbbf2b4..e2f22c584d3b3 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 1d352913e54a3..168c735ab7a6c 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 2521318f0b3c9..f8b7c417e67fd 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 15824b82965e8..b103fb43af97c 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 cc0e665525036..d20faa39d6492 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 350cb7028abee..d864b60eb433b 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 15aacfbf1f38d..7be2cd9e67084 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 a36e1d7048e92..2d95de184f0f4 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 01aea403b27f0..2754a3e204263 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 0000000000000..5e5086bed2763 --- /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 0000000000000..9a3e3a45d6c4e --- /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 04de5f71f5bfc..8780bac59e4b7 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 588445d0b4992..ca3c6d325687d 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/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 b8e4c84ad56a1..dbd48d614e99b 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 0000000000000..a99fe63c5f386 --- /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 8e3e0a9168e30..d91c91b3b223c 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 fdda76b4e1212..6c2579bd3e4e2 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 70b9340b562cd..053caf6688309 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 0000000000000..8b2f6d3cb6156 --- /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 19dcbaf1dfcfd..9362ce2c028e6 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_source/es_source.js b/x-pack/plugins/maps/public/layers/sources/es_source/es_source.js index 3402e367cbd73..9ab87577b7780 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 555b8999d6284..fd93daf249b26 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/maps_vis_type_alias.js b/x-pack/plugins/maps/public/maps_vis_type_alias.js new file mode 100644 index 0000000000000..85613f4608c6f --- /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 d3b9626dc8366..0b076621326ce 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 7429545ec0e46..f577618c74ffe 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 8c99e0adcc14f..32579d036590e 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 1e71025935519..c7073efa96cd5 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 72cc748617540..fec16251914ea 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 fdf2a8ea0e4f3..32d4beeb381d7 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; From 2caed9b7be951cd65c1cb84ab2f1150e6ee57503 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Mon, 20 Apr 2020 17:43:48 -0600 Subject: [PATCH 26/34] [SIEM] Adds recursive exact key checks for validation and formatter ## Summary * Adds exact check to cause failures whenever a schema is using t.exact({}) and that schema has extra keys at any level of the schema hierarchy. * Fixes the exact check to work with unknown in a safe way and only do recursive checks for keys only when the unknown is an Object. * Changes the output to use a format error mechanism which pushes all the errors onto one line to be consistent with the response errors. We can change this as a team to whatever we want I just put it to a comma separator as that is what the responses type checks were using downstream. * Moves the downstream code up higher to be used within SIEM for timeline and detection engine. * Adds tests in TDD/red light/green light fashion where I fail the extra key checks first and then pass them second. ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios --- .../detection_engine/routes/rules/validate.ts | 4 +- .../schemas/response/__mocks__/utils.ts | 37 ----------- .../response/check_type_dependents.test.ts | 10 +-- .../schemas/response/error_schema.test.ts | 5 +- .../response/find_rules_schema.test.ts | 10 +-- .../response/import_rules_schema.test.ts | 27 ++++++-- .../response/prepackaged_rules_schema.test.ts | 4 +- .../prepackaged_rules_status_schema.test.ts | 4 +- .../response/rules_bulk_schema.test.ts | 10 +-- .../schemas/response/rules_schema.test.ts | 5 +- .../type_timeline_only_schema.test.ts | 4 +- .../schemas/types/iso_date_string.test.ts | 2 +- .../schemas/types/lists_default_array.test.ts | 2 +- ...positive_integer_greater_than_zero.test.ts | 2 +- .../schemas/types/postive_integer.test.ts | 2 +- .../types/references_default_array.test.ts | 2 +- .../routes/schemas/types/risk_score.test.ts | 2 +- .../routes/schemas/types/uuid.test.ts | 2 +- .../routes/export_timelines_route.test.ts | 8 +-- .../routes/import_timelines_route.test.ts | 5 +- .../utils/build_validation/__mocks__/utils.ts | 43 +++++++++++++ .../build_validation}/exact_check.test.ts | 21 +++---- .../build_validation}/exact_check.ts | 59 ++++++++--------- .../build_validation/format_errors.test.ts} | 11 +--- .../build_validation/format_errors.ts} | 0 .../build_validation/route_validation.test.ts | 63 ++++++++++++++++--- .../build_validation/route_validation.ts | 6 +- 27 files changed, 199 insertions(+), 151 deletions(-) create mode 100644 x-pack/plugins/siem/server/utils/build_validation/__mocks__/utils.ts rename x-pack/plugins/siem/server/{lib/detection_engine/routes/schemas/response => utils/build_validation}/exact_check.test.ts (94%) rename x-pack/plugins/siem/server/{lib/detection_engine/routes/schemas/response => utils/build_validation}/exact_check.ts (59%) rename x-pack/plugins/siem/server/{lib/detection_engine/routes/schemas/response/utils.test.ts => utils/build_validation/format_errors.test.ts} (94%) rename x-pack/plugins/siem/server/{lib/detection_engine/routes/schemas/response/utils.ts => utils/build_validation/format_errors.ts} (100%) 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 c207d075331b6..cfba40fc225a2 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 21f18f9db55fb..fef6bcf42e49f 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 0eda2a7a13d96..fbd2382e2826d 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 11d8b85f25920..6e159a792edb6 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 f5c1970ee8c55..68b67db595d76 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 ce4bbf420a634..b0b863ebbbc0b 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 46667826416e1..827167c63fd58 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 1c270ff402f75..a864667583c0a 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 8dc97d727c4d1..9a7cf5e2c2871 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 4bfc51c1a66aa..82a6682e6461f 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 68a3c8b303823..85fb124464487 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 fbafaf7f52ecb..ff62ea4443f3f 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 e8a9c7b0886a1..2a97c8a4a143e 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 bc17303f24203..d6f21681df88f 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 cee451279663a..26441745a7f29 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 3ae8415b4f170..76f722274ce23 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 ab3f80944489f..76e3445358dd9 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 342e6f2db2e16..7b68dbcef2d7e 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/timeline/routes/export_timelines_route.test.ts b/x-pack/plugins/siem/server/lib/timeline/routes/export_timelines_route.test.ts index 47ca25e16bd50..2bccb7c393837 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 3931bf0e5bea5..9f41943cfa27f 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 0000000000000..578972dda5aef --- /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 cae4365d06856..1e70deaeed438 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 6fa0472950189..9484765f9973d 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 c1eb32be4895c..f9dd9e76a1d9c 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 d17a8457ff81b..866dbe9480ca5 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 bfcd0998fe690..30b95dcfa94ee 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) ) ); From f76669b17ad14cf22a9e6136402de9dfc0ea48dc Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Mon, 20 Apr 2020 16:52:02 -0700 Subject: [PATCH 27/34] Fix page layouts, clean up unused code (#63992) --- .../ingest_manager/components/header.tsx | 6 +++--- .../applications/ingest_manager/layouts/default.tsx | 2 -- .../ingest_manager/layouts/with_header.tsx | 12 ++++++++---- .../ingest_manager/layouts/without_header.tsx | 2 -- .../create_datasource_page/components/layout.tsx | 6 ++---- .../agent_config/create_datasource_page/index.tsx | 9 +-------- .../agent_config/create_datasource_page/types.ts | 1 - 7 files changed, 14 insertions(+), 24 deletions(-) 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 1aab6d901a992..ceb87fb048ae3 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, }) => ( - + props.theme.eui.euiHeaderChildSize}); background: ${props => props.theme.eui.euiColorEmptyShade}; - display: flex; - flex-direction: column; `; const Nav = styled.nav` 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 bb867718204b2..d5ce5e17ad84e 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 @@ -10,18 +10,22 @@ import { Header, HeaderProps } from '../components'; const Page = styled(EuiPage)` background: ${props => props.theme.eui.euiColorEmptyShade}; - flex: 1; - align-items: flex-start; `; 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/layouts/without_header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/without_header.tsx index 9f9fa03942c09..cad98c5a0a7e1 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/without_header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/without_header.tsx @@ -9,8 +9,6 @@ import { EuiPage, EuiPageBody, EuiSpacer } from '@elastic/eui'; const Page = styled(EuiPage)` background: ${props => props.theme.eui.euiColorEmptyShade}; - flex: 1; - align-items: flex-start; `; interface Props { 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 73a7ba8ec119d..39d882f7fdf65 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 1ad579d591b21..3c79fe17fdbb5 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 bd05be2d8a558..85cc758fc4c46 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'; From f6f610db471f2585dedccf5d0fa28f751079d38f Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Tue, 21 Apr 2020 07:43:35 +0200 Subject: [PATCH 28/34] kbn/config-schema: Consider maybe properties as optional keys in ObjectType (#63838) * consider optional properties as optional keys in ObjectType * fix type on security config * fix ObjectTypeOptions --- .../src/types/object_type.test.ts | 24 +++++++++++++++++++ .../src/types/object_type.ts | 23 ++++++++++++++---- .../server/http/router/validator/validator.ts | 17 +++++++++---- 3 files changed, 55 insertions(+), 9 deletions(-) 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 47a0f5f7a5491..5ab59d1c02077 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 5a50e714a5931..fee2d02c1bfb9 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/src/core/server/http/router/validator/validator.ts b/src/core/server/http/router/validator/validator.ts index 6c766e69f0f37..a2299b47ae253 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; } /** From d6fdb689e83330795dbae3640224c0a23091be43 Mon Sep 17 00:00:00 2001 From: Ben Skelker <54019610+benskelker@users.noreply.github.com> Date: Tue, 21 Apr 2020 09:26:06 +0300 Subject: [PATCH 29/34] [Docs]Adds saved object key setting to load balancing kib instances (#63935) * adds saved object key setting to load balancing kib instances * SIEM section doc updates * Revert "SIEM section doc updates" - wrong PR This reverts commit fc55d90a6d1fe30b2f9f06e3bf6b7efee52c84d5. * corrections --- docs/setup/production.asciidoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/setup/production.asciidoc b/docs/setup/production.asciidoc index eef2b11e53d85..19f9d64d13623 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: From 5adccd005b107a64acf732b178109ec4fc61e6d9 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 21 Apr 2020 08:37:02 +0200 Subject: [PATCH 30/34] follow conventions for saved object definitions (#63571) --- .../server/saved_objects/index.ts | 20 +++++++++++++++++++ .../tsvb_telemetry.ts} | 0 .../validation_telemetry_service.ts | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/plugins/vis_type_timeseries/server/saved_objects/index.ts rename src/plugins/vis_type_timeseries/server/{validation_telemetry/saved_object_type.ts => saved_objects/tsvb_telemetry.ts} (100%) 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 0000000000000..5f7f5767f423d --- /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 779d9441df2fd..e4b8ca19094e4 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; From 46f6f8779e7564778042578b4ecfbe91ab12713b Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Tue, 21 Apr 2020 09:55:06 +0300 Subject: [PATCH 31/34] Enable include/exclude in Terms agg for numeric fields (#59425) * Enable include/exclude in Terms agg for numeric fields Closes #4576 * Added a new component that allows adding multiple values * Added some validation to include/exclude fields * Removed unnecessary comments and accepted API changes * Fixed i18n ID issue * Refactored some code and fixed discard button issue * Added SimpleNumberList component and value parsing in include_exclude.tsx * Fixed merge conflict * Fixed merge conflict * Refactored some code * Got rid of lodash isArray, added Number.isFinite where needed and changed symbol of string join and array split * Added some more test cases to cover migrate_include_exclude_format write method Co-authored-by: Elastic Machine --- src/plugins/data/public/index.ts | 2 + src/plugins/data/public/public.api.md | 33 +++-- .../buckets/migrate_include_exclude_format.ts | 15 +- .../public/search/aggs/buckets/terms.test.ts | 60 ++++++++ .../data/public/search/aggs/buckets/terms.ts | 9 +- .../public/components/agg_params_map.ts | 2 + .../controls/components/number_list/utils.ts | 2 +- .../components/simple_number_list.tsx | 140 ++++++++++++++++++ .../components/controls/include_exclude.tsx | 49 ++++++ .../public/components/controls/index.ts | 1 + 10 files changed, 289 insertions(+), 24 deletions(-) create mode 100644 src/plugins/vis_default_editor/public/components/controls/components/simple_number_list.tsx create mode 100644 src/plugins/vis_default_editor/public/components/controls/include_exclude.tsx diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index b62b728beca35..05a4141483587 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 427c4f7864554..6383f61864146 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 0beeb1c372275..116f8cfad60f6 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 0dc052bd1fdf6..9769efb6da749 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 5baa38af0e8d6..698e0dfb1d340 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/vis_default_editor/public/components/agg_params_map.ts b/src/plugins/vis_default_editor/public/components/agg_params_map.ts index 5af3cfc5b0928..9bc3146b9903b 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 dac86249ebbb9..5cb594ade8dba 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 0000000000000..becf8e47ef573 --- /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 0000000000000..f60f6ce7ce249 --- /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 e8944aa667853..cfb236e5e22e3 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'; From 2f363ba14482d2734be8ff65c7813abb0dfb69cb Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 21 Apr 2020 09:49:48 +0200 Subject: [PATCH 32/34] Migrate vis_type_table to kibana/new platform (#63105) * Move vis_type_table to Kibana Platform * Adapt mocha tests * Adapt CODEOWNERS * Adapt SCSS --- .github/CODEOWNERS | 1 + .i18nrc.json | 2 +- .../__tests__/vis_type_table}/agg_table.js | 16 ++- .../vis_type_table}/agg_table_group.js | 15 ++- .../__tests__/vis_type_table}/legacy.ts | 18 +-- .../vis_type_table}/tabified_data.js | 0 .../core_plugins/vis_type_table/index.ts | 44 ------- .../core_plugins/vis_type_table/package.json | 4 - .../public/agg_table/_index.scss | 1 - .../public/paginated_table/_index.scss | 1 - .../vis_type_table/public/table_vis_type.ts | 98 ---------------- .../vis_type_table/public/vis_controller.ts | 104 ----------------- .../public/angular/angular_config.tsx | 6 +- .../vis_type_table/config.ts} | 8 +- src/plugins/vis_type_table/kibana.json | 11 ++ .../__snapshots__/table_vis_fn.test.ts.snap | 0 .../vis_type_table/public/_table_vis.scss | 0 .../public/agg_table/_agg_table.scss | 0 .../public/agg_table/_index.scss | 1 + .../public/agg_table/agg_table.html | 0 .../public/agg_table/agg_table.js | 4 +- .../public/agg_table/agg_table_group.html | 0 .../public/agg_table/agg_table_group.js | 0 .../public/components/table_vis_options.tsx | 8 +- .../vis_type_table/public/components/utils.ts | 0 .../public/get_inner_angular.ts | 8 +- .../vis_type_table/public/index.scss | 6 +- .../vis_type_table/public/index.ts | 4 +- .../public/paginated_table/_index.scss | 1 + .../paginated_table/_table_cell_filter.scss | 0 .../paginated_table/paginated_table.html | 0 .../public/paginated_table/paginated_table.js | 0 .../paginated_table/paginated_table.test.ts | 11 +- .../public/paginated_table/rows.js | 0 .../paginated_table/table_cell_filter.html | 0 .../vis_type_table/public/plugin.ts | 17 +-- .../vis_type_table/public/services.ts | 4 +- .../vis_type_table/public/table_vis.html | 0 .../public/table_vis_controller.js | 0 .../public/table_vis_controller.test.ts | 31 +++-- .../public/table_vis_fn.test.ts | 2 +- .../vis_type_table/public/table_vis_fn.ts | 6 +- .../public/table_vis_legacy_module.ts | 0 .../public/table_vis_response_handler.ts | 0 .../vis_type_table/public/table_vis_type.ts | 100 ++++++++++++++++ .../vis_type_table/public/types.ts | 2 +- .../vis_type_table/public/vis_controller.ts | 109 ++++++++++++++++++ src/plugins/vis_type_table/server/index.ts | 34 ++++++ 48 files changed, 354 insertions(+), 323 deletions(-) rename src/legacy/core_plugins/{vis_type_table/public/agg_table/__tests__ => kibana/public/__tests__/vis_type_table}/agg_table.js (94%) rename src/legacy/core_plugins/{vis_type_table/public/agg_table/__tests__ => kibana/public/__tests__/vis_type_table}/agg_table_group.js (81%) rename src/legacy/core_plugins/{vis_type_table/public => kibana/public/__tests__/vis_type_table}/legacy.ts (69%) rename src/legacy/core_plugins/{vis_type_table/public/agg_table/__tests__ => kibana/public/__tests__/vis_type_table}/tabified_data.js (100%) delete mode 100644 src/legacy/core_plugins/vis_type_table/index.ts delete mode 100644 src/legacy/core_plugins/vis_type_table/package.json delete mode 100644 src/legacy/core_plugins/vis_type_table/public/agg_table/_index.scss delete mode 100644 src/legacy/core_plugins/vis_type_table/public/paginated_table/_index.scss delete mode 100644 src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts delete mode 100644 src/legacy/core_plugins/vis_type_table/public/vis_controller.ts rename src/{legacy/core_plugins/vis_type_table/public/legacy_imports.ts => plugins/vis_type_table/config.ts} (79%) create mode 100644 src/plugins/vis_type_table/kibana.json rename src/{legacy/core_plugins => plugins}/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/_table_vis.scss (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/agg_table/_agg_table.scss (100%) create mode 100644 src/plugins/vis_type_table/public/agg_table/_index.scss rename src/{legacy/core_plugins => plugins}/vis_type_table/public/agg_table/agg_table.html (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/agg_table/agg_table.js (98%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/agg_table/agg_table_group.html (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/agg_table/agg_table_group.js (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/components/table_vis_options.tsx (96%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/components/utils.ts (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/get_inner_angular.ts (91%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/index.scss (61%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/index.ts (92%) create mode 100644 src/plugins/vis_type_table/public/paginated_table/_index.scss rename src/{legacy/core_plugins => plugins}/vis_type_table/public/paginated_table/_table_cell_filter.scss (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/paginated_table/paginated_table.html (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/paginated_table/paginated_table.js (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/paginated_table/paginated_table.test.ts (98%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/paginated_table/rows.js (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/paginated_table/table_cell_filter.html (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/plugin.ts (80%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/services.ts (86%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/table_vis.html (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/table_vis_controller.js (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/table_vis_controller.test.ts (89%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/table_vis_fn.test.ts (95%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/table_vis_fn.ts (94%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/table_vis_legacy_module.ts (100%) rename src/{legacy/core_plugins => plugins}/vis_type_table/public/table_vis_response_handler.ts (100%) create mode 100644 src/plugins/vis_type_table/public/table_vis_type.ts rename src/{legacy/core_plugins => plugins}/vis_type_table/public/types.ts (94%) create mode 100644 src/plugins/vis_type_table/public/vis_controller.ts create mode 100644 src/plugins/vis_type_table/server/index.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ab05b32ab063e..c32bf8cbaa1c3 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 4a516f23ebf05..d4286a7bd50e0 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/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 a23407a599ae2..b212ecf578dd1 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 40a0993ccb017..3cd7de393d66a 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 3d5f8c1b3efe9..c6467a5beae68 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/vis_type_table/index.ts b/src/legacy/core_plugins/vis_type_table/index.ts deleted file mode 100644 index 04ca9da7de32b..0000000000000 --- 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 2809b0e047836..0000000000000 --- 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 b19d4a887a7f3..0000000000000 --- 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 9473b847d3c2b..0000000000000 --- 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 43816121bc23b..0000000000000 --- 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 5bb730d2f9b10..0000000000000 --- 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/plugins/kibana_legacy/public/angular/angular_config.tsx b/src/plugins/kibana_legacy/public/angular/angular_config.tsx index 71cd57ef2d72e..295cf27688c80 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/legacy/core_plugins/vis_type_table/public/legacy_imports.ts b/src/plugins/vis_type_table/config.ts similarity index 79% rename from src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts rename to src/plugins/vis_type_table/config.ts index 1030e971d6450..6749bd83de39f 100644 --- a/src/legacy/core_plugins/vis_type_table/public/legacy_imports.ts +++ b/src/plugins/vis_type_table/config.ts @@ -17,4 +17,10 @@ * under the License. */ -export { npSetup, npStart } from 'ui/new_platform'; +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 0000000000000..bb0f6478a4240 --- /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 0000000000000..340e08a76f1bd --- /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 b9e79f96e4fc1..0cd501e2d0344 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 265528f33f9cd..68348d5ef1060 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 6208e358b4184..d69b9bba31b03 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 54124ebc42620..0972c85e0dbe0 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 efbaf69659ea2..5621fdb094772 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 0000000000000..23d56c09b2818 --- /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 7352236f03feb..23e4aee0378dc 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 ea12a5320a14d..a41d939523bcc 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 b4b491ac7a555..3aaffe75e27f1 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 8d6f88bf8dd4a..4607324ca150c 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 36392c10f93f3..9accf8950d910 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 a97e596e89754..9739a7a284e6c 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 0000000000000..26e5ac8cfd71a --- /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 c6de14b9f050c..39023d1305cb6 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 0000000000000..d49dd32c8c89c --- /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 0000000000000..882958a28777d --- /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() {}, +}); From 1deb5d63eb4c941cc2c318a1417ea0c0efa659d8 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 21 Apr 2020 12:31:05 +0200 Subject: [PATCH 33/34] Move authz lib out of snapshot restore (#63947) Created the first entry for the new __packages_do_no_import__ folder that will house ESUIs private packages. --- .../components/authorization_provider.tsx | 73 +++++++++++++++++++ .../authorization/components/index.ts | 30 ++++++++ .../components/not_authorized_section.tsx | 30 ++++++++ .../components/section_error.tsx | 19 ++++- .../components/with_privileges.tsx | 26 +++++-- .../authorization/index.ts | 30 ++++++++ .../authorization/types.ts | 27 +++++++ .../public/authorization/index.ts | 20 +++++ src/plugins/es_ui_shared/public/index.ts | 12 +++ .../snapshot_restore/common/types/index.ts | 1 - .../common/types/privileges.ts | 14 ---- .../public/application/app.tsx | 15 ++-- .../public/application/app_providers.tsx | 7 +- .../public/application/components/index.ts | 1 - .../policy_form/steps/step_logistics.tsx | 4 +- .../components/repository_form/step_one.tsx | 5 +- .../repository_form/type_settings/index.tsx | 4 +- .../components/authorization_provider.tsx | 47 ------------ .../lib/authorization/components/index.ts | 11 --- .../components/not_authorized_section.tsx | 17 ----- .../application/lib/authorization/index.ts | 7 -- .../policy_details/policy_details.tsx | 3 +- .../sections/home/policy_list/policy_list.tsx | 12 ++- .../policy_list/policy_table/policy_table.tsx | 2 +- .../repository_details/repository_details.tsx | 7 +- .../home/repository_list/repository_list.tsx | 3 +- .../repository_table/repository_table.tsx | 5 +- .../home/restore_list/restore_list.tsx | 11 ++- .../snapshot_details/snapshot_details.tsx | 8 +- .../home/snapshot_list/snapshot_list.tsx | 6 +- .../snapshot_table/snapshot_table.tsx | 2 +- .../sections/policy_add/policy_add.tsx | 6 +- .../sections/policy_edit/policy_edit.tsx | 3 +- .../repository_add/repository_add.tsx | 4 +- .../repository_edit/repository_edit.tsx | 3 +- .../restore_snapshot/restore_snapshot.tsx | 3 +- .../application/services/http/use_request.ts | 5 +- .../snapshot_restore/public/shared_imports.ts | 6 ++ .../snapshot_restore/server/routes/api/app.ts | 5 +- 39 files changed, 335 insertions(+), 159 deletions(-) create mode 100644 src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/authorization_provider.tsx create mode 100644 src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/index.ts create mode 100644 src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/not_authorized_section.tsx rename {x-pack/plugins/snapshot_restore/public/application => src/plugins/es_ui_shared/__packages_do_not_import__/authorization}/components/section_error.tsx (54%) rename {x-pack/plugins/snapshot_restore/public/application/lib => src/plugins/es_ui_shared/__packages_do_not_import__}/authorization/components/with_privileges.tsx (70%) create mode 100644 src/plugins/es_ui_shared/__packages_do_not_import__/authorization/index.ts create mode 100644 src/plugins/es_ui_shared/__packages_do_not_import__/authorization/types.ts create mode 100644 src/plugins/es_ui_shared/public/authorization/index.ts delete mode 100644 x-pack/plugins/snapshot_restore/common/types/privileges.ts delete mode 100644 x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/authorization_provider.tsx delete mode 100644 x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/index.ts delete mode 100644 x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/not_authorized_section.tsx delete mode 100644 x-pack/plugins/snapshot_restore/public/application/lib/authorization/index.ts 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 0000000000000..68f1cf2045efb --- /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 0000000000000..71be3cc6152ca --- /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 0000000000000..c35f674ef9ec4 --- /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 bd9e48796779e..3d56309adae97 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 223a2882c3cab..8f4b2b976d141 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 0000000000000..ad89052b3bb54 --- /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 0000000000000..cdc2052122688 --- /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/plugins/es_ui_shared/public/authorization/index.ts b/src/plugins/es_ui_shared/public/authorization/index.ts new file mode 100644 index 0000000000000..3a02c0d2694f3 --- /dev/null +++ b/src/plugins/es_ui_shared/public/authorization/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from '../../__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 a0371bf351193..7e5510d7c9c65 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/x-pack/plugins/snapshot_restore/common/types/index.ts b/x-pack/plugins/snapshot_restore/common/types/index.ts index 5cb3839fa9e01..d52584ca737a2 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 bf710b8225599..0000000000000 --- 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 77ef697814b2c..350d8aec711ed 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 e2732c0051337..3ca25b7d32dba 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 a7038ebd71578..f5bb892389870 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 f2d4e2bd74598..105f0601e3dfb 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 3b4c9d595b9f2..34bc06343a780 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 75295a1205cef..1e54868397a6d 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 d32fe29cc1dfa..0000000000000 --- 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 ac77aa5268660..0000000000000 --- 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 3fc13245708e8..0000000000000 --- 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/lib/authorization/index.ts b/x-pack/plugins/snapshot_restore/public/application/lib/authorization/index.ts deleted file mode 100644 index 73bbde465146c..0000000000000 --- a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/index.ts +++ /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 * from './components'; 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 f3110199ee17c..03d381c9f3aa3 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 0122e25e5e165..51297038b0f3f 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 7f9c5c5af7705..ba28bcddf5347 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 d293f194f647a..9932f14664076 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 6fa12537e9d6f..2256fa5991dec 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 7c0438f6b837f..bf2643d78bca1 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 da9ce3b124a11..0e3d9363d0535 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 d16545debe1ec..1943762a3c36e 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 fe99ccb6f596c..30e4c771644bc 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 ad64dcc7adcfe..427c241970007 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 4eb0f54978d09..6d1a432be7f9f 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 9ca7eba5c4eeb..0f1473fc05492 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 126e04bc7dc1d..08bfde833c368 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 aa29b8b9f0551..95f8b9b8bde7d 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 252fd07a85f80..9eabed8341ee0 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 200d601fd2ce9..27a565ccb74bc 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 7e7ef09d0c09d..e0024ea8e0c12 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 5d334fddc144b..bda64fdb66571 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'; From 34cb91ad5d8d659a1f4ecc3a09fb090f43de2d68 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 22 Apr 2020 13:03:56 +0200 Subject: [PATCH 34/34] [Ingest Node Pipelines] Clone Pipeline (#64049) * First iteration of clone functionality Wired up for both the list table and the details flyout in the list section. * satisfy eslint * Turn on sorting for the list table * Clean up const declarations * Address PR feedback Sentence-casify and update some other copy. * Mark edit and delete as primary actions in list table * Handle URI encoded chars in pipeline name when cloning --- .../public/application/app.tsx | 3 +- .../public/application/sections/index.ts | 2 + .../sections/pipelines_clone/index.ts | 7 ++ .../pipelines_clone/pipelines_clone.tsx | 59 +++++++++++ .../pipelines_create/pipelines_create.tsx | 13 ++- .../sections/pipelines_list/details.tsx | 100 +++++++++++++++--- .../sections/pipelines_list/main.tsx | 8 +- .../sections/pipelines_list/table.tsx | 18 ++++ 8 files changed, 194 insertions(+), 16 deletions(-) create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx diff --git a/x-pack/plugins/ingest_pipelines/public/application/app.tsx b/x-pack/plugins/ingest_pipelines/public/application/app.tsx index 87fe55eae91ec..1027d08c133db 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/app.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/app.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { HashRouter, Switch, Route } from 'react-router-dom'; import { BASE_PATH } from '../../common/constants'; -import { PipelinesList, PipelinesCreate, PipelinesEdit } from './sections'; +import { PipelinesList, PipelinesCreate, PipelinesEdit, PipelinesClone } from './sections'; export const App = () => { return ( @@ -20,6 +20,7 @@ export const App = () => { export const AppWithoutRouter = () => ( + diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/index.ts b/x-pack/plugins/ingest_pipelines/public/application/sections/index.ts index fde6106b508db..b2925666c5768 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/index.ts +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/index.ts @@ -9,3 +9,5 @@ export { PipelinesList } from './pipelines_list'; export { PipelinesCreate } from './pipelines_create'; export { PipelinesEdit } from './pipelines_edit'; + +export { PipelinesClone } from './pipelines_clone'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/index.ts b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/index.ts new file mode 100644 index 0000000000000..614a3598d407d --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PipelinesClone } from './pipelines_clone'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx new file mode 100644 index 0000000000000..b3b1217caf834 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent, useEffect } from 'react'; +import { RouteComponentProps } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { SectionLoading, useKibana } from '../../../shared_imports'; + +import { PipelinesCreate } from '../pipelines_create'; + +export interface ParamProps { + sourceName: string; +} + +/** + * This section is a wrapper around the create section where we receive a pipeline name + * to load and set as the source pipeline for the {@link PipelinesCreate} form. + */ +export const PipelinesClone: FunctionComponent> = props => { + const { sourceName } = props.match.params; + const { services } = useKibana(); + + const { error, data: pipeline, isLoading, isInitialRequest } = services.api.useLoadPipeline( + decodeURIComponent(sourceName) + ); + + useEffect(() => { + if (error && !isLoading) { + services.notifications!.toasts.addError(error, { + title: i18n.translate('xpack.ingestPipelines.clone.loadSourcePipelineErrorTitle', { + defaultMessage: 'Cannot load {name}.', + values: { name: sourceName }, + }), + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [error, isLoading]); + + if (isLoading && isInitialRequest) { + return ( + + + + ); + } else { + // We still show the create form even if we were not able to load the + // latest pipeline data. + const sourcePipeline = pipeline ? { ...pipeline, name: `${pipeline.name}-copy` } : undefined; + return ; + } +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx index 452b0fccde539..2f3e2630adbd1 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx @@ -21,7 +21,17 @@ import { Pipeline } from '../../../../common/types'; import { useKibana } from '../../../shared_imports'; import { PipelineForm } from '../../components'; -export const PipelinesCreate: React.FunctionComponent = ({ history }) => { +interface Props { + /** + * This value may be passed in to prepopulate the creation form + */ + sourcePipeline?: Pipeline; +} + +export const PipelinesCreate: React.FunctionComponent = ({ + history, + sourcePipeline, +}) => { const { services } = useKibana(); const [isSaving, setIsSaving] = useState(false); @@ -87,6 +97,7 @@ export const PipelinesCreate: React.FunctionComponent = ({ void; + onCloneClick: (pipelineName: string) => void; onDeleteClick: (pipelineName: string[]) => void; onClose: () => void; } @@ -34,8 +40,63 @@ export const PipelineDetails: FunctionComponent = ({ pipeline, onClose, onEditClick, + onCloneClick, onDeleteClick, }) => { + const [showPopover, setShowPopover] = useState(false); + const actionMenuItems = [ + /** + * Edit pipeline + */ + { + name: i18n.translate('xpack.ingestPipelines.list.pipelineDetails.editActionLabel', { + defaultMessage: 'Edit', + }), + icon: , + onClick: () => onEditClick(pipeline.name), + }, + /** + * Clone pipeline + */ + { + name: i18n.translate('xpack.ingestPipelines.list.pipelineDetails.cloneActionLabel', { + defaultMessage: 'Clone', + }), + icon: , + onClick: () => onCloneClick(pipeline.name), + }, + /** + * Delete pipeline + */ + { + name: i18n.translate('xpack.ingestPipelines.list.pipelineDetails.deleteActionLabel', { + defaultMessage: 'Delete', + }), + icon: , + onClick: () => onDeleteClick([pipeline.name]), + }, + ]; + + const managePipelineButton = ( + setShowPopover(previousBool => !previousBool)} + iconType="arrowUp" + iconSide="right" + fill + > + {i18n.translate('xpack.ingestPipelines.list.pipelineDetails.managePipelineButtonLabel', { + defaultMessage: 'Manage', + })} + + ); + return ( = ({
- onEditClick(pipeline.name)}> - {i18n.translate('xpack.ingestPipelines.list.pipelineDetails.editButtonLabel', { - defaultMessage: 'Edit', - })} - - - - onDeleteClick([pipeline.name])}> - {i18n.translate('xpack.ingestPipelines.list.pipelineDetails.deleteButtonLabel', { - defaultMessage: 'Delete', - })} - + setShowPopover(false)} + button={managePipelineButton} + panelPaddingSize="none" + withTitle + repositionOnScroll + > + +
diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx index ca4892fe281c2..bd0043e3e74af 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx @@ -51,6 +51,10 @@ export const PipelinesList: React.FunctionComponent = ({ hi history.push(encodeURI(`${BASE_PATH}/edit/${encodeURIComponent(name)}`)); }; + const clonePipeline = (name: string) => { + history.push(encodeURI(`${BASE_PATH}/create/${encodeURIComponent(name)}`)); + }; + if (isLoading) { content = ( @@ -66,6 +70,7 @@ export const PipelinesList: React.FunctionComponent = ({ hi onReloadClick={sendRequest} onEditPipelineClick={editPipeline} onDeletePipelineClick={setPipelinesToDelete} + onClonePipelineClick={clonePipeline} onViewPipelineClick={setSelectedPipeline} pipelines={data} /> @@ -130,8 +135,9 @@ export const PipelinesList: React.FunctionComponent = ({ hi setSelectedPipeline(undefined)} - onDeleteClick={setPipelinesToDelete} onEditClick={editPipeline} + onCloneClick={clonePipeline} + onDeleteClick={setPipelinesToDelete} /> )} {pipelinesToDelete?.length > 0 ? ( diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx index 01b05eace3b60..05488f46c148e 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx @@ -15,6 +15,7 @@ export interface Props { pipelines: Pipeline[]; onReloadClick: () => void; onEditPipelineClick: (pipelineName: string) => void; + onClonePipelineClick: (pipelineName: string) => void; onDeletePipelineClick: (pipelineName: string[]) => void; onViewPipelineClick: (pipeline: Pipeline) => void; } @@ -23,6 +24,7 @@ export const PipelineTable: FunctionComponent = ({ pipelines, onReloadClick, onEditPipelineClick, + onClonePipelineClick, onDeletePipelineClick, onViewPipelineClick, }) => { @@ -32,6 +34,7 @@ export const PipelineTable: FunctionComponent = ({ = ({ name: i18n.translate('xpack.ingestPipelines.list.table.nameColumnTitle', { defaultMessage: 'Name', }), + sortable: true, render: (name: string, pipeline) => ( onViewPipelineClick(pipeline)}>{name} ), @@ -100,6 +104,7 @@ export const PipelineTable: FunctionComponent = ({ }), actions: [ { + isPrimary: true, name: i18n.translate('xpack.ingestPipelines.list.table.editActionLabel', { defaultMessage: 'Edit', }), @@ -112,6 +117,19 @@ export const PipelineTable: FunctionComponent = ({ onClick: ({ name }) => onEditPipelineClick(name), }, { + name: i18n.translate('xpack.ingestPipelines.list.table.cloneActionLabel', { + defaultMessage: 'Clone', + }), + description: i18n.translate( + 'xpack.ingestPipelines.list.table.cloneActionDescription', + { defaultMessage: 'Clone this pipeline' } + ), + type: 'icon', + icon: 'copy', + onClick: ({ name }) => onClonePipelineClick(name), + }, + { + isPrimary: true, name: i18n.translate('xpack.ingestPipelines.list.table.deleteActionLabel', { defaultMessage: 'Delete', }),