From ef521cfa84e4188fcac6fed9a84e9dfd971643bb Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Sat, 29 Jun 2019 04:05:55 -0700 Subject: [PATCH] EUIfy Watcher (#35301) --- x-pack/dev-tools/jest/create_jest_config.js | 1 + x-pack/legacy/plugins/watcher/README.md | 121 +-- .../client_integration/helpers/constants.ts | 11 + .../helpers/http_requests.ts | 158 +++ .../client_integration/helpers/index.ts | 23 + .../helpers/setup_environment.ts | 32 + .../helpers/watch_create_json.helpers.ts | 83 ++ .../helpers/watch_create_threshold.helpers.ts | 104 ++ .../helpers/watch_edit.helpers.ts | 56 ++ .../helpers/watch_list.helpers.ts | 99 ++ .../helpers/watch_status.helpers.ts | 141 +++ .../watch_create_json.test.ts | 284 ++++++ .../watch_create_threshold.test.tsx | 786 +++++++++++++++ .../client_integration/watch_edit.test.ts | 229 +++++ .../client_integration/watch_list.test.ts | 238 +++++ .../client_integration/watch_status.test.ts | 285 ++++++ .../watcher/__tests__/plugin_definition.js | 14 - .../{action_modes.js => action_modes.ts} | 6 +- .../{action_states.js => action_states.ts} | 16 +- .../{action_types.js => action_types.ts} | 8 +- .../constants/{agg_types.js => agg_types.ts} | 6 +- .../watcher/common/constants/comparators.ts | 13 + .../{error_codes.js => error_codes.ts} | 3 +- ...roll_settings.js => es_scroll_settings.ts} | 8 +- .../common/constants/{index.js => index.ts} | 0 .../{index_names.js => index_names.ts} | 2 +- .../common/constants/{lists.js => lists.ts} | 4 +- .../watcher/common/constants/pagination.ts | 10 + .../watcher/common/constants/plugin.ts | 17 + ...resh_intervals.js => refresh_intervals.ts} | 4 +- .../common/constants/{routes.js => routes.ts} | 2 +- .../{sort_orders.js => sort_orders.ts} | 4 +- .../{time_units.js => time_units.ts} | 4 +- .../{comparators.js => watch_history.ts} | 8 +- .../common/constants/watch_state_comments.js | 33 - .../common/constants/watch_state_comments.ts | 46 + .../{watch_states.js => watch_states.ts} | 14 +- .../{watch_types.js => watch_types.ts} | 6 +- .../__tests__/get_action_type.js | 11 - ...{get_action_type.js => get_action_type.ts} | 9 +- .../get_action_type/{index.js => index.ts} | 0 .../watcher/common/types/action_types.ts | 86 ++ .../watcher/common/types/watch_types.ts | 57 ++ .../plugins/watcher/plugin_definition.js | 22 +- .../legacy/plugins/watcher/public/_hacks.scss | 4 - x-pack/legacy/plugins/watcher/public/app.html | 3 + x-pack/legacy/plugins/watcher/public/app.js | 87 ++ .../action_state_icon/action_state_icon.html | 22 - .../action_state_icon/action_state_icon.js | 29 - .../_action_type_select.scss | 44 - .../components/action_type_select/_index.scss | 1 - .../action_type_select.html | 34 - .../action_type_select/action_type_select.js | 72 -- .../chart_tooltip/_chart_tooltip.scss | 21 - .../components/chart_tooltip/_index.scss | 1 - .../chart_tooltip/chart_tooltip.html | 5 - .../components/chart_tooltip/chart_tooltip.js | 70 -- .../components/confirm_watches_modal.tsx | 52 + .../components/delete_watches_modal.tsx | 89 ++ .../duration_select/_duration_select.scss | 11 - .../components/duration_select/_index.scss | 1 - .../duration_select/duration_select.html | 24 - .../duration_select/duration_select.js | 76 -- .../components/duration_select/index.js | 7 - .../errors_display_modal.html | 18 - .../errors_display_modal.js | 18 - .../components/errors_display_modal/index.js | 7 - .../components/expression_builder/_index.scss | 1 - .../expression_item/expression_item.html | 26 - .../expression_item/expression_item.js | 98 -- .../components/expression_item/index.js | 7 - .../_expression_popover.scss | 3 - .../expression_popover.html | 13 - .../expression_popover/expression_popover.js | 28 - .../components/expression_popover/index.js | 7 - .../expression_builder.html | 3 - .../expression_builder/expression_builder.js | 28 - .../components/expression_builder/index.js | 7 - .../components/flot_chart/_flot_chart.scss | 3 - .../public/components/flot_chart/_index.scss | 1 - .../public/components/flot_chart/constants.js | 7 - .../components/flot_chart/flot_chart.html | 1 - .../components/flot_chart/flot_chart.js | 65 -- .../public/components/flot_chart/index.js | 7 - .../forbidden_message/forbidden_message.html | 17 - .../forbidden_message/forbidden_message.js | 25 - .../components/forbidden_message/index.js | 7 - .../watcher/public/components/form_errors.tsx | 31 + .../watcher/public/components/index.ts | 13 + .../components/index_select/_index.scss | 1 - .../index_select/_index_select.scss | 55 -- .../public/components/index_select/index.js | 7 - .../components/index_select/index_select.html | 46 - .../components/index_select/index_select.js | 207 ---- .../public/components/json_editor/_index.scss | 1 - .../components/json_editor/_json_editor.scss | 3 - .../public/components/json_editor/index.js | 7 - .../components/json_editor/json_editor.html | 12 - .../components/json_editor/json_editor.js | 48 - .../kbn_tabs/components/kbn_tab/index.js | 7 - .../kbn_tabs/components/kbn_tab/kbn_tab.html | 9 - .../kbn_tabs/components/kbn_tab/kbn_tab.js | 30 - .../kbn_tabs/components/kbn_tabs/index.js | 7 - .../components/kbn_tabs/kbn_tabs.html | 1 - .../kbn_tabs/components/kbn_tabs/kbn_tabs.js | 32 - .../public/components/kbn_tabs/index.js | 8 - .../components/page_error/index.ts} | 4 +- .../components/page_error/page_error.tsx | 36 + .../page_error/page_error_forbidden.tsx | 27 + .../page_error/page_error_not_exist.tsx | 36 + .../public/components/panel_pager/_index.scss | 1 - .../components/panel_pager/_panel_pager.scss | 31 - .../public/components/panel_pager/index.js | 7 - .../components/panel_pager/panel_pager.html | 28 - .../components/panel_pager/panel_pager.js | 28 - .../public/components/section_error.tsx | 43 + .../public/components/section_loading.tsx | 23 + .../threshold_preview_chart/_index.scss | 1 - .../_threshold_preview_chart.scss | 15 - .../threshold_preview_chart/constants.js | 20 - .../threshold_preview_chart/index.js | 7 - .../threshold_preview_chart.html | 22 - .../threshold_preview_chart.js | 188 ---- .../threshold_watch_expression/_index.scss | 1 - .../_threshold_watch_expression.scss | 17 - .../threshold_watch_agg_field/index.js | 7 - .../threshold_watch_agg_field.html | 37 - .../threshold_watch_agg_field.js | 68 -- .../threshold_watch_agg_type/index.js | 7 - .../threshold_watch_agg_type.html | 31 - .../threshold_watch_agg_type.js | 57 -- .../components/threshold_watch_base/index.js | 7 - .../threshold_watch_base.js | 57 -- .../threshold_watch_group_by/index.js | 7 - .../threshold_watch_group_by.html | 77 -- .../threshold_watch_group_by.js | 86 -- .../threshold_watch_threshold_level/index.js | 7 - .../threshold_watch_threshold_level.html | 45 - .../threshold_watch_threshold_level.js | 59 -- .../threshold_watch_time_window/index.js | 7 - .../threshold_watch_time_window.html | 63 -- .../threshold_watch_time_window.js | 77 -- .../threshold_watch_expression/index.js | 7 - .../lib/comparators.js | 24 - .../lib/group_by_types.js | 26 - .../lib/time_units.js | 46 - .../threshold_watch_expression.html | 89 -- .../threshold_watch_expression.js | 228 ----- .../tool_bar_selected_count/index.js | 7 - .../tool_bar_selected_count.html | 4 - .../tool_bar_selected_count.js | 27 - .../components/watch_actions/_index.scss | 2 - .../watch_actions/_watch_actions.scss | 14 - .../lib/watch_action_controller_base/index.js | 7 - .../watch_action_controller_base.js | 18 - .../watch_action/_watch_action.scss | 56 -- .../components/watch_action/index.js | 7 - .../components/watch_action/watch_action.html | 57 -- .../components/watch_action/watch_action.js | 76 -- .../components/watch_email_action/index.js | 7 - .../watch_email_action.html | 72 -- .../watch_email_action/watch_email_action.js | 47 - .../components/watch_logging_action/index.js | 7 - .../watch_logging_action.html | 25 - .../watch_logging_action.js | 36 - .../components/watch_slack_action/index.js | 7 - .../watch_slack_action.html | 48 - .../watch_slack_action/watch_slack_action.js | 46 - .../public/components/watch_actions/index.js | 7 - .../watch_actions/watch_actions.html | 27 - .../components/watch_actions/watch_actions.js | 104 -- .../watch_history_item_detail/_index.scss | 1 - .../_watch_history_item_detail.scss | 6 - .../watch_history_item_detail/index.js | 7 - .../watch_history_item_detail.html | 14 - .../watch_history_item_detail.js | 32 - .../components/watch_state_icon/index.js | 7 - .../watch_state_icon/watch_state_icon.html | 18 - .../watch_state_icon/watch_state_icon.js | 29 - .../public/components/watch_status.tsx | 44 + .../components/xpack_aria_describes/index.js | 7 - .../xpack_aria_describes.js | 81 -- .../watcher/public/constants/base_path.ts | 7 + .../plugins/watcher/public/constants/index.js | 7 - .../index.js => constants/index.ts} | 2 +- .../watcher/public/constants/time_units.js | 43 - .../directives/check_box/check_box.html | 6 - .../public/directives/check_box/check_box.js | 27 - .../public/directives/check_box/index.js | 7 - .../watcher/public/directives/json_input.js | 38 - .../watcher/public/directives/moment.js | 20 - .../directives/sortable_column/index.js | 8 - .../sortable_column/sortable_column.html | 14 - .../sortable_column/sortable_column.js | 63 -- .../public/directives/toggle_button/index.js | 7 - .../toggle_button/toggle_button.html | 12 - .../directives/toggle_button/toggle_button.js | 28 - .../public/directives/toggle_panel/index.js | 7 - .../directives/toggle_panel/toggle_panel.html | 15 - .../directives/toggle_panel/toggle_panel.js | 34 - .../public/directives/tooltip/index.js | 7 - .../public/directives/tooltip/tooltip.html | 6 - .../public/directives/tooltip/tooltip.js | 40 - .../action_state_icon => }/index.js | 3 +- .../legacy/plugins/watcher/public/index.scss | 38 +- .../legacy/plugins/watcher/public/lib/api.ts | 193 ++++ .../plugins/watcher/public/lib/breadcrumbs.js | 49 - .../plugins/watcher/public/lib/breadcrumbs.ts | 37 + .../plugins/watcher/public/lib/clamp/clamp.js | 9 - .../plugins/watcher/public/lib/clamp/index.js | 7 - .../documentation_links.js | 13 - .../documentation_links.ts | 24 + .../public/lib/documentation_links/index.js | 7 - .../documentation_links/index.ts} | 2 +- .../make_documentation_link.js | 25 - .../plugins/watcher/public/lib/format_date.ts | 15 + ...et_search_value.js => get_search_value.ts} | 2 +- .../public/lib/get_search_value/index.js | 7 - .../watcher/public/lib/get_time_unit_label.ts | 33 + .../get_time_units_label.js | 14 - .../public/lib/get_time_units_label/index.js | 7 - .../public/lib/manage_angular_lifecycle.js | 23 + .../plugins/watcher/public/lib/navigation.ts | 23 + .../__tests__/sortable_boolean.js | 39 - .../public/lib/sortable_boolean/index.js | 7 - .../lib/sortable_boolean/sortable_boolean.js | 14 - .../lib/update_management_sections/index.js | 8 - .../update_history_section.js | 20 - .../update_watch_sections.js | 29 - .../plugins/watcher/public/lib/use_request.ts | 126 +++ .../watcher/public/models/action/action.js | 12 +- .../public/models/action/base_action.js | 8 +- .../public/models/action/email_action.js | 76 +- .../public/models/action/hipchat.action.js | 69 -- .../public/models/action/index.action.js | 88 -- .../public/models/action/index_action.js | 81 ++ .../public/models/action/jira.action.js | 70 -- .../public/models/action/jira_action.js | 106 ++ .../public/models/action/logging_action.js | 39 +- .../public/models/action/pagerduty.action.js | 80 -- .../public/models/action/pagerduty_action.js | 80 ++ .../public/models/action/slack_action.js | 71 +- .../public/models/action/webhook.action.js | 91 -- .../public/models/action/webhook_action.js | 145 +++ .../models/action_status/action_status.js | 2 +- .../models/execute_details/execute_details.js | 56 +- .../plugins/watcher/public/models/index.d.ts | 42 + .../watch/agg_types.ts} | 35 +- .../watcher/public/models/watch/base_watch.js | 52 +- .../public/models/watch/comparators.ts | 58 ++ .../public/models/watch/default_watch.json | 2 +- .../public/models/watch/group_by_types.ts | 31 + .../watcher/public/models/watch/json_watch.js | 70 +- .../public/models/watch/monitoring_watch.js | 2 +- .../public/models/watch/threshold_watch.js | 207 +++- .../watcher/public/models/watch/watch.js | 4 +- .../watch_history_item/watch_history_item.js | 2 +- .../models/watch_status/watch_status.js | 4 +- ...ion.js => register_management_sections.js} | 39 +- .../plugins/watcher/public/register_route.js | 68 ++ .../action_status_table.html | 74 -- .../action_status_table.js | 45 - .../components/action_status_table/index.js | 7 - .../components/watch_detail/index.js | 7 - .../components/watch_detail/watch_detail.html | 89 -- .../components/watch_detail/watch_detail.js | 222 ----- .../components/watch_history/index.js | 7 - .../watch_history/watch_history.html | 60 -- .../components/watch_history/watch_history.js | 85 -- .../watch_history_range_filter/index.js | 7 - .../watch_history_range_filter.html | 41 - .../watch_history_range_filter.js | 31 - .../components/watch_history_table/index.js | 7 - .../watch_history_table.html | 71 -- .../watch_history_table.js | 53 - .../public/sections/watch_detail/index.js | 7 - .../watch_detail/watch_detail_route.html | 5 - .../watch_detail/watch_detail_route.js | 84 -- .../components/json_watch_edit/index.js | 7 - .../components/json_watch_edit/index.ts} | 4 +- .../json_watch_edit/json_watch_edit.html | 121 --- .../json_watch_edit/json_watch_edit.js | 289 ------ .../json_watch_edit/json_watch_edit.tsx | 140 +++ .../json_watch_edit/json_watch_edit_form.tsx | 222 +++++ .../json_watch_edit_simulate.tsx | 416 ++++++++ .../json_watch_edit_simulate_results.tsx | 207 ++++ .../components/monitoring_watch_edit/index.ts | 7 + .../monitoring_watch_edit.tsx | 67 ++ .../threshold_watch_edit/_index.scss | 1 - .../_threshold_watch_edit.scss | 31 - .../action_fields/email_action_fields.tsx | 120 +++ .../action_fields/index.ts | 13 + .../action_fields/index_action_fields.tsx | 56 ++ .../action_fields/jira_action_fields.tsx | 122 +++ .../action_fields/logging_action_fields.tsx | 56 ++ .../action_fields/pagerduty_action_fields.tsx | 61 ++ .../action_fields/slack_action_fields.tsx | 76 ++ .../action_fields/webhook_action_fields.tsx | 250 +++++ .../components/threshold_watch_edit/index.js | 7 - .../components/threshold_watch_edit/index.ts} | 4 +- .../threshold_watch_action_accordion.tsx | 254 +++++ .../threshold_watch_action_dropdown.tsx | 123 +++ .../threshold_watch_action_panel.tsx | 51 + .../threshold_watch_edit.html | 105 -- .../threshold_watch_edit.js | 325 ------- .../threshold_watch_edit.tsx | 913 ++++++++++++++++++ .../watch_visualization.tsx | 290 ++++++ .../watch_action_mode_table/index.js | 7 - .../watch_action_mode_table.html | 46 - .../watch_action_mode_table.js | 58 -- .../watch_edit/components/watch_edit.tsx | 198 ++++ .../index.js | 7 - .../watch_edit_actions_execute_summary.html | 64 -- .../watch_edit_actions_execute_summary.js | 51 - .../components/watch_edit_detail/_index.scss | 1 - .../watch_edit_detail/_watch_edit_detail.scss | 5 - .../components/watch_edit_detail/index.js | 7 - .../watch_edit_detail/watch_edit_detail.html | 81 -- .../watch_edit_detail/watch_edit_detail.js | 68 -- .../watch_edit_execute_detail/_index.scss | 1 - .../_watch_edit_execute_detail.scss | 19 - .../watch_edit_execute_detail/index.js | 7 - .../watch_edit_execute_detail.html | 125 --- .../watch_edit_execute_detail.js | 109 --- .../watch_edit_execute_info_panel/index.js | 7 - .../watch_edit_execute_info_panel.html | 18 - .../watch_edit_execute_info_panel.js | 25 - .../components/watch_edit_title_bar/index.js | 7 - .../watch_edit_title_bar.html | 37 - .../watch_edit_title_bar.js | 31 - .../watch_edit_title_panel/_index.scss | 1 - .../_watch_edit_title_panel.scss | 13 - .../watch_edit_title_panel/index.js | 7 - .../watch_edit_title_panel.html | 201 ---- .../watch_edit_title_panel.js | 175 ---- .../watch_edit_watch_execute_summary/index.js | 7 - .../watch_edit_watch_execute_summary.html | 8 - .../watch_edit_watch_execute_summary.js | 27 - .../public/sections/watch_edit/index.js | 7 - .../sections/watch_edit/watch_context.ts | 8 + .../sections/watch_edit/watch_edit_actions.ts | 118 +++ .../sections/watch_edit/watch_edit_route.html | 8 - .../sections/watch_edit/watch_edit_route.js | 84 -- .../components/watch_history_item/index.js | 7 - .../watch_history_item.html | 26 - .../watch_history_item/watch_history_item.js | 38 - .../index.js | 7 - .../watch_history_item_actions_summary.html | 53 - .../watch_history_item_actions_summary.js | 42 - .../watch_history_item_watch_summary/index.js | 7 - .../watch_history_item_watch_summary.html | 10 - .../watch_history_item_watch_summary.js | 27 - .../sections/watch_history_item/index.js | 7 - .../watch_history_item_route.html | 4 - .../watch_history_item_route.js | 75 -- .../watch_list/components/watch_list.tsx | 483 +++++++++ .../components/watch_list/_index.scss | 1 - .../components/watch_list/_watch_list.scss | 6 - .../watch_list/components/watch_list/index.js | 7 - .../components/watch_list/watch_list.html | 121 --- .../components/watch_list/watch_list.js | 212 ---- .../components/watch_table/index.js | 7 - .../components/watch_table/watch_table.html | 174 ---- .../components/watch_table/watch_table.js | 90 -- .../public/sections/watch_list/index.js | 8 - .../sections/watch_list/watch_list_route.html | 1 - .../sections/watch_list/watch_list_route.js | 79 -- .../watch_status/components/watch_detail.tsx | 237 +++++ .../watch_status/components/watch_history.tsx | 311 ++++++ .../watch_status/components/watch_status.tsx | 258 +++++ .../watch_status/watch_details_context.ts | 8 + .../action_defaults_service.factory.js | 22 - .../action_defaults_service.js | 19 - .../action_defaults/actions/email_action.js | 33 - .../action_defaults/actions/logging_action.js | 30 - .../action_defaults/actions/slack_action.js | 30 - .../public/services/action_defaults/index.js | 8 - .../services/action_defaults/registry.js | 11 - .../services/fields/fields_service.factory.js | 14 - .../public/services/fields/fields_service.js | 22 - .../watcher/public/services/fields/index.js | 7 - .../html_id_generator_service.factory.js | 17 - .../html_id_generator_service.js | 24 - .../services/html_id_generator/index.js | 7 - .../watcher/public/services/indices/index.js | 7 - .../indices/indices_service.factory.js | 14 - .../services/indices/indices_service.js | 22 - .../watcher/public/services/interval/index.js | 7 - .../xpack_watcher_interval_service.factory.js | 16 - .../xpack_watcher_interval_service.js | 16 - .../watcher/public/services/license/index.js | 7 - .../license/license_service.factory.js | 14 - .../services/license/license_service.js | 69 -- .../watcher/public/services/settings/index.js | 7 - .../settings/settings_service.factory.js | 14 - .../services/settings/settings_service.js | 23 - .../watcher/public/services/timezone/index.js | 7 - .../xpack_watcher_timezone_service.factory.js | 14 - .../xpack_watcher_timezone_service.js | 31 - .../watcher/public/services/watch/index.js | 7 - .../services/watch/watch_service.factory.js | 14 - .../public/services/watch/watch_service.js | 170 ---- .../public/services/watch_history/index.js | 7 - .../watch_history_service.factory.js | 14 - .../watch_history/watch_history_service.js | 27 - .../watcher/public/services/watches/index.js | 7 - .../public/services/watches/watches.js | 33 - .../check_license/__tests__/check_license.js | 142 --- .../server/lib/check_license/check_license.js | 74 -- .../watcher/server/lib/check_license/index.js | 7 - .../__tests__/license_pre_routing_factory.js | 9 +- .../license_pre_routing_factory.js | 5 +- .../lib/register_license_checker/index.js | 7 - .../register_license_checker.js | 21 - .../watcher/server/models/action/action.js | 12 +- .../server/models/action/action.test.js | 4 +- .../server/models/action/base_action.js | 2 +- .../server/models/action/email_action.js | 4 +- .../server/models/action/index_action.js | 97 ++ .../server/models/action/jira_action.js | 140 +++ .../server/models/action/logging_action.js | 4 +- .../server/models/action/pagerduty_action.js | 99 ++ .../server/models/action/slack_action.js | 4 +- .../server/models/action/unknown_action.js | 2 +- .../server/models/action/webhook_action.js | 176 ++++ .../action_status/__tests__/action_status.js | 4 +- .../models/action_status/action_status.js | 27 +- .../models/execute_details/execute_details.js | 2 +- .../watcher/server/models/fields/fields.js | 2 +- .../models/settings/__tests__/settings.js | 6 +- .../server/models/settings/settings.js | 4 +- .../models/watch/__tests__/threshold_watch.js | 2 +- .../watcher/server/models/watch/base_watch.js | 6 +- .../watch/threshold_watch/build_condition.js | 61 +- .../watch/threshold_watch/build_transform.js | 49 +- .../watch/threshold_watch/threshold_watch.js | 2 +- .../watcher/server/models/watch/watch.js | 4 +- .../watch_history_item/watch_history_item.js | 6 +- .../models/watch_status/watch_status.js | 4 +- .../routes/api/watch/register_save_route.js | 43 +- .../api/watches/register_delete_route.js | 35 +- .../watcher/test/fixtures/execute_details.ts | 29 + .../plugins/watcher/test/fixtures/index.ts | 9 + .../plugins/watcher/test/fixtures/watch.ts | 67 ++ .../watcher/test/fixtures/watch_history.ts | 38 + .../server/lib/check_license/check_license.js | 2 +- .../translations/translations/ja-JP.json | 191 ---- .../translations/translations/zh-CN.json | 191 +--- .../functional/apps/watcher/watcher_test.js | 33 +- x-pack/test/functional/config.js | 6 +- .../functional/page_objects/watcher_page.js | 28 +- 451 files changed, 10869 insertions(+), 10880 deletions(-) create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/constants.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/http_requests.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/index.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/setup_environment.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_create_json.helpers.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_create_threshold.helpers.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_edit.helpers.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_list.helpers.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_status.helpers.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_edit.test.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_list.test.ts create mode 100644 x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_status.test.ts delete mode 100644 x-pack/legacy/plugins/watcher/__tests__/plugin_definition.js rename x-pack/legacy/plugins/watcher/common/constants/{action_modes.js => action_modes.ts} (94%) rename x-pack/legacy/plugins/watcher/common/constants/{action_states.js => action_states.ts} (82%) rename x-pack/legacy/plugins/watcher/common/constants/{action_types.js => action_types.ts} (81%) rename x-pack/legacy/plugins/watcher/common/constants/{agg_types.js => agg_types.ts} (82%) create mode 100644 x-pack/legacy/plugins/watcher/common/constants/comparators.ts rename x-pack/legacy/plugins/watcher/common/constants/{error_codes.js => error_codes.ts} (85%) rename x-pack/legacy/plugins/watcher/common/constants/{es_scroll_settings.js => es_scroll_settings.ts} (77%) rename x-pack/legacy/plugins/watcher/common/constants/{index.js => index.ts} (100%) rename x-pack/legacy/plugins/watcher/common/constants/{index_names.js => index_names.ts} (84%) rename x-pack/legacy/plugins/watcher/common/constants/{lists.js => lists.ts} (74%) create mode 100644 x-pack/legacy/plugins/watcher/common/constants/pagination.ts create mode 100644 x-pack/legacy/plugins/watcher/common/constants/plugin.ts rename x-pack/legacy/plugins/watcher/common/constants/{refresh_intervals.js => refresh_intervals.ts} (78%) rename x-pack/legacy/plugins/watcher/common/constants/{routes.js => routes.ts} (84%) rename x-pack/legacy/plugins/watcher/common/constants/{sort_orders.js => sort_orders.ts} (77%) rename x-pack/legacy/plugins/watcher/common/constants/{time_units.js => time_units.ts} (81%) rename x-pack/legacy/plugins/watcher/common/constants/{comparators.js => watch_history.ts} (74%) delete mode 100644 x-pack/legacy/plugins/watcher/common/constants/watch_state_comments.js create mode 100644 x-pack/legacy/plugins/watcher/common/constants/watch_state_comments.ts rename x-pack/legacy/plugins/watcher/common/constants/{watch_states.js => watch_states.ts} (77%) rename x-pack/legacy/plugins/watcher/common/constants/{watch_types.js => watch_types.ts} (77%) rename x-pack/legacy/plugins/watcher/common/lib/get_action_type/{get_action_type.js => get_action_type.ts} (57%) rename x-pack/legacy/plugins/watcher/common/lib/get_action_type/{index.js => index.ts} (100%) create mode 100644 x-pack/legacy/plugins/watcher/common/types/action_types.ts create mode 100644 x-pack/legacy/plugins/watcher/common/types/watch_types.ts delete mode 100644 x-pack/legacy/plugins/watcher/public/_hacks.scss create mode 100644 x-pack/legacy/plugins/watcher/public/app.html create mode 100644 x-pack/legacy/plugins/watcher/public/app.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/action_state_icon/action_state_icon.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/action_state_icon/action_state_icon.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/action_type_select/_action_type_select.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/action_type_select/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/action_type_select/action_type_select.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/action_type_select/action_type_select.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/chart_tooltip/_chart_tooltip.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/chart_tooltip/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/chart_tooltip/chart_tooltip.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/chart_tooltip/chart_tooltip.js create mode 100644 x-pack/legacy/plugins/watcher/public/components/confirm_watches_modal.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/components/delete_watches_modal.tsx delete mode 100644 x-pack/legacy/plugins/watcher/public/components/duration_select/_duration_select.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/duration_select/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/duration_select/duration_select.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/duration_select/duration_select.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/duration_select/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/errors_display_modal/errors_display_modal.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/errors_display_modal/errors_display_modal.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/errors_display_modal/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/expression_builder/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/expression_item.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/expression_item.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/_expression_popover.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/expression_popover.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/expression_popover.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/expression_builder/expression_builder.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/expression_builder/expression_builder.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/expression_builder/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/flot_chart/_flot_chart.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/flot_chart/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/flot_chart/constants.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/flot_chart/flot_chart.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/flot_chart/flot_chart.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/flot_chart/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/forbidden_message/forbidden_message.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/forbidden_message/forbidden_message.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/forbidden_message/index.js create mode 100644 x-pack/legacy/plugins/watcher/public/components/form_errors.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/components/index.ts delete mode 100644 x-pack/legacy/plugins/watcher/public/components/index_select/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/index_select/_index_select.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/index_select/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/index_select/index_select.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/index_select/index_select.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/json_editor/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/json_editor/_json_editor.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/json_editor/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/json_editor/json_editor.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/json_editor/json_editor.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/kbn_tab.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/kbn_tab.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/kbn_tabs.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/kbn_tabs.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/kbn_tabs/index.js rename x-pack/legacy/plugins/watcher/{common/constants/watch_history.js => public/components/page_error/index.ts} (80%) create mode 100644 x-pack/legacy/plugins/watcher/public/components/page_error/page_error.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/components/page_error/page_error_forbidden.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/components/page_error/page_error_not_exist.tsx delete mode 100644 x-pack/legacy/plugins/watcher/public/components/panel_pager/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/panel_pager/_panel_pager.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/panel_pager/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/panel_pager/panel_pager.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/panel_pager/panel_pager.js create mode 100644 x-pack/legacy/plugins/watcher/public/components/section_error.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/components/section_loading.tsx delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/_threshold_preview_chart.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/constants.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/threshold_preview_chart.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/threshold_preview_chart.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/_threshold_watch_expression.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/threshold_watch_agg_field.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/threshold_watch_agg_field.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/threshold_watch_agg_type.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/threshold_watch_agg_type.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_base/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_base/threshold_watch_base.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/threshold_watch_group_by.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/threshold_watch_group_by.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/threshold_watch_threshold_level.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/threshold_watch_threshold_level.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/threshold_watch_time_window.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/threshold_watch_time_window.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/comparators.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/group_by_types.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/time_units.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/threshold_watch_expression.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/threshold_watch_expression.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/tool_bar_selected_count.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/tool_bar_selected_count.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/_watch_actions.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/lib/watch_action_controller_base/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/lib/watch_action_controller_base/watch_action_controller_base.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/_watch_action.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/watch_action.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/watch_action.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/watch_email_action.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/watch_email_action.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/watch_logging_action.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/watch_logging_action.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/watch_slack_action.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/watch_slack_action.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/watch_actions.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_actions/watch_actions.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/_watch_history_item_detail.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/watch_history_item_detail.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/watch_history_item_detail.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_state_icon/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_state_icon/watch_state_icon.html delete mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_state_icon/watch_state_icon.js create mode 100644 x-pack/legacy/plugins/watcher/public/components/watch_status.tsx delete mode 100644 x-pack/legacy/plugins/watcher/public/components/xpack_aria_describes/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/components/xpack_aria_describes/xpack_aria_describes.js create mode 100644 x-pack/legacy/plugins/watcher/public/constants/base_path.ts delete mode 100644 x-pack/legacy/plugins/watcher/public/constants/index.js rename x-pack/legacy/plugins/watcher/public/{components/action_type_select/index.js => constants/index.ts} (85%) delete mode 100644 x-pack/legacy/plugins/watcher/public/constants/time_units.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/check_box/check_box.html delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/check_box/check_box.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/check_box/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/json_input.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/moment.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/sortable_column/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/sortable_column/sortable_column.html delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/sortable_column/sortable_column.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/toggle_button/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/toggle_button/toggle_button.html delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/toggle_button/toggle_button.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/toggle_panel/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/toggle_panel/toggle_panel.html delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/toggle_panel/toggle_panel.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/tooltip/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/tooltip/tooltip.html delete mode 100644 x-pack/legacy/plugins/watcher/public/directives/tooltip/tooltip.js rename x-pack/legacy/plugins/watcher/public/{components/action_state_icon => }/index.js (78%) create mode 100644 x-pack/legacy/plugins/watcher/public/lib/api.ts delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/breadcrumbs.js create mode 100644 x-pack/legacy/plugins/watcher/public/lib/breadcrumbs.ts delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/clamp/clamp.js delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/clamp/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/documentation_links/documentation_links.js create mode 100644 x-pack/legacy/plugins/watcher/public/lib/documentation_links/documentation_links.ts delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/documentation_links/index.js rename x-pack/legacy/plugins/watcher/public/{components/chart_tooltip/index.js => lib/documentation_links/index.ts} (86%) delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/documentation_links/make_documentation_link.js create mode 100644 x-pack/legacy/plugins/watcher/public/lib/format_date.ts rename x-pack/legacy/plugins/watcher/public/lib/{get_search_value/get_search_value.js => get_search_value.ts} (90%) delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/get_search_value/index.js create mode 100644 x-pack/legacy/plugins/watcher/public/lib/get_time_unit_label.ts delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/get_time_units_label/get_time_units_label.js delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/get_time_units_label/index.js create mode 100644 x-pack/legacy/plugins/watcher/public/lib/manage_angular_lifecycle.js create mode 100644 x-pack/legacy/plugins/watcher/public/lib/navigation.ts delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/__tests__/sortable_boolean.js delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/sortable_boolean.js delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/update_management_sections/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/update_management_sections/update_history_section.js delete mode 100644 x-pack/legacy/plugins/watcher/public/lib/update_management_sections/update_watch_sections.js create mode 100644 x-pack/legacy/plugins/watcher/public/lib/use_request.ts delete mode 100644 x-pack/legacy/plugins/watcher/public/models/action/hipchat.action.js delete mode 100644 x-pack/legacy/plugins/watcher/public/models/action/index.action.js create mode 100644 x-pack/legacy/plugins/watcher/public/models/action/index_action.js delete mode 100644 x-pack/legacy/plugins/watcher/public/models/action/jira.action.js create mode 100644 x-pack/legacy/plugins/watcher/public/models/action/jira_action.js delete mode 100644 x-pack/legacy/plugins/watcher/public/models/action/pagerduty.action.js create mode 100644 x-pack/legacy/plugins/watcher/public/models/action/pagerduty_action.js delete mode 100644 x-pack/legacy/plugins/watcher/public/models/action/webhook.action.js create mode 100644 x-pack/legacy/plugins/watcher/public/models/action/webhook_action.js create mode 100644 x-pack/legacy/plugins/watcher/public/models/index.d.ts rename x-pack/legacy/plugins/watcher/public/{components/threshold_watch_expression/lib/agg_types.js => models/watch/agg_types.ts} (53%) create mode 100644 x-pack/legacy/plugins/watcher/public/models/watch/comparators.ts create mode 100644 x-pack/legacy/plugins/watcher/public/models/watch/group_by_types.ts rename x-pack/legacy/plugins/watcher/public/{sections/watch_list/register_management_section.js => register_management_sections.js} (73%) create mode 100644 x-pack/legacy/plugins/watcher/public/register_route.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/action_status_table.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/action_status_table.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/watch_detail.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/watch_detail.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/watch_history.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/watch_history.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/watch_history_range_filter.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/watch_history_range_filter.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/watch_history_table.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/watch_history_table.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/watch_detail_route.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_detail/watch_detail_route.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/index.js rename x-pack/legacy/plugins/watcher/{common/constants/pagination.js => public/sections/watch_edit/components/json_watch_edit/index.ts} (82%) delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.js create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_form.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate_results.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/monitoring_watch_edit/index.ts create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/_threshold_watch_edit.scss create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/email_action_fields.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/index.ts create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/index_action_fields.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/jira_action_fields.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/logging_action_fields.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/pagerduty_action_fields.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/slack_action_fields.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/index.js rename x-pack/legacy/plugins/watcher/{common/constants/plugin.js => public/sections/watch_edit/components/threshold_watch_edit/index.ts} (79%) create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_accordion.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_dropdown.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_panel.tsx delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.js create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/watch_action_mode_table.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/watch_action_mode_table.js create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit.tsx delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/watch_edit_actions_execute_summary.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/watch_edit_actions_execute_summary.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/_watch_edit_detail.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/watch_edit_detail.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/watch_edit_detail.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/_watch_edit_execute_detail.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/watch_edit_execute_detail.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/watch_edit_execute_detail.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/watch_edit_execute_info_panel.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/watch_edit_execute_info_panel.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/watch_edit_title_bar.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/watch_edit_title_bar.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/_watch_edit_title_panel.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/watch_edit_title_panel.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/watch_edit_title_panel.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/watch_edit_watch_execute_summary.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/watch_edit_watch_execute_summary.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/index.js create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_context.ts create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_actions.ts delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_route.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_route.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/watch_history_item.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/watch_history_item.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/watch_history_item_actions_summary.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/watch_history_item_actions_summary.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/watch_history_item_watch_summary.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/watch_history_item_watch_summary.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/watch_history_item_route.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_history_item/watch_history_item_route.js create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list.tsx delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/_index.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/_watch_list.scss delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/watch_list_route.html delete mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_list/watch_list_route.js create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_detail.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_history.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_status.tsx create mode 100644 x-pack/legacy/plugins/watcher/public/sections/watch_status/watch_details_context.ts delete mode 100644 x-pack/legacy/plugins/watcher/public/services/action_defaults/action_defaults_service.factory.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/action_defaults/action_defaults_service.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/email_action.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/logging_action.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/slack_action.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/action_defaults/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/action_defaults/registry.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/fields/fields_service.factory.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/fields/fields_service.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/fields/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/html_id_generator/html_id_generator_service.factory.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/html_id_generator/html_id_generator_service.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/html_id_generator/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/indices/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/indices/indices_service.factory.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/indices/indices_service.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/interval/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/interval/xpack_watcher_interval_service.factory.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/interval/xpack_watcher_interval_service.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/license/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/license/license_service.factory.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/license/license_service.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/settings/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/settings/settings_service.factory.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/settings/settings_service.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/timezone/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/timezone/xpack_watcher_timezone_service.factory.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/timezone/xpack_watcher_timezone_service.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/watch/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/watch/watch_service.factory.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/watch/watch_service.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/watch_history/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/watch_history/watch_history_service.factory.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/watch_history/watch_history_service.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/watches/index.js delete mode 100644 x-pack/legacy/plugins/watcher/public/services/watches/watches.js delete mode 100644 x-pack/legacy/plugins/watcher/server/lib/check_license/__tests__/check_license.js delete mode 100644 x-pack/legacy/plugins/watcher/server/lib/check_license/check_license.js delete mode 100644 x-pack/legacy/plugins/watcher/server/lib/check_license/index.js delete mode 100644 x-pack/legacy/plugins/watcher/server/lib/register_license_checker/index.js delete mode 100644 x-pack/legacy/plugins/watcher/server/lib/register_license_checker/register_license_checker.js create mode 100644 x-pack/legacy/plugins/watcher/server/models/action/index_action.js create mode 100644 x-pack/legacy/plugins/watcher/server/models/action/jira_action.js create mode 100644 x-pack/legacy/plugins/watcher/server/models/action/pagerduty_action.js create mode 100644 x-pack/legacy/plugins/watcher/server/models/action/webhook_action.js create mode 100644 x-pack/legacy/plugins/watcher/test/fixtures/execute_details.ts create mode 100644 x-pack/legacy/plugins/watcher/test/fixtures/index.ts create mode 100644 x-pack/legacy/plugins/watcher/test/fixtures/watch.ts create mode 100644 x-pack/legacy/plugins/watcher/test/fixtures/watch_history.ts diff --git a/x-pack/dev-tools/jest/create_jest_config.js b/x-pack/dev-tools/jest/create_jest_config.js index 2d9eec59e286e..952078656b208 100644 --- a/x-pack/dev-tools/jest/create_jest_config.js +++ b/x-pack/dev-tools/jest/create_jest_config.js @@ -24,6 +24,7 @@ export function createJestConfig({ '^ui/(.*)': `${kibanaDirectory}/src/legacy/ui/public/$1`, 'uiExports/(.*)': `${kibanaDirectory}/src/dev/jest/mocks/file_mocks.js`, '^src/core/(.*)': `${kibanaDirectory}/src/core/$1`, + '^plugins/watcher/models/(.*)': `${xPackKibanaDirectory}/legacy/plugins/watcher/public/models/$1`, '^plugins/([^\/.]*)(.*)': `${kibanaDirectory}/src/legacy/core_plugins/$1/public$2`, '^plugins/xpack_main/(.*);': `${xPackKibanaDirectory}/legacy/plugins/xpack_main/public/$1`, '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': diff --git a/x-pack/legacy/plugins/watcher/README.md b/x-pack/legacy/plugins/watcher/README.md index bc19f4e4fd968..4f9111760a0a6 100644 --- a/x-pack/legacy/plugins/watcher/README.md +++ b/x-pack/legacy/plugins/watcher/README.md @@ -4,32 +4,19 @@ This plugins adopts some conventions in addition to or in place of conventions i ## Folder structure ``` +common/ + constants/ // constants used across client and server + lib/ // helpers used across client and server + types/ // TS definitions public/ - directives/ (This is the same as *A, but is for directives that are used cross-view) - services/ - watch/ - index.js (no code here; only `export from watch.js`) - watch.js - notifications/ - index.js (no code here; only `export from notifications.js`) - notifications.js - ... - views/ - edit/ - ... - list/ - directives/ (*A) - my_directive_name/ - directives/ (Subcomponents of my_directive_name are defined here, and this follows the same structure as *A) - index.js (no code here; only `export from my_directive_name.js`) - my_directive_name.js - my_directive_name.html - index.js (imports the directives in this folder, i.e.,my_directive_name) - routes/ - index.js (no code here; only imports routes.js) - routes.js - index.js - + components/ // common React components + constants/ // constants used on the client + lib/ // helpers used on the client + models/ // client models + sections/ // Sections of the app with corresponding React components + watch_edit + watch_list + watch_status server/ lib/ screenshots/ @@ -44,90 +31,6 @@ server/ say_hello.js ``` -## Data Services - -api calls: -- GET /watch/{id} -- PUT /watch/{id} - -using the service - -```js -import watch from './services/watch' - -watch.get(...) -watch.put(...) -``` - -## Services / Lib -- Shared code that requires state should be made into a service. For example, see `pageService`. -- Shared code that doesn't require state (e.g. a simple helper function) should be made a lib. -For example, see `clamp`. - -## Controller classes -- All functions in controller classes should be defined as arrow function constants. This is to ensure the `this` context is consistent, regardless of where it is being called. - -GOOD -``` - controller: class WatchListController { - onQueryChanged = (query) => {...}; - } -``` - -BAD -``` - controller: class WatchListController { - onQueryChanged(query) {...}; - } -``` - -``` - controller: class WatchListController { - constructor() { - this.onQueryChanged = (query) => {...}; - } - } -``` - -- Constructors should be used to initialize state and define $scope.$watch(es) - -GOOD -``` - controllerAs: 'watchList', - bindToController: true, - scope: { foo: '=' }, - controller: class WatchListController { - constructor() { - this.foo = this.foo || 'default'; - - $scope.$watch('watchList.foo', () => { - console.log('foo changed, fool'); - }); - } - } -``` -## Event handlers - -Event handler functions should be named with the following pattern: - -> `on`, in present tense - -In case there is ambiguity about _what_ the verb is acting upon a noun should be included like so: - -> `on`, in present tense - -GOOD -``` -onDelete -onWatchDelete -``` - -BAD -``` -onDeleted -onWatchDeleted -``` - ## Data Flow We have a layered architecture in the Watcher UI codebase, with each layer performing a specific function to the data as it flows through it. diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/constants.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/constants.ts new file mode 100644 index 0000000000000..3f7c1fbe1c57f --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/constants.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getWatch } from '../../../test/fixtures'; + +export const WATCH_ID = 'my-test-watch'; + +export const WATCH = { watch: getWatch({ id: WATCH_ID }) }; diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/http_requests.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/http_requests.ts new file mode 100644 index 0000000000000..2170559dace5a --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/http_requests.ts @@ -0,0 +1,158 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import sinon, { SinonFakeServer } from 'sinon'; +import { ROUTES } from '../../../common/constants'; + +const { API_ROOT } = ROUTES; + +type HttpResponse = Record | any[]; + +const mockResponse = (defaultResponse: HttpResponse, response: HttpResponse) => [ + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({ ...defaultResponse, ...response }), +]; + +// Register helpers to mock HTTP Requests +const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { + const setLoadWatchesResponse = (response: HttpResponse = {}) => { + const defaultResponse = { watches: [] }; + + server.respondWith('GET', `${API_ROOT}/watches`, mockResponse(defaultResponse, response)); + }; + + const setLoadWatchResponse = (response: HttpResponse = {}) => { + const defaultResponse = { watch: {} }; + server.respondWith('GET', `${API_ROOT}/watch/:id`, mockResponse(defaultResponse, response)); + }; + + const setLoadWatchHistoryResponse = (response: HttpResponse = {}) => { + const defaultResponse = { watchHistoryItems: [] }; + server.respondWith( + 'GET', + `${API_ROOT}/watch/:id/history?startTime=*`, + mockResponse(defaultResponse, response) + ); + }; + + const setLoadWatchHistoryItemResponse = (response: HttpResponse = {}) => { + const defaultResponse = { watchHistoryItem: {} }; + server.respondWith('GET', `${API_ROOT}/history/:id`, mockResponse(defaultResponse, response)); + }; + + const setDeleteWatchResponse = (response?: HttpResponse, error?: any) => { + const status = error ? error.status || 400 : 200; + const body = error ? JSON.stringify(error.body) : JSON.stringify(response); + + server.respondWith('POST', `${API_ROOT}/watches/delete`, [ + status, + { 'Content-Type': 'application/json' }, + body, + ]); + }; + + const setSaveWatchResponse = (id: string, response?: HttpResponse, error?: any) => { + const status = error ? error.status || 400 : 200; + const body = error ? JSON.stringify(error.body) : JSON.stringify(response); + + server.respondWith('PUT', `${API_ROOT}/watch/${id}`, [ + status, + { 'Content-Type': 'application/json' }, + body, + ]); + }; + + const setLoadExecutionResultResponse = (response: HttpResponse = {}) => { + const defaultResponse = { watchHistoryItem: {} }; + server.respondWith('PUT', `${API_ROOT}/watch/execute`, mockResponse(defaultResponse, response)); + }; + + const setLoadMatchingIndicesResponse = (response: HttpResponse = {}) => { + const defaultResponse = { indices: [] }; + server.respondWith('POST', `${API_ROOT}/indices`, mockResponse(defaultResponse, response)); + }; + + const setLoadEsFieldsResponse = (response: HttpResponse = {}) => { + const defaultResponse = { fields: [] }; + server.respondWith('POST', `${API_ROOT}/fields`, mockResponse(defaultResponse, response)); + }; + + const setLoadSettingsResponse = (response: HttpResponse = {}) => { + const defaultResponse = { action_types: {} }; + server.respondWith('GET', `${API_ROOT}/settings`, mockResponse(defaultResponse, response)); + }; + + const setLoadWatchVisualizeResponse = (response: HttpResponse = {}) => { + const defaultResponse = { visualizeData: {} }; + server.respondWith( + 'POST', + `${API_ROOT}/watch/visualize`, + mockResponse(defaultResponse, response) + ); + }; + + const setDeactivateWatchResponse = (response: HttpResponse = {}) => { + const defaultResponse = { watchStatus: {} }; + server.respondWith( + 'PUT', + `${API_ROOT}/watch/:id/deactivate`, + mockResponse(defaultResponse, response) + ); + }; + + const setActivateWatchResponse = (response: HttpResponse = {}) => { + const defaultResponse = { watchStatus: {} }; + server.respondWith( + 'PUT', + `${API_ROOT}/watch/:id/activate`, + mockResponse(defaultResponse, response) + ); + }; + + const setAcknowledgeWatchResponse = (response: HttpResponse = {}) => { + const defaultResponse = { watchStatus: {} }; + server.respondWith( + 'PUT', + `${API_ROOT}/watch/:id/action/:actionId/acknowledge`, + mockResponse(defaultResponse, response) + ); + }; + + return { + setLoadWatchesResponse, + setLoadWatchResponse, + setLoadWatchHistoryResponse, + setLoadWatchHistoryItemResponse, + setDeleteWatchResponse, + setSaveWatchResponse, + setLoadExecutionResultResponse, + setLoadMatchingIndicesResponse, + setLoadEsFieldsResponse, + setLoadSettingsResponse, + setLoadWatchVisualizeResponse, + setDeactivateWatchResponse, + setActivateWatchResponse, + setAcknowledgeWatchResponse, + }; +}; + +export const init = () => { + const server = sinon.fakeServer.create(); + server.respondImmediately = true; + + // Define default response for unhandled requests. + // We make requests to APIs which don't impact the component under test, e.g. UI metric telemetry, + // and we can mock them all with a 200 instead of mocking each one individually. + server.respondWith([200, {}, 'DefaultResponse']); + + const httpRequestsMockHelpers = registerHttpRequestMockHelpers(server); + + return { + server, + httpRequestsMockHelpers, + }; +}; diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/index.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/index.ts new file mode 100644 index 0000000000000..ad005078db0a8 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { setup as watchListSetup } from './watch_list.helpers'; +import { setup as watchStatusSetup } from './watch_status.helpers'; +import { setup as watchCreateJsonSetup } from './watch_create_json.helpers'; +import { setup as watchCreateThresholdSetup } from './watch_create_threshold.helpers'; +import { setup as watchEditSetup } from './watch_edit.helpers'; + +export { nextTick, getRandomString, findTestSubject, TestBed } from '../../../../../../test_utils'; + +export { setupEnvironment } from './setup_environment'; + +export const pageHelpers = { + watchList: { setup: watchListSetup }, + watchStatus: { setup: watchStatusSetup }, + watchCreateJson: { setup: watchCreateJsonSetup }, + watchCreateThreshold: { setup: watchCreateThresholdSetup }, + watchEdit: { setup: watchEditSetup }, +}; diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/setup_environment.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/setup_environment.ts new file mode 100644 index 0000000000000..806840a7821fd --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/setup_environment.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import axios from 'axios'; +import axiosXhrAdapter from 'axios/lib/adapters/xhr'; +import { init as initHttpRequests } from './http_requests'; +import { setHttpClient, setSavedObjectsClient } from '../../../public/lib/api'; + +const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); + +const mockSavedObjectsClient = () => { + return { + find: (_params?: any) => {}, + }; +}; + +export const setupEnvironment = () => { + const { server, httpRequestsMockHelpers } = initHttpRequests(); + + // @ts-ignore + setHttpClient(mockHttpClient); + + setSavedObjectsClient(mockSavedObjectsClient()); + + return { + server, + httpRequestsMockHelpers, + }; +}; diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_create_json.helpers.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_create_json.helpers.ts new file mode 100644 index 0000000000000..bea215281a4bc --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_create_json.helpers.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { registerTestBed, TestBed, TestBedConfig } from '../../../../../../test_utils'; +import { WatchEdit } from '../../../public/sections/watch_edit/components/watch_edit'; +import { ROUTES, WATCH_TYPES } from '../../../common/constants'; +import { registerRouter } from '../../../public/lib/navigation'; + +const testBedConfig: TestBedConfig = { + memoryRouter: { + onRouter: router => registerRouter(router), + initialEntries: [`${ROUTES.API_ROOT}/watches/new-watch/${WATCH_TYPES.JSON}`], + componentRoutePath: `${ROUTES.API_ROOT}/watches/new-watch/:type`, + }, + doMountAsync: true, +}; + +const initTestBed = registerTestBed(WatchEdit, testBedConfig); + +export interface WatchCreateJsonTestBed extends TestBed { + actions: { + selectTab: (tab: 'edit' | 'simulate') => void; + clickSubmitButton: () => void; + clickSimulateButton: () => void; + }; +} + +export const setup = async (): Promise => { + const testBed = await initTestBed(); + + /** + * User Actions + */ + + const selectTab = (tab: 'edit' | 'simulate') => { + const tabs = ['edit', 'simulate']; + + testBed + .find('tab') + .at(tabs.indexOf(tab)) + .simulate('click'); + }; + + const clickSubmitButton = () => { + testBed.find('saveWatchButton').simulate('click'); + }; + + const clickSimulateButton = () => { + testBed.find('simulateWatchButton').simulate('click'); + }; + + return { + ...testBed, + actions: { + selectTab, + clickSubmitButton, + clickSimulateButton, + }, + }; +}; + +type WatchCreateJsonTestSubjects = TestSubjects; + +export type TestSubjects = + | 'actionModesSelect' + | 'idInput' + | 'ignoreConditionSwitch' + | 'jsonEditor' + | 'jsonWatchForm' + | 'jsonWatchSimulateForm' + | 'nameInput' + | 'pageTitle' + | 'saveWatchButton' + | 'scheduledTimeInput' + | 'sectionError' + | 'sectionLoading' + | 'simulateResultsFlyout' + | 'simulateResultsFlyoutTitle' + | 'simulateWatchButton' + | 'tab' + | 'triggeredTimeInput'; diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_create_threshold.helpers.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_create_threshold.helpers.ts new file mode 100644 index 0000000000000..a023358a7f0a6 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_create_threshold.helpers.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { registerTestBed, TestBed, TestBedConfig } from '../../../../../../test_utils'; +import { WatchEdit } from '../../../public/sections/watch_edit/components/watch_edit'; +import { ROUTES, WATCH_TYPES } from '../../../common/constants'; +import { registerRouter } from '../../../public/lib/navigation'; + +const testBedConfig: TestBedConfig = { + memoryRouter: { + onRouter: router => registerRouter(router), + initialEntries: [`${ROUTES.API_ROOT}/watches/new-watch/${WATCH_TYPES.THRESHOLD}`], + componentRoutePath: `${ROUTES.API_ROOT}/watches/new-watch/:type`, + }, + doMountAsync: true, +}; + +const initTestBed = registerTestBed(WatchEdit, testBedConfig); + +export interface WatchCreateThresholdTestBed extends TestBed { + actions: { + clickSubmitButton: () => void; + clickAddActionButton: () => void; + clickActionLink: ( + actionType: 'logging' | 'email' | 'webhook' | 'index' | 'slack' | 'jira' | 'pagerduty' + ) => void; + clickSimulateButton: () => void; + }; +} + +export const setup = async (): Promise => { + const testBed = await initTestBed(); + + /** + * User Actions + */ + + const clickSubmitButton = () => { + testBed.find('saveWatchButton').simulate('click'); + }; + + const clickAddActionButton = () => { + testBed.find('addWatchActionButton').simulate('click'); + }; + + const clickSimulateButton = () => { + testBed.find('simulateActionButton').simulate('click'); + }; + + const clickActionLink = ( + actionType: 'logging' | 'email' | 'webhook' | 'index' | 'slack' | 'jira' | 'pagerduty' + ) => { + testBed.find(`${actionType}ActionButton`).simulate('click'); + }; + + return { + ...testBed, + actions: { + clickSubmitButton, + clickAddActionButton, + clickActionLink, + clickSimulateButton, + }, + }; +}; + +type WatchCreateThresholdTestSubjects = TestSubjects; + +export type TestSubjects = + | 'addWatchActionButton' + | 'emailBodyInput' + | 'emailSubjectInput' + | 'indexInput' + | 'indicesComboBox' + | 'jiraIssueTypeInput' + | 'jiraProjectKeyInput' + | 'jiraSummaryInput' + | 'loggingTextInput' + | 'mockComboBox' + | 'nameInput' + | 'pagerdutyDescriptionInput' + | 'pageTitle' + | 'saveWatchButton' + | 'sectionLoading' + | 'simulateActionButton' + | 'slackMessageTextarea' + | 'slackRecipientComboBox' + | 'toEmailAddressInput' + | 'triggerIntervalSizeInput' + | 'watchActionAccordion' + | 'watchActionAccordion.mockComboBox' + | 'watchActionsPanel' + | 'watchConditionTitle' + | 'watchTimeFieldSelect' + | 'watchVisualizationChart' + | 'webhookBodyEditor' + | 'webhookHostInput' + | 'webhookPasswordInput' + | 'webhookPathInput' + | 'webhookPortInput' + | 'webhookMethodSelect' + | 'webhookUsernameInput'; diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_edit.helpers.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_edit.helpers.ts new file mode 100644 index 0000000000000..d0b458e30c70e --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_edit.helpers.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { registerTestBed, TestBed, TestBedConfig } from '../../../../../../test_utils'; +import { WatchEdit } from '../../../public/sections/watch_edit/components/watch_edit'; +import { ROUTES } from '../../../common/constants'; +import { registerRouter } from '../../../public/lib/navigation'; +import { WATCH_ID } from './constants'; + +const testBedConfig: TestBedConfig = { + memoryRouter: { + onRouter: router => registerRouter(router), + initialEntries: [`${ROUTES.API_ROOT}/watches/watch/${WATCH_ID}/edit`], + componentRoutePath: `${ROUTES.API_ROOT}/watches/watch/:id/edit`, + }, + doMountAsync: true, +}; + +const initTestBed = registerTestBed(WatchEdit, testBedConfig); + +export interface WatchEditTestBed extends TestBed { + actions: { + clickSubmitButton: () => void; + }; +} + +export const setup = async (): Promise => { + const testBed = await initTestBed(); + + /** + * User Actions + */ + + const clickSubmitButton = () => { + testBed.find('saveWatchButton').simulate('click'); + }; + + return { + ...testBed, + actions: { + clickSubmitButton, + }, + }; +}; + +type WatchEditSubjects = TestSubjects; + +export type TestSubjects = + | 'idInput' + | 'jsonWatchForm' + | 'nameInput' + | 'pageTitle' + | 'thresholdWatchForm' + | 'watchTimeFieldSelect'; diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_list.helpers.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_list.helpers.ts new file mode 100644 index 0000000000000..7319806737a6f --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_list.helpers.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { act } from 'react-dom/test-utils'; + +import { + registerTestBed, + findTestSubject, + TestBed, + TestBedConfig, + nextTick, +} from '../../../../../../test_utils'; +import { WatchList } from '../../../public/sections/watch_list/components/watch_list'; +import { ROUTES } from '../../../common/constants'; + +const testBedConfig: TestBedConfig = { + memoryRouter: { + initialEntries: [`${ROUTES.API_ROOT}/watches`], + }, + doMountAsync: true, +}; + +const initTestBed = registerTestBed(WatchList, testBedConfig); + +export interface WatchListTestBed extends TestBed { + actions: { + selectWatchAt: (index: number) => void; + clickWatchAt: (index: number) => void; + clickWatchActionAt: (index: number, action: 'delete' | 'edit') => void; + }; +} + +export const setup = async (): Promise => { + const testBed = await initTestBed(); + + /** + * User Actions + */ + + const selectWatchAt = (index: number) => { + const { rows } = testBed.table.getMetaData('watchesTable'); + const row = rows[index]; + const checkBox = row.reactWrapper.find('input').hostNodes(); + checkBox.simulate('change', { target: { checked: true } }); + }; + + const clickWatchAt = async (index: number) => { + const { rows } = testBed.table.getMetaData('watchesTable'); + const watchesLink = findTestSubject(rows[index].reactWrapper, 'watchesLink'); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + const { href } = watchesLink.props(); + testBed.router.navigateTo(href!); + await nextTick(); + testBed.component.update(); + }); + }; + + const clickWatchActionAt = async (index: number, action: 'delete' | 'edit') => { + const { component, table } = testBed; + const { rows } = table.getMetaData('watchesTable'); + const currentRow = rows[index]; + const lastColumn = currentRow.columns[currentRow.columns.length - 1].reactWrapper; + const button = findTestSubject(lastColumn, `${action}WatchButton`); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + button.simulate('click'); + component.update(); + }); + }; + + return { + ...testBed, + actions: { + selectWatchAt, + clickWatchAt, + clickWatchActionAt, + }, + }; +}; + +type WatchListTestSubjects = TestSubjects; + +export type TestSubjects = + | 'appTitle' + | 'documentationLink' + | 'watchesTable' + | 'cell' + | 'row' + | 'deleteWatchButton' + | 'createWatchButton' + | 'emptyPrompt' + | 'emptyPrompt.createWatchButton' + | 'editWatchButton'; diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_status.helpers.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_status.helpers.ts new file mode 100644 index 0000000000000..ac4e4eab1dcc8 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/helpers/watch_status.helpers.ts @@ -0,0 +1,141 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { act } from 'react-dom/test-utils'; + +import { + registerTestBed, + findTestSubject, + TestBed, + TestBedConfig, + nextTick, +} from '../../../../../../test_utils'; +import { WatchStatus } from '../../../public/sections/watch_status/components/watch_status'; +import { ROUTES } from '../../../common/constants'; +import { WATCH_ID } from './constants'; + +const testBedConfig: TestBedConfig = { + memoryRouter: { + initialEntries: [`${ROUTES.API_ROOT}/watches/watch/${WATCH_ID}/status`], + componentRoutePath: `${ROUTES.API_ROOT}/watches/watch/:id/status`, + }, + doMountAsync: true, +}; + +const initTestBed = registerTestBed(WatchStatus, testBedConfig); + +export interface WatchStatusTestBed extends TestBed { + actions: { + selectTab: (tab: 'execution history' | 'action statuses') => void; + clickToggleActivationButton: () => void; + clickAcknowledgeButton: (index: number) => void; + clickDeleteWatchButton: () => void; + clickWatchExecutionAt: (index: number, tableCellText: string) => void; + }; +} + +export const setup = async (): Promise => { + const testBed = await initTestBed(); + + /** + * User Actions + */ + + const selectTab = (tab: 'execution history' | 'action statuses') => { + const tabs = ['execution history', 'action statuses']; + + testBed + .find('tab') + .at(tabs.indexOf(tab)) + .simulate('click'); + }; + + const clickToggleActivationButton = async () => { + const { component } = testBed; + const button = testBed.find('toggleWatchActivationButton'); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + button.simulate('click'); + component.update(); + }); + }; + + const clickAcknowledgeButton = async (index: number) => { + const { component, table } = testBed; + const { rows } = table.getMetaData('watchActionStatusTable'); + const currentRow = rows[index]; + const lastColumn = currentRow.columns[currentRow.columns.length - 1].reactWrapper; + const button = findTestSubject(lastColumn, 'acknowledgeWatchButton'); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + button.simulate('click'); + component.update(); + }); + }; + + const clickDeleteWatchButton = async () => { + const { component } = testBed; + const button = testBed.find('deleteWatchButton'); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + button.simulate('click'); + component.update(); + }); + }; + + const clickWatchExecutionAt = async (index: number, tableCellText: string) => { + const { component, table } = testBed; + const { rows } = table.getMetaData('watchHistoryTable'); + const currentRow = rows[index]; + const firstColumn = currentRow.columns[0].reactWrapper; + + const button = findTestSubject(firstColumn, `watchStartTimeColumn-${tableCellText}`); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + button.simulate('click'); + await nextTick(100); + component.update(); + }); + }; + + return { + ...testBed, + actions: { + selectTab, + clickToggleActivationButton, + clickAcknowledgeButton, + clickDeleteWatchButton, + clickWatchExecutionAt, + }, + }; +}; + +type WatchStatusTestSubjects = TestSubjects; + +export type TestSubjects = + | 'acknowledgeWatchButton' + | 'actionErrorsButton' + | 'actionErrorsFlyout' + | 'actionErrorsFlyout.errorMessage' + | 'actionErrorsFlyout.title' + | 'deleteWatchButton' + | 'pageTitle' + | 'tab' + | 'toggleWatchActivationButton' + | 'watchActionStatusTable' + | 'watchActionsTable' + | 'watchDetailSection' + | 'watchHistoryDetailFlyout' + | 'watchHistoryDetailFlyout.title' + | 'watchHistorySection' + | 'watchHistoryErrorDetailFlyout' + | 'watchHistoryErrorDetailFlyout.errorMessage' + | 'watchHistoryErrorDetailFlyout.title' + | 'watchHistoryTable'; diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts new file mode 100644 index 0000000000000..fbcd940ed65bb --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts @@ -0,0 +1,284 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { act } from 'react-dom/test-utils'; +import { setupEnvironment, pageHelpers, nextTick } from './helpers'; +import { WatchCreateJsonTestBed } from './helpers/watch_create_json.helpers'; +import { WATCH } from './helpers/constants'; +import defaultWatchJson from '../../public/models/watch/default_watch.json'; +import { getExecuteDetails } from '../../test/fixtures'; + +jest.mock('ui/chrome', () => ({ + breadcrumbs: { set: () => {} }, + addBasePath: (path: string) => path || '/api/watcher', +})); + +jest.mock('ui/time_buckets', () => {}); + +const { setup } = pageHelpers.watchCreateJson; + +describe.skip(' create route', () => { + const { server, httpRequestsMockHelpers } = setupEnvironment(); + let testBed: WatchCreateJsonTestBed; + + afterAll(() => { + server.restore(); + }); + + describe('on component mount', () => { + beforeEach(async () => { + testBed = await setup(); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + const { component } = testBed; + await nextTick(); + component.update(); + }); + }); + + test('should set the correct page title', () => { + const { find } = testBed; + expect(find('pageTitle').text()).toBe('Create advanced watch'); + }); + + describe('tabs', () => { + test('should have 2 tabs', () => { + const { find } = testBed; + + expect(find('tab').length).toBe(2); + expect(find('tab').map(t => t.text())).toEqual(['Edit', 'Simulate']); + }); + + test('should navigate to the "Simulate" tab', () => { + const { exists, actions } = testBed; + + expect(exists('jsonWatchForm')).toBe(true); + expect(exists('jsonWatchSimulateForm')).toBe(false); + + actions.selectTab('simulate'); + + expect(exists('jsonWatchForm')).toBe(false); + expect(exists('jsonWatchSimulateForm')).toBe(true); + }); + }); + + describe('create', () => { + describe('form validation', () => { + test('should not allow empty ID field', () => { + const { form, actions } = testBed; + form.setInputValue('idInput', ''); + + actions.clickSubmitButton(); + + expect(form.getErrorsMessages()).toContain('ID is required'); + }); + test('should not allow invalid characters for ID field', () => { + const { form, actions } = testBed; + form.setInputValue('idInput', 'invalid$id*field/'); + + actions.clickSubmitButton(); + + expect(form.getErrorsMessages()).toContain( + 'ID can only contain letters, underscores, dashes, and numbers.' + ); + }); + }); + + describe('form payload & API errors', () => { + test('should send the correct payload', async () => { + const { form, actions } = testBed; + const { watch } = WATCH; + + form.setInputValue('nameInput', watch.name); + form.setInputValue('idInput', watch.id); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSubmitButton(); + await nextTick(); + }); + + const latestRequest = server.requests[server.requests.length - 1]; + + const DEFAULT_LOGGING_ACTION_ID = 'logging_1'; + const DEFAULT_LOGGING_ACTION_TYPE = 'logging'; + const DEFAULT_LOGGING_ACTION_TEXT = + 'There are {{ctx.payload.hits.total}} documents in your index. Threshold is 10.'; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + id: watch.id, + name: watch.name, + type: watch.type, + isNew: true, + actions: [ + { + id: DEFAULT_LOGGING_ACTION_ID, + type: DEFAULT_LOGGING_ACTION_TYPE, + text: DEFAULT_LOGGING_ACTION_TEXT, + [DEFAULT_LOGGING_ACTION_TYPE]: { + text: DEFAULT_LOGGING_ACTION_TEXT, + }, + }, + ], + watch: defaultWatchJson, + }) + ); + }); + + test('should surface the API errors from the "save" HTTP request', async () => { + const { form, actions, component, exists, find } = testBed; + const { watch } = WATCH; + + form.setInputValue('nameInput', watch.name); + form.setInputValue('idInput', watch.id); + + const error = { + status: 400, + error: 'Bad request', + message: 'Watch payload is invalid', + }; + + httpRequestsMockHelpers.setSaveWatchResponse(watch.id, undefined, { body: error }); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSubmitButton(); + await nextTick(); + component.update(); + }); + + expect(exists('sectionError')).toBe(true); + expect(find('sectionError').text()).toContain(error.message); + }); + }); + }); + + describe('simulate', () => { + beforeEach(() => { + const { actions, form } = testBed; + + // Set watch id (required field) and switch to simulate tab + form.setInputValue('idInput', WATCH.watch.id); + actions.selectTab('simulate'); + }); + + describe('form payload & API errors', () => { + test('should execute a watch with no input', async () => { + const { actions } = testBed; + const { + watch: { id, type }, + } = WATCH; + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSimulateButton(); + await nextTick(); + }); + + const latestRequest = server.requests[server.requests.length - 1]; + + const actionModes = Object.keys(defaultWatchJson.actions).reduce( + (actionAccum: any, action) => { + actionAccum[action] = 'simulate'; + return actionAccum; + }, + {} + ); + + const executedWatch = { + id, + type, + isNew: true, + actions: [], + watch: defaultWatchJson, + }; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + executeDetails: getExecuteDetails({ + actionModes, + }), + watch: executedWatch, + }) + ); + }); + + test('should execute a watch with a valid payload', async () => { + const { actions, form, find, exists, component } = testBed; + const { + watch: { id, type }, + } = WATCH; + + const SCHEDULED_TIME = '5'; + const TRIGGERED_TIME = '5'; + const IGNORE_CONDITION = true; + const ACTION_MODE = 'force_execute'; + + form.setInputValue('scheduledTimeInput', SCHEDULED_TIME); + form.setInputValue('triggeredTimeInput', TRIGGERED_TIME); + form.toggleEuiSwitch('ignoreConditionSwitch'); + form.setInputValue('actionModesSelect', ACTION_MODE); + + expect(exists('simulateResultsFlyout')).toBe(false); + + httpRequestsMockHelpers.setLoadExecutionResultResponse({ + watchHistoryItem: { + details: {}, + watchStatus: { + actionStatuses: [], + }, + }, + }); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSimulateButton(); + await nextTick(); + component.update(); + }); + + const latestRequest = server.requests[server.requests.length - 1]; + + const actionModes = Object.keys(defaultWatchJson.actions).reduce( + (actionAccum: any, action) => { + actionAccum[action] = ACTION_MODE; + return actionAccum; + }, + {} + ); + + const executedWatch = { + id, + type, + isNew: true, + actions: [], + watch: defaultWatchJson, + }; + + const triggeredTime = `now+${TRIGGERED_TIME}s`; + const scheduledTime = `now+${SCHEDULED_TIME}s`; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + executeDetails: getExecuteDetails({ + triggerData: { + triggeredTime, + scheduledTime, + }, + ignoreCondition: IGNORE_CONDITION, + actionModes, + }), + watch: executedWatch, + }) + ); + expect(exists('simulateResultsFlyout')).toBe(true); + expect(find('simulateResultsFlyoutTitle').text()).toEqual('Simulation results'); + }); + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx b/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx new file mode 100644 index 0000000000000..352cbf5c43ba0 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx @@ -0,0 +1,786 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { act } from 'react-dom/test-utils'; +import axiosXhrAdapter from 'axios/lib/adapters/xhr'; +import axios from 'axios'; +import { setupEnvironment, pageHelpers, nextTick } from './helpers'; +import { WatchCreateThresholdTestBed } from './helpers/watch_create_threshold.helpers'; +import { getExecuteDetails } from '../../test/fixtures'; +import { WATCH_TYPES } from '../../common/constants'; + +const WATCH_NAME = 'my_test_watch'; + +const WATCH_TIME_FIELD = '@timestamp'; + +const MATCH_INDICES = ['index1']; + +const ES_FIELDS = [{ name: '@timestamp', type: 'date' }]; + +const SETTINGS = { + action_types: { + email: { enabled: true }, + index: { enabled: true }, + jira: { enabled: true }, + logging: { enabled: true }, + pagerduty: { enabled: true }, + slack: { enabled: true }, + webhook: { enabled: true }, + }, +}; + +const WATCH_VISUALIZE_DATA = { + count: [[1559404800000, 14], [1559448000000, 196], [1559491200000, 44]], +}; + +const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); + +jest.mock('ui/chrome', () => ({ + breadcrumbs: { set: () => {} }, + addBasePath: (path: string) => path || '/api/watcher', + getUiSettingsClient: () => ({ + get: () => {}, + isDefault: () => true, + }), +})); + +jest.mock('ui/time_buckets', () => { + class MockTimeBuckets { + setBounds(_domain: any) { + return {}; + } + getInterval() { + return { + expression: {}, + }; + } + } + return { TimeBuckets: MockTimeBuckets }; +}); + +jest.mock('../../public/lib/api', () => ({ + ...jest.requireActual('../../public/lib/api'), + loadIndexPatterns: async () => { + const INDEX_PATTERNS = [ + { attributes: { title: 'index1' } }, + { attributes: { title: 'index2' } }, + { attributes: { title: 'index3' } }, + ]; + return await INDEX_PATTERNS; + }, + getHttpClient: () => mockHttpClient, +})); + +jest.mock('@elastic/eui', () => ({ + ...jest.requireActual('@elastic/eui'), + // Mocking EuiComboBox, as it utilizes "react-virtualized" for rendering search suggestions, + // which does not produce a valid component wrapper + EuiComboBox: (props: any) => ( + { + props.onChange([syntheticEvent['0']]); + }} + /> + ), +})); + +const { setup } = pageHelpers.watchCreateThreshold; + +describe.skip(' create route', () => { + const { server, httpRequestsMockHelpers } = setupEnvironment(); + let testBed: WatchCreateThresholdTestBed; + + afterAll(() => { + server.restore(); + }); + + describe('on component mount', () => { + beforeEach(async () => { + testBed = await setup(); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + const { component } = testBed; + await nextTick(); + component.update(); + }); + }); + + test('should set the correct page title', () => { + const { find } = testBed; + + expect(find('pageTitle').text()).toBe('Create threshold alert'); + }); + + describe('create', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadMatchingIndicesResponse({ indices: MATCH_INDICES }); + httpRequestsMockHelpers.setLoadEsFieldsResponse({ fields: ES_FIELDS }); + httpRequestsMockHelpers.setLoadSettingsResponse(SETTINGS); + httpRequestsMockHelpers.setLoadWatchVisualizeResponse(WATCH_VISUALIZE_DATA); + + testBed = await setup(); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + await nextTick(); + testBed.component.update(); + }); + }); + + describe('form validation', () => { + test('should not allow empty name field', () => { + const { form } = testBed; + + form.setInputValue('nameInput', ''); + + expect(form.getErrorsMessages()).toContain('Name is required.'); + }); + + test('should not allow empty time field', () => { + const { form } = testBed; + + form.setInputValue('watchTimeFieldSelect', ''); + + expect(form.getErrorsMessages()).toContain('A time field is required.'); + }); + + test('should not allow empty interval size field', () => { + const { form } = testBed; + + form.setInputValue('triggerIntervalSizeInput', ''); + + expect(form.getErrorsMessages()).toContain('Interval size is required.'); + }); + + test('should not allow negative interval size field', () => { + const { form } = testBed; + + form.setInputValue('triggerIntervalSizeInput', '-1'); + + expect(form.getErrorsMessages()).toContain('Interval size cannot be a negative number.'); + }); + + test('should disable the Create button with invalid fields', () => { + const { find } = testBed; + + expect(find('saveWatchButton').props().disabled).toEqual(true); + }); + + test('it should enable the Create button and render additonal content with valid fields', async () => { + const { form, find, component, exists } = testBed; + + form.setInputValue('nameInput', 'my_test_watch'); + find('mockComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox + form.setInputValue('watchTimeFieldSelect', '@timestamp'); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + await nextTick(); + component.update(); + }); + + expect(find('saveWatchButton').props().disabled).toEqual(false); + + expect(find('watchConditionTitle').text()).toBe('Match the following condition'); + expect(exists('watchVisualizationChart')).toBe(true); + expect(exists('watchActionsPanel')).toBe(true); + }); + }); + + describe('actions', () => { + beforeEach(async () => { + const { form, find, component } = testBed; + + // Set up valid fields needed for actions component to render + form.setInputValue('nameInput', WATCH_NAME); + find('mockComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox + form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + await nextTick(); + component.update(); + }); + }); + + test('should simulate a logging action', async () => { + const { form, find, actions, exists } = testBed; + + const LOGGING_MESSAGE = 'test log message'; + + actions.clickAddActionButton(); + actions.clickActionLink('logging'); + + expect(exists('watchActionAccordion')).toBe(true); + + // First, provide invalid field and verify + form.setInputValue('loggingTextInput', ''); + + expect(form.getErrorsMessages()).toContain('Log text is required.'); + expect(find('simulateActionButton').props().disabled).toEqual(true); + + // Next, provide valid field and verify + form.setInputValue('loggingTextInput', LOGGING_MESSAGE); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSimulateButton(); + await nextTick(); + }); + + // Verify request + const latestRequest = server.requests[server.requests.length - 1]; + + const thresholdWatch = { + id: JSON.parse(latestRequest.requestBody).watch.id, // watch ID is created dynamically + name: WATCH_NAME, + type: WATCH_TYPES.THRESHOLD, + isNew: true, + actions: [ + { + id: 'logging_1', + type: 'logging', + text: LOGGING_MESSAGE, + logging: { + text: LOGGING_MESSAGE, + }, + }, + ], + index: MATCH_INDICES, + timeField: WATCH_TIME_FIELD, + triggerIntervalSize: 1, + triggerIntervalUnit: 'm', + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + threshold: 1000, + }; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + executeDetails: getExecuteDetails({ + actionModes: { + logging_1: 'force_execute', + }, + ignoreCondition: true, + recordExecution: false, + }), + watch: thresholdWatch, + }) + ); + }); + + test('should simulate an index action', async () => { + const { form, find, actions, exists } = testBed; + + const INDEX = 'my_index'; + + actions.clickAddActionButton(); + actions.clickActionLink('index'); + + expect(exists('watchActionAccordion')).toBe(true); + + // First, provide invalid field and verify + form.setInputValue('indexInput', ''); + + expect(form.getErrorsMessages()).toContain('Index name is required.'); + expect(find('simulateActionButton').props().disabled).toEqual(true); + + // Next, provide valid field and verify + form.setInputValue('indexInput', INDEX); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSimulateButton(); + await nextTick(); + }); + + // Verify request + const latestRequest = server.requests[server.requests.length - 1]; + + const thresholdWatch = { + id: JSON.parse(latestRequest.requestBody).watch.id, // watch ID is created dynamically + name: WATCH_NAME, + type: WATCH_TYPES.THRESHOLD, + isNew: true, + actions: [ + { + id: 'index_1', + type: 'index', + index: { + index: INDEX, + }, + }, + ], + index: MATCH_INDICES, + timeField: WATCH_TIME_FIELD, + triggerIntervalSize: 1, + triggerIntervalUnit: 'm', + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + threshold: 1000, + }; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + executeDetails: getExecuteDetails({ + actionModes: { + index_1: 'force_execute', + }, + ignoreCondition: true, + recordExecution: false, + }), + watch: thresholdWatch, + }) + ); + }); + + test('should simulate a Slack action', async () => { + const { form, actions, exists } = testBed; + + const SLACK_MESSAGE = 'test slack message'; + + actions.clickAddActionButton(); + actions.clickActionLink('slack'); + + expect(exists('watchActionAccordion')).toBe(true); + + form.setInputValue('slackMessageTextarea', SLACK_MESSAGE); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSimulateButton(); + await nextTick(); + }); + + // Verify request + const latestRequest = server.requests[server.requests.length - 1]; + + const thresholdWatch = { + id: JSON.parse(latestRequest.requestBody).watch.id, // watch ID is created dynamically + name: WATCH_NAME, + type: WATCH_TYPES.THRESHOLD, + isNew: true, + actions: [ + { + id: 'slack_1', + type: 'slack', + text: SLACK_MESSAGE, + slack: { + message: { + text: SLACK_MESSAGE, + }, + }, + }, + ], + index: MATCH_INDICES, + timeField: WATCH_TIME_FIELD, + triggerIntervalSize: 1, + triggerIntervalUnit: 'm', + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + threshold: 1000, + }; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + executeDetails: getExecuteDetails({ + actionModes: { + slack_1: 'force_execute', + }, + ignoreCondition: true, + recordExecution: false, + }), + watch: thresholdWatch, + }) + ); + }); + + test('should simulate an email action', async () => { + const { form, find, actions, exists } = testBed; + + const EMAIL_RECIPIENT = 'test@test.com'; + const EMAIL_SUBJECT = 'test email subject'; + const EMAIL_BODY = 'this is a test email body'; + + actions.clickAddActionButton(); + actions.clickActionLink('email'); + + expect(exists('watchActionAccordion')).toBe(true); + + // First, provide invalid fields and verify + form.setInputValue('emailBodyInput', ''); + + expect(form.getErrorsMessages()).toContain('Email body is required.'); + expect(find('simulateActionButton').props().disabled).toEqual(true); + + // Next, provide valid field and verify + find('watchActionAccordion.mockComboBox').simulate('change', [ + { label: EMAIL_RECIPIENT, value: EMAIL_RECIPIENT }, + ]); // Using mocked EuiComboBox + form.setInputValue('emailSubjectInput', EMAIL_SUBJECT); + form.setInputValue('emailBodyInput', EMAIL_BODY); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSimulateButton(); + await nextTick(); + }); + + // Verify request + const latestRequest = server.requests[server.requests.length - 1]; + + const thresholdWatch = { + id: JSON.parse(latestRequest.requestBody).watch.id, // watch ID is created dynamically + name: WATCH_NAME, + type: WATCH_TYPES.THRESHOLD, + isNew: true, + actions: [ + { + id: 'email_1', + type: 'email', + to: [EMAIL_RECIPIENT], + subject: EMAIL_SUBJECT, + body: EMAIL_BODY, + email: { + to: [EMAIL_RECIPIENT], + subject: EMAIL_SUBJECT, + body: { + text: EMAIL_BODY, + }, + }, + }, + ], + index: MATCH_INDICES, + timeField: WATCH_TIME_FIELD, + triggerIntervalSize: 1, + triggerIntervalUnit: 'm', + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + threshold: 1000, + }; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + executeDetails: getExecuteDetails({ + actionModes: { + email_1: 'force_execute', + }, + ignoreCondition: true, + recordExecution: false, + }), + watch: thresholdWatch, + }) + ); + }); + + test('should simulate a webhook action', async () => { + const { form, find, actions, exists } = testBed; + + const METHOD = 'put'; + const HOST = 'localhost'; + const PORT = '9200'; + const PATH = '/test'; + const USERNAME = 'test_user'; + const PASSWORD = 'test_password'; + + actions.clickAddActionButton(); + actions.clickActionLink('webhook'); + + expect(exists('watchActionAccordion')).toBe(true); + + // First, provide invalid fields and verify + form.setInputValue('webhookHostInput', ''); + form.setInputValue('webhookPortInput', ''); + + expect(form.getErrorsMessages()).toEqual([ + 'Webhook host is required.', + 'Webhook port is required.', + ]); + expect(find('simulateActionButton').props().disabled).toEqual(true); + + // Next, provide valid fields and verify + form.setInputValue('webhookMethodSelect', METHOD); + form.setInputValue('webhookHostInput', HOST); + form.setInputValue('webhookPortInput', PORT); + form.setInputValue('webhookPathInput', PATH); + form.setInputValue('webhookUsernameInput', USERNAME); + form.setInputValue('webhookPasswordInput', PASSWORD); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSimulateButton(); + await nextTick(); + }); + + // Verify request + const latestRequest = server.requests[server.requests.length - 1]; + + const thresholdWatch = { + id: JSON.parse(latestRequest.requestBody).watch.id, // watch ID is created dynamically + name: WATCH_NAME, + type: WATCH_TYPES.THRESHOLD, + isNew: true, + actions: [ + { + id: 'webhook_1', + type: 'webhook', + method: METHOD, + host: HOST, + port: Number(PORT), + path: PATH, + body: + '{\n "message": "Watch [{{ctx.metadata.name}}] has exceeded the threshold"\n}', // Default + username: USERNAME, + password: PASSWORD, + webhook: { + host: HOST, + port: Number(PORT), + }, + }, + ], + index: MATCH_INDICES, + timeField: WATCH_TIME_FIELD, + triggerIntervalSize: 1, + triggerIntervalUnit: 'm', + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + threshold: 1000, + }; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + executeDetails: getExecuteDetails({ + actionModes: { + webhook_1: 'force_execute', + }, + ignoreCondition: true, + recordExecution: false, + }), + watch: thresholdWatch, + }) + ); + }); + + test('should simulate a Jira action', async () => { + const { form, find, actions, exists } = testBed; + + const PROJECT_KEY = 'TEST_PROJECT_KEY'; + const ISSUE_TYPE = 'Bug'; + const SUMMARY = 'test Jira summary'; + + actions.clickAddActionButton(); + actions.clickActionLink('jira'); + + expect(exists('watchActionAccordion')).toBe(true); + + // First, provide invalid fields and verify + form.setInputValue('jiraProjectKeyInput', ''); + form.setInputValue('jiraIssueTypeInput', ''); + form.setInputValue('jiraSummaryInput', ''); + + expect(form.getErrorsMessages()).toEqual([ + 'Jira project key is required.', + 'Jira issue type is required.', + 'Jira summary is required.', + ]); + expect(find('simulateActionButton').props().disabled).toEqual(true); + + // Next, provide valid fields and verify + form.setInputValue('jiraProjectKeyInput', PROJECT_KEY); + form.setInputValue('jiraIssueTypeInput', ISSUE_TYPE); + form.setInputValue('jiraSummaryInput', SUMMARY); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSimulateButton(); + await nextTick(); + }); + + // Verify request + const latestRequest = server.requests[server.requests.length - 1]; + + const thresholdWatch = { + id: JSON.parse(latestRequest.requestBody).watch.id, // watch ID is created dynamically + name: WATCH_NAME, + type: WATCH_TYPES.THRESHOLD, + isNew: true, + actions: [ + { + id: 'jira_1', + type: 'jira', + projectKey: PROJECT_KEY, + issueType: ISSUE_TYPE, + summary: SUMMARY, + jira: { + fields: { + project: { + key: PROJECT_KEY, + }, + issuetype: { + name: ISSUE_TYPE, + }, + summary: SUMMARY, + }, + }, + }, + ], + index: MATCH_INDICES, + timeField: WATCH_TIME_FIELD, + triggerIntervalSize: 1, + triggerIntervalUnit: 'm', + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + threshold: 1000, + }; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + executeDetails: getExecuteDetails({ + actionModes: { + jira_1: 'force_execute', + }, + ignoreCondition: true, + recordExecution: false, + }), + watch: thresholdWatch, + }) + ); + }); + + test('should simulate a PagerDuty action', async () => { + const { form, actions, exists, find } = testBed; + + const DESCRIPTION = 'test pagerduty description'; + + actions.clickAddActionButton(); + actions.clickActionLink('pagerduty'); + + expect(exists('watchActionAccordion')).toBe(true); + + // First, provide invalid fields and verify + form.setInputValue('pagerdutyDescriptionInput', ''); + + expect(form.getErrorsMessages()).toContain('PagerDuty description is required.'); + expect(find('simulateActionButton').props().disabled).toEqual(true); + + // Next, provide valid fields and verify + form.setInputValue('pagerdutyDescriptionInput', DESCRIPTION); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSimulateButton(); + await nextTick(); + }); + + // Verify request + const latestRequest = server.requests[server.requests.length - 1]; + + const thresholdWatch = { + id: JSON.parse(latestRequest.requestBody).watch.id, // watch ID is created dynamically + name: WATCH_NAME, + type: WATCH_TYPES.THRESHOLD, + isNew: true, + actions: [ + { + id: 'pagerduty_1', + type: 'pagerduty', + description: DESCRIPTION, + pagerduty: { + description: DESCRIPTION, + }, + }, + ], + index: MATCH_INDICES, + timeField: WATCH_TIME_FIELD, + triggerIntervalSize: 1, + triggerIntervalUnit: 'm', + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + threshold: 1000, + }; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + executeDetails: getExecuteDetails({ + actionModes: { + pagerduty_1: 'force_execute', + }, + ignoreCondition: true, + recordExecution: false, + }), + watch: thresholdWatch, + }) + ); + }); + }); + + describe('form payload', () => { + test('should send the correct payload', async () => { + const { form, find, component, actions } = testBed; + + // Set up required fields + form.setInputValue('nameInput', WATCH_NAME); + find('mockComboBox').simulate('change', [{ label: 'index1', value: 'index1' }]); // Using mocked EuiComboBox + form.setInputValue('watchTimeFieldSelect', WATCH_TIME_FIELD); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + await nextTick(); + component.update(); + actions.clickSubmitButton(); + await nextTick(); + }); + + // Verify request + const latestRequest = server.requests[server.requests.length - 1]; + + const thresholdWatch = { + id: JSON.parse(latestRequest.requestBody).id, // watch ID is created dynamically + name: WATCH_NAME, + type: WATCH_TYPES.THRESHOLD, + isNew: true, + actions: [], + index: MATCH_INDICES, + timeField: WATCH_TIME_FIELD, + triggerIntervalSize: 1, + triggerIntervalUnit: 'm', + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + threshold: 1000, + }; + + expect(latestRequest.requestBody).toEqual(JSON.stringify(thresholdWatch)); + }); + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_edit.test.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_edit.test.ts new file mode 100644 index 0000000000000..fb23a86980a33 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_edit.test.ts @@ -0,0 +1,229 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { act } from 'react-dom/test-utils'; +import axiosXhrAdapter from 'axios/lib/adapters/xhr'; +import axios from 'axios'; +import { setupEnvironment, pageHelpers, nextTick } from './helpers'; +import { WatchEditTestBed } from './helpers/watch_edit.helpers'; +import { WATCH } from './helpers/constants'; +import defaultWatchJson from '../../public/models/watch/default_watch.json'; +import { getWatch } from '../../test/fixtures'; +import { getRandomString } from '../../../../../test_utils'; + +const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); + +jest.mock('ui/chrome', () => ({ + breadcrumbs: { set: () => {} }, + addBasePath: (path: string) => path || '/api/watcher', +})); + +jest.mock('ui/time_buckets', () => { + class MockTimeBuckets { + setBounds(_domain: any) { + return {}; + } + getInterval() { + return { + expression: {}, + }; + } + } + return { TimeBuckets: MockTimeBuckets }; +}); + +jest.mock('../../public/lib/api', () => ({ + ...jest.requireActual('../../public/lib/api'), + loadIndexPatterns: async () => { + const INDEX_PATTERNS = [ + { attributes: { title: 'index1' } }, + { attributes: { title: 'index2' } }, + { attributes: { title: 'index3' } }, + ]; + return await INDEX_PATTERNS; + }, + getHttpClient: () => mockHttpClient, +})); + +const { setup } = pageHelpers.watchEdit; + +describe.skip('', () => { + const { server, httpRequestsMockHelpers } = setupEnvironment(); + let testBed: WatchEditTestBed; + + afterAll(() => { + server.restore(); + }); + + describe('Advanced watch', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadWatchResponse(WATCH); + + testBed = await setup(); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + await nextTick(); + testBed.component.update(); + }); + }); + + describe('on component mount', () => { + test('should set the correct page title', () => { + const { find } = testBed; + expect(find('pageTitle').text()).toBe(`Edit ${WATCH.watch.name}`); + }); + + test('should populate the correct values', () => { + const { find, exists, component } = testBed; + const { watch } = WATCH; + const codeEditor = component.find('EuiCodeEditor'); + + expect(exists('jsonWatchForm')).toBe(true); + expect(find('nameInput').props().value).toBe(watch.name); + expect(find('idInput').props().value).toBe(watch.id); + expect(JSON.parse(codeEditor.props().value as string)).toEqual(defaultWatchJson); + + // ID should not be editable + expect(find('idInput').props().readOnly).toEqual(true); + }); + + test('save a watch with new values', async () => { + const { form, actions } = testBed; + const { watch } = WATCH; + + const EDITED_WATCH_NAME = 'new_watch_name'; + + form.setInputValue('nameInput', EDITED_WATCH_NAME); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSubmitButton(); + await nextTick(); + }); + + const latestRequest = server.requests[server.requests.length - 1]; + + const DEFAULT_LOGGING_ACTION_ID = 'logging_1'; + const DEFAULT_LOGGING_ACTION_TYPE = 'logging'; + const DEFAULT_LOGGING_ACTION_TEXT = + 'There are {{ctx.payload.hits.total}} documents in your index. Threshold is 10.'; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + id: watch.id, + name: EDITED_WATCH_NAME, + type: watch.type, + isNew: false, + actions: [ + { + id: DEFAULT_LOGGING_ACTION_ID, + type: DEFAULT_LOGGING_ACTION_TYPE, + text: DEFAULT_LOGGING_ACTION_TEXT, + [DEFAULT_LOGGING_ACTION_TYPE]: { + text: DEFAULT_LOGGING_ACTION_TEXT, + }, + }, + ], + watch: defaultWatchJson, + }) + ); + }); + }); + }); + + describe('Threshold watch', () => { + const watch = getWatch({ + id: getRandomString(), + type: 'threshold', + name: 'my_threshold_watch', + timeField: '@timestamp', + triggerIntervalSize: 10, + triggerIntervalUnit: 'm', + aggType: 'count', + termSize: 10, + thresholdComparator: '>', + timeWindowSize: 10, + timeWindowUnit: 'm', + threshold: [1000], + }); + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadWatchResponse({ watch }); + + testBed = await setup(); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + const { component } = testBed; + await nextTick(); + component.update(); + }); + }); + + describe('on component mount', () => { + test('should set the correct page title', () => { + const { find } = testBed; + expect(find('pageTitle').text()).toBe(`Edit ${watch.name}`); + }); + + test('should populate the correct values', () => { + const { find, exists } = testBed; + + expect(exists('thresholdWatchForm')).toBe(true); + expect(find('nameInput').props().value).toBe(watch.name); + expect(find('watchTimeFieldSelect').props().value).toBe(watch.timeField); + }); + + test('should save the watch with new values', async () => { + const { form, actions } = testBed; + + const EDITED_WATCH_NAME = 'new_threshold_watch_name'; + + form.setInputValue('nameInput', EDITED_WATCH_NAME); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + actions.clickSubmitButton(); + await nextTick(); + }); + + const latestRequest = server.requests[server.requests.length - 1]; + const { + id, + type, + timeField, + triggerIntervalSize, + triggerIntervalUnit, + aggType, + termSize, + thresholdComparator, + timeWindowSize, + timeWindowUnit, + threshold, + } = watch; + + expect(latestRequest.requestBody).toEqual( + JSON.stringify({ + id, + name: EDITED_WATCH_NAME, + type, + isNew: false, + actions: [], + timeField, + triggerIntervalSize, + triggerIntervalUnit, + aggType, + termSize, + thresholdComparator, + timeWindowSize, + timeWindowUnit, + threshold: threshold && threshold[0], + }) + ); + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_list.test.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_list.test.ts new file mode 100644 index 0000000000000..ebd6628c9c083 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_list.test.ts @@ -0,0 +1,238 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { act } from 'react-dom/test-utils'; +import * as fixtures from '../../test/fixtures'; +import { + setupEnvironment, + pageHelpers, + nextTick, + getRandomString, + findTestSubject, +} from './helpers'; +import { WatchListTestBed } from './helpers/watch_list.helpers'; +import { ROUTES } from '../../common/constants'; + +const { API_ROOT } = ROUTES; + +jest.mock('ui/chrome', () => ({ + breadcrumbs: { set: () => {} }, + addBasePath: (path: string) => path || '/api/watcher', +})); + +jest.mock('ui/time_buckets', () => {}); + +const { setup } = pageHelpers.watchList; + +describe.skip('', () => { + const { server, httpRequestsMockHelpers } = setupEnvironment(); + let testBed: WatchListTestBed; + + afterAll(() => { + server.restore(); + }); + + describe('on component mount', () => { + beforeEach(async () => { + testBed = await setup(); + }); + + describe('watches', () => { + describe('when there are no watches', () => { + beforeEach(() => { + httpRequestsMockHelpers.setLoadWatchesResponse({ watches: [] }); + }); + + test('should display an empty prompt', async () => { + const { component, exists } = await setup(); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + await nextTick(); + component.update(); + }); + + expect(exists('emptyPrompt')).toBe(true); + expect(exists('emptyPrompt.createWatchButton')).toBe(true); + }); + }); + + // create a threshold and advanced watch type and monitoring + describe('when there are watches', () => { + const watch1 = fixtures.getWatch({ + name: `watchA-${getRandomString()}`, + id: `a-${getRandomString()}`, + type: 'threshold', + }); + const watch2 = fixtures.getWatch({ + name: `watchB-${getRandomString()}`, + id: `b-${getRandomString()}`, + type: 'json', + }); + const watch3 = fixtures.getWatch({ + name: `watchC-${getRandomString()}`, + id: `c-${getRandomString()}`, + type: 'monitoring', + isSystemWatch: true, + }); + + const watches = [watch1, watch2, watch3]; + + beforeEach(async () => { + httpRequestsMockHelpers.setLoadWatchesResponse({ watches }); + + testBed = await setup(); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + await nextTick(); + testBed.component.update(); + }); + }); + + test('should set the correct app title', () => { + const { exists, find } = testBed; + expect(exists('appTitle')).toBe(true); + expect(find('appTitle').text()).toEqual('Watcher'); + }); + + test('should have a link to the documentation', () => { + const { exists, find } = testBed; + expect(exists('documentationLink')).toBe(true); + expect(find('documentationLink').text()).toBe('Watcher docs'); + }); + + test('should list them in the table', async () => { + const { table } = testBed; + const { tableCellsValues } = table.getMetaData('watchesTable'); + + const getExpectedValue = (value: any) => (typeof value === 'undefined' ? '' : value); + + tableCellsValues.forEach((row, i) => { + const watch = watches[i]; + const { name, id, watchStatus } = watch; + + expect(row).toEqual([ + '', + id, // required value + getExpectedValue(name), + watchStatus.state, // required value + getExpectedValue(watchStatus.comment), + getExpectedValue(watchStatus.lastMetCondition), + getExpectedValue(watchStatus.lastChecked), + '', + ]); + }); + }); + + test('should have a button to create a watch', () => { + const { exists } = testBed; + expect(exists('createWatchButton')).toBe(true); + }); + + test('should have a link to view watch details', () => { + const { table } = testBed; + const { rows } = table.getMetaData('watchesTable'); + const idColumn = rows[0].columns[1].reactWrapper; + + expect(findTestSubject(idColumn, `watchIdColumn-${watch1.id}`).length).toBe(1); + expect(findTestSubject(idColumn, `watchIdColumn-${watch1.id}`).props().href).toEqual( + `#/management/elasticsearch/watcher/watches/watch/${watch1.id}/status` + ); + }); + + test('should have action buttons on each row to edit and delete a watch', () => { + const { table } = testBed; + const { rows } = table.getMetaData('watchesTable'); + const lastColumn = rows[0].columns[rows[0].columns.length - 1].reactWrapper; + + expect(findTestSubject(lastColumn, 'editWatchButton').length).toBe(1); + expect(findTestSubject(lastColumn, 'deleteWatchButton').length).toBe(1); + }); + + describe('system watch', () => { + test('should disable edit and delete actions', async () => { + const { table } = testBed; + const { rows } = table.getMetaData('watchesTable'); + const systemWatch = rows[2]; + const firstColumn = systemWatch.columns[0].reactWrapper; + const lastColumn = systemWatch.columns[rows[0].columns.length - 1].reactWrapper; + + expect( + findTestSubject(firstColumn, `checkboxSelectRow-${watch3.id}`) + .getDOMNode() + .getAttribute('disabled') + ).toEqual(''); + expect( + findTestSubject(lastColumn, 'editWatchButton') + .getDOMNode() + .getAttribute('disabled') + ).toEqual(''); + expect( + findTestSubject(lastColumn, 'deleteWatchButton') + .getDOMNode() + .getAttribute('disabled') + ).toEqual(''); + }); + }); + + describe('delete watch', () => { + test('should show a confirmation when clicking the delete watch button', async () => { + const { actions } = testBed; + + await actions.clickWatchActionAt(0, 'delete'); + + // We need to read the document "body" as the modal is added there and not inside + // the component DOM tree. + expect( + document.body.querySelector('[data-test-subj="deleteWatchesConfirmation"]') + ).not.toBe(null); + + expect( + document.body.querySelector('[data-test-subj="deleteWatchesConfirmation"]')! + .textContent + ).toContain('Delete watch'); + }); + + test('should send the correct HTTP request to delete watch', async () => { + const { component, actions, table } = testBed; + const { rows } = table.getMetaData('watchesTable'); + + const watchId = rows[0].columns[2].value; + + await actions.clickWatchActionAt(0, 'delete'); + + const modal = document.body.querySelector( + '[data-test-subj="deleteWatchesConfirmation"]' + ); + const confirmButton: HTMLButtonElement | null = modal!.querySelector( + '[data-test-subj="confirmModalConfirmButton"]' + ); + + httpRequestsMockHelpers.setDeleteWatchResponse({ + results: { + successes: [watchId], + errors: [], + }, + }); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + confirmButton!.click(); + await nextTick(); + component.update(); + }); + + const latestRequest = server.requests[server.requests.length - 1]; + + expect(latestRequest.method).toBe('POST'); + expect(latestRequest.url).toBe(`${API_ROOT}/watches/delete`); + }); + }); + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_status.test.ts b/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_status.test.ts new file mode 100644 index 0000000000000..3c01a5e007c2a --- /dev/null +++ b/x-pack/legacy/plugins/watcher/__jest__/client_integration/watch_status.test.ts @@ -0,0 +1,285 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { act } from 'react-dom/test-utils'; +import { setupEnvironment, pageHelpers, nextTick } from './helpers'; +import { WatchStatusTestBed } from './helpers/watch_status.helpers'; +import { WATCH } from './helpers/constants'; +import { getWatchHistory } from '../../test/fixtures'; +import moment from 'moment'; +import { ROUTES } from '../../common/constants'; +import { WATCH_STATES, ACTION_STATES } from '../../common/constants'; + +const { API_ROOT } = ROUTES; + +jest.mock('ui/chrome', () => ({ + breadcrumbs: { set: () => {} }, + addBasePath: (path: string) => path || '/api/watcher', +})); + +jest.mock('ui/time_buckets', () => {}); + +const { setup } = pageHelpers.watchStatus; + +const watchHistory1 = getWatchHistory({ startTime: '2019-06-04T01:11:11.294' }); +const watchHistory2 = getWatchHistory({ startTime: '2019-06-04T01:10:10.987Z' }); + +const watchHistoryItems = { watchHistoryItems: [watchHistory1, watchHistory2] }; + +const ACTION_ID = 'my_logging_action_1'; + +const watch = { + ...WATCH.watch, + watchStatus: { + state: WATCH_STATES.FIRING, + isActive: true, + actionStatuses: [ + { + id: ACTION_ID, + state: ACTION_STATES.FIRING, + isAckable: true, + }, + ], + }, +}; + +describe.skip('', () => { + const { server, httpRequestsMockHelpers } = setupEnvironment(); + let testBed: WatchStatusTestBed; + + afterAll(() => { + server.restore(); + }); + + describe('on component mount', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadWatchResponse({ watch }); + httpRequestsMockHelpers.setLoadWatchHistoryResponse(watchHistoryItems); + + testBed = await setup(); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + await nextTick(); + testBed.component.update(); + }); + }); + + test('should set the correct page title', () => { + const { find } = testBed; + + expect(find('pageTitle').text()).toBe(`Current status for '${watch.name}'`); + }); + + describe('tabs', () => { + test('should have 2 tabs', () => { + const { find } = testBed; + + expect(find('tab').length).toBe(2); + expect(find('tab').map(t => t.text())).toEqual(['Execution history', 'Action statuses']); + }); + + test('should navigate to the "Action statuses" tab', () => { + const { exists, actions } = testBed; + + expect(exists('watchHistorySection')).toBe(true); + expect(exists('watchDetailSection')).toBe(false); + + actions.selectTab('action statuses'); + + expect(exists('watchHistorySection')).toBe(false); + expect(exists('watchDetailSection')).toBe(true); + }); + }); + + describe('execution history', () => { + test('should list history items in the table', () => { + const { table } = testBed; + const { tableCellsValues } = table.getMetaData('watchHistoryTable'); + + const getExpectedValue = (value: any) => (typeof value === 'undefined' ? '' : value); + + tableCellsValues.forEach((row, i) => { + const historyItem = watchHistoryItems.watchHistoryItems[i]; + const { startTime, watchStatus } = historyItem; + + expect(row).toEqual([ + getExpectedValue(moment(startTime).format()), + getExpectedValue(watchStatus.state), + getExpectedValue(watchStatus.comment), + ]); + }); + }); + + test('should show execution history details on click', async () => { + const { actions, exists } = testBed; + + const watchHistoryItem = { + ...watchHistory1, + watchId: watch.id, + watchStatus: { + state: WATCH_STATES.FIRING, + actionStatuses: [ + { + id: 'my_logging_action_1', + state: ACTION_STATES.FIRING, + isAckable: true, + }, + ], + }, + }; + + const formattedStartTime = moment(watchHistoryItem.startTime).format(); + + httpRequestsMockHelpers.setLoadWatchHistoryItemResponse({ watchHistoryItem }); + + await actions.clickWatchExecutionAt(0, formattedStartTime); + + const latestRequest = server.requests[server.requests.length - 1]; + + expect(latestRequest.method).toBe('GET'); + expect(latestRequest.url).toBe(`${API_ROOT}/history/${watchHistoryItem.id}`); + + expect(exists('watchHistoryDetailFlyout')).toBe(true); + }); + }); + + describe('delete watch', () => { + test('should show a confirmation when clicking the delete button', async () => { + const { actions } = testBed; + + await actions.clickDeleteWatchButton(); + + // We need to read the document "body" as the modal is added there and not inside + // the component DOM tree. + expect( + document.body.querySelector('[data-test-subj="deleteWatchesConfirmation"]') + ).not.toBe(null); + + expect( + document.body.querySelector('[data-test-subj="deleteWatchesConfirmation"]')!.textContent + ).toContain('Delete watch'); + }); + + test('should send the correct HTTP request to delete watch', async () => { + const { component, actions } = testBed; + + await actions.clickDeleteWatchButton(); + + const modal = document.body.querySelector('[data-test-subj="deleteWatchesConfirmation"]'); + const confirmButton: HTMLButtonElement | null = modal!.querySelector( + '[data-test-subj="confirmModalConfirmButton"]' + ); + + httpRequestsMockHelpers.setDeleteWatchResponse({ + results: { + successes: [watch.id], + errors: [], + }, + }); + + // @ts-ignore (remove when react 16.9.0 is released) + await act(async () => { + confirmButton!.click(); + await nextTick(); + component.update(); + }); + + const latestRequest = server.requests[server.requests.length - 1]; + + expect(latestRequest.method).toBe('POST'); + expect(latestRequest.url).toBe(`${API_ROOT}/watches/delete`); + }); + }); + + describe('activate & deactive watch', () => { + test('should send the correct HTTP request to deactivate and activate a watch', async () => { + const { actions } = testBed; + + httpRequestsMockHelpers.setDeactivateWatchResponse({ + watchStatus: { + state: WATCH_STATES.DISABLED, + isActive: false, + }, + }); + + await actions.clickToggleActivationButton(); + + const deactivateRequest = server.requests[server.requests.length - 1]; + + expect(deactivateRequest.method).toBe('PUT'); + expect(deactivateRequest.url).toBe(`${API_ROOT}/watch/${watch.id}/deactivate`); + + httpRequestsMockHelpers.setActivateWatchResponse({ + watchStatus: { + state: WATCH_STATES.FIRING, + isActive: true, + }, + }); + + await actions.clickToggleActivationButton(); + + const activateRequest = server.requests[server.requests.length - 1]; + + expect(activateRequest.method).toBe('PUT'); + expect(activateRequest.url).toBe(`${API_ROOT}/watch/${watch.id}/activate`); + }); + }); + + describe('action statuses', () => { + beforeEach(() => { + const { actions } = testBed; + + actions.selectTab('action statuses'); + }); + + test('should list the watch actions in a table', () => { + const { table } = testBed; + const { tableCellsValues } = table.getMetaData('watchActionStatusTable'); + + tableCellsValues.forEach((row, i) => { + const action = watch.watchStatus.actionStatuses[i]; + const { id, state, isAckable } = action; + + expect(row).toEqual([id, state, isAckable ? 'Acknowledge' : '']); + }); + }); + + test('should allow an action to be acknowledged', async () => { + const { actions, table } = testBed; + + httpRequestsMockHelpers.setAcknowledgeWatchResponse({ + watchStatus: { + state: WATCH_STATES.FIRING, + isActive: true, + comment: 'Acked', + actionStatuses: [ + { + id: ACTION_ID, + state: ACTION_STATES.ACKNOWLEDGED, + isAckable: false, + }, + ], + }, + }); + + await actions.clickAcknowledgeButton(0); + + const latestRequest = server.requests[server.requests.length - 1]; + + expect(latestRequest.method).toBe('PUT'); + expect(latestRequest.url).toBe( + `${API_ROOT}/watch/${watch.id}/action/${ACTION_ID}/acknowledge` + ); + + const { tableCellsValues } = table.getMetaData('watchActionStatusTable'); + + tableCellsValues.forEach(row => { + expect(row).toEqual([ACTION_ID, ACTION_STATES.ACKNOWLEDGED, '']); + }); + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/watcher/__tests__/plugin_definition.js b/x-pack/legacy/plugins/watcher/__tests__/plugin_definition.js deleted file mode 100644 index c95d5aba8bbc5..0000000000000 --- a/x-pack/legacy/plugins/watcher/__tests__/plugin_definition.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { pluginDefinition } from '../plugin_definition'; - -describe ('pluginDefinition', () => { - it('defines the configPrefix correctly', () => { - expect(pluginDefinition.configPrefix).to.be('xpack.watcher'); - }); -}); diff --git a/x-pack/legacy/plugins/watcher/common/constants/action_modes.js b/x-pack/legacy/plugins/watcher/common/constants/action_modes.ts similarity index 94% rename from x-pack/legacy/plugins/watcher/common/constants/action_modes.js rename to x-pack/legacy/plugins/watcher/common/constants/action_modes.ts index 245f237f16efa..5f703f14b3933 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/action_modes.js +++ b/x-pack/legacy/plugins/watcher/common/constants/action_modes.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const ACTION_MODES = { - +export const ACTION_MODES: { [key: string]: string } = { // The action execution will be simulated. For example, The email action will create the email that would have been sent but will not actually send it. In this mode, the action may be throttled if the current state of the watch indicates it should be. SIMULATE: 'simulate', @@ -19,6 +18,5 @@ export const ACTION_MODES = { FORCE_EXECUTE: 'force_execute', // The action will be skipped and won’t be executed nor simulated. Effectively forcing the action to be throttled. - SKIP: 'skip' - + SKIP: 'skip', }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/action_states.js b/x-pack/legacy/plugins/watcher/common/constants/action_states.ts similarity index 82% rename from x-pack/legacy/plugins/watcher/common/constants/action_states.js rename to x-pack/legacy/plugins/watcher/common/constants/action_states.ts index e34a5fd37ff3a..e9501fc2a60c4 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/action_states.js +++ b/x-pack/legacy/plugins/watcher/common/constants/action_states.ts @@ -6,36 +6,34 @@ import { i18n } from '@kbn/i18n'; -export const ACTION_STATES = { - +export const ACTION_STATES: { [key: string]: string } = { // Action is not being executed because conditions haven't been met OK: i18n.translate('xpack.watcher.constants.actionStates.okStateText', { - defaultMessage: 'OK' + defaultMessage: 'OK', }), // Action has been acknowledged by user ACKNOWLEDGED: i18n.translate('xpack.watcher.constants.actionStates.acknowledgedStateText', { - defaultMessage: 'Acked' + defaultMessage: 'Acked', }), // Action has been throttled (time-based) by the system THROTTLED: i18n.translate('xpack.watcher.constants.actionStates.throttledStateText', { - defaultMessage: 'Throttled' + defaultMessage: 'Throttled', }), // Action has been completed FIRING: i18n.translate('xpack.watcher.constants.actionStates.firingStateText', { - defaultMessage: 'Firing' + defaultMessage: 'Firing', }), // Action has failed ERROR: i18n.translate('xpack.watcher.constants.actionStates.errorStateText', { - defaultMessage: 'Error' + defaultMessage: 'Error', }), // Action has a configuration error CONFIG_ERROR: i18n.translate('xpack.watcher.constants.actionStates.configErrorStateText', { - defaultMessage: 'Config error' + defaultMessage: 'Config error', }), - }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/action_types.js b/x-pack/legacy/plugins/watcher/common/constants/action_types.ts similarity index 81% rename from x-pack/legacy/plugins/watcher/common/constants/action_types.js rename to x-pack/legacy/plugins/watcher/common/constants/action_types.ts index 7b408f0d3a0cc..72f1c9d4bf102 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/action_types.js +++ b/x-pack/legacy/plugins/watcher/common/constants/action_types.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const ACTION_TYPES = { - +export const ACTION_TYPES: { [key: string]: string } = { EMAIL: 'email', WEBHOOK: 'webhook', @@ -14,14 +13,11 @@ export const ACTION_TYPES = { LOGGING: 'logging', - HIPCHAT: 'hipchat', - SLACK: 'slack', JIRA: 'jira', PAGERDUTY: 'pagerduty', - UNKNOWN: 'unknown/invalid' - + UNKNOWN: 'unknown/invalid', }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/agg_types.js b/x-pack/legacy/plugins/watcher/common/constants/agg_types.ts similarity index 82% rename from x-pack/legacy/plugins/watcher/common/constants/agg_types.js rename to x-pack/legacy/plugins/watcher/common/constants/agg_types.ts index 3f53a36e89127..1a0f78c33111c 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/agg_types.js +++ b/x-pack/legacy/plugins/watcher/common/constants/agg_types.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const AGG_TYPES = { - +export const AGG_TYPES: { [key: string]: string } = { COUNT: 'count', AVERAGE: 'avg', @@ -14,6 +13,5 @@ export const AGG_TYPES = { MIN: 'min', - MAX: 'max' - + MAX: 'max', }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/comparators.ts b/x-pack/legacy/plugins/watcher/common/constants/comparators.ts new file mode 100644 index 0000000000000..21b350c0f8ce4 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/common/constants/comparators.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const COMPARATORS: { [key: string]: string } = { + GREATER_THAN: '>', + GREATER_THAN_OR_EQUALS: '>=', + BETWEEN: 'between', + LESS_THAN: '<', + LESS_THAN_OR_EQUALS: '<=', +}; diff --git a/x-pack/legacy/plugins/watcher/common/constants/error_codes.js b/x-pack/legacy/plugins/watcher/common/constants/error_codes.ts similarity index 85% rename from x-pack/legacy/plugins/watcher/common/constants/error_codes.js rename to x-pack/legacy/plugins/watcher/common/constants/error_codes.ts index 2fa875549358f..f5433ebb576e6 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/error_codes.js +++ b/x-pack/legacy/plugins/watcher/common/constants/error_codes.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const ERROR_CODES = { - +export const ERROR_CODES: { [key: string]: string } = { // Property missing on object ERR_PROP_MISSING: 'ERR_PROP_MISSING', }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/es_scroll_settings.js b/x-pack/legacy/plugins/watcher/common/constants/es_scroll_settings.ts similarity index 77% rename from x-pack/legacy/plugins/watcher/common/constants/es_scroll_settings.js rename to x-pack/legacy/plugins/watcher/common/constants/es_scroll_settings.ts index 25bdcca565ee9..a51a7b117eea6 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/es_scroll_settings.js +++ b/x-pack/legacy/plugins/watcher/common/constants/es_scroll_settings.ts @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -export const ES_SCROLL_SETTINGS = { - +export const ES_SCROLL_SETTINGS: { + KEEPALIVE: string; + PAGE_SIZE: number; +} = { // How long to keep a scroll alive KEEPALIVE: '30s', // How many results to return per scroll response - PAGE_SIZE: 100 + PAGE_SIZE: 100, }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/index.js b/x-pack/legacy/plugins/watcher/common/constants/index.ts similarity index 100% rename from x-pack/legacy/plugins/watcher/common/constants/index.js rename to x-pack/legacy/plugins/watcher/common/constants/index.ts diff --git a/x-pack/legacy/plugins/watcher/common/constants/index_names.js b/x-pack/legacy/plugins/watcher/common/constants/index_names.ts similarity index 84% rename from x-pack/legacy/plugins/watcher/common/constants/index_names.js rename to x-pack/legacy/plugins/watcher/common/constants/index_names.ts index 663d932fbe133..b9ca85963a33d 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/index_names.js +++ b/x-pack/legacy/plugins/watcher/common/constants/index_names.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const INDEX_NAMES = { +export const INDEX_NAMES: { [key: string]: string } = { WATCHES: '.watches', WATCHER_HISTORY: '.watcher-history-*', }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/lists.js b/x-pack/legacy/plugins/watcher/common/constants/lists.ts similarity index 74% rename from x-pack/legacy/plugins/watcher/common/constants/lists.js rename to x-pack/legacy/plugins/watcher/common/constants/lists.ts index da89b99f828ba..1224b279f4816 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/lists.js +++ b/x-pack/legacy/plugins/watcher/common/constants/lists.ts @@ -5,6 +5,6 @@ */ // Durations are in ms -export const LISTS = { - NEW_ITEMS_HIGHLIGHT_DURATION: 1 * 1000 +export const LISTS: { [key: string]: number } = { + NEW_ITEMS_HIGHLIGHT_DURATION: 1 * 1000, }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/pagination.ts b/x-pack/legacy/plugins/watcher/common/constants/pagination.ts new file mode 100644 index 0000000000000..31df84ca60ec0 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/common/constants/pagination.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const PAGINATION: { initialPageSize: number; pageSizeOptions: number[] } = { + initialPageSize: 10, + pageSizeOptions: [10, 50, 100], +}; diff --git a/x-pack/legacy/plugins/watcher/common/constants/plugin.ts b/x-pack/legacy/plugins/watcher/common/constants/plugin.ts new file mode 100644 index 0000000000000..a4279a0dd0c41 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/common/constants/plugin.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { LICENSE_TYPE_GOLD, LicenseType } from '../../../../common/constants'; + +export const PLUGIN = { + ID: 'watcher', + MINIMUM_LICENSE_REQUIRED: LICENSE_TYPE_GOLD as LicenseType, + getI18nName: (i18n: any): string => { + return i18n.translate('xpack.watcher.appName', { + defaultMessage: 'Watcher', + }); + }, +}; diff --git a/x-pack/legacy/plugins/watcher/common/constants/refresh_intervals.js b/x-pack/legacy/plugins/watcher/common/constants/refresh_intervals.ts similarity index 78% rename from x-pack/legacy/plugins/watcher/common/constants/refresh_intervals.js rename to x-pack/legacy/plugins/watcher/common/constants/refresh_intervals.ts index 1aff358bcd636..db1daa865c323 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/refresh_intervals.js +++ b/x-pack/legacy/plugins/watcher/common/constants/refresh_intervals.ts @@ -7,8 +7,8 @@ // In milliseconds const SIXTY_SECONDS = 60 * 1000; -export const REFRESH_INTERVALS = { +export const REFRESH_INTERVALS: { [key: string]: number } = { WATCH_LIST: SIXTY_SECONDS, WATCH_HISTORY: SIXTY_SECONDS, - WATCH_VISUALIZATION: SIXTY_SECONDS + WATCH_VISUALIZATION: SIXTY_SECONDS, }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/routes.js b/x-pack/legacy/plugins/watcher/common/constants/routes.ts similarity index 84% rename from x-pack/legacy/plugins/watcher/common/constants/routes.js rename to x-pack/legacy/plugins/watcher/common/constants/routes.ts index 97ae0ee6de181..73a1824c507da 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/routes.js +++ b/x-pack/legacy/plugins/watcher/common/constants/routes.ts @@ -4,6 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export const ROUTES = { +export const ROUTES: { [key: string]: string } = { API_ROOT: '/api/watcher', }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/sort_orders.js b/x-pack/legacy/plugins/watcher/common/constants/sort_orders.ts similarity index 77% rename from x-pack/legacy/plugins/watcher/common/constants/sort_orders.js rename to x-pack/legacy/plugins/watcher/common/constants/sort_orders.ts index 062941929ddc6..c73d458a3018f 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/sort_orders.js +++ b/x-pack/legacy/plugins/watcher/common/constants/sort_orders.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export const SORT_ORDERS = { +export const SORT_ORDERS: { [key: string]: string } = { ASCENDING: 'asc', - DESCENDING: 'desc' + DESCENDING: 'desc', }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/time_units.js b/x-pack/legacy/plugins/watcher/common/constants/time_units.ts similarity index 81% rename from x-pack/legacy/plugins/watcher/common/constants/time_units.js rename to x-pack/legacy/plugins/watcher/common/constants/time_units.ts index ea63c21e22213..c861d47416a80 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/time_units.js +++ b/x-pack/legacy/plugins/watcher/common/constants/time_units.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export const TIME_UNITS = { +export const TIME_UNITS: { [key: string]: string } = { SECOND: 's', MINUTE: 'm', HOUR: 'h', - DAY: 'd' + DAY: 'd', }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/comparators.js b/x-pack/legacy/plugins/watcher/common/constants/watch_history.ts similarity index 74% rename from x-pack/legacy/plugins/watcher/common/constants/comparators.js rename to x-pack/legacy/plugins/watcher/common/constants/watch_history.ts index 69009b2f604b6..520fe1019c8c3 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/comparators.js +++ b/x-pack/legacy/plugins/watcher/common/constants/watch_history.ts @@ -4,10 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export const COMPARATORS = { - - GREATER_THAN: '>', - - LESS_THAN: '<' - +export const WATCH_HISTORY: { [key: string]: string } = { + INITIAL_RANGE: 'now-1h', }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/watch_state_comments.js b/x-pack/legacy/plugins/watcher/common/constants/watch_state_comments.js deleted file mode 100644 index cfa4d93d8a140..0000000000000 --- a/x-pack/legacy/plugins/watcher/common/constants/watch_state_comments.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export const WATCH_STATE_COMMENTS = { - - OK: '', - - PARTIALLY_THROTTLED: i18n.translate('xpack.watcher.constants.watchStateComments.partiallyThrottledStateCommentText', { - defaultMessage: 'Partially Throttled' - }), - - THROTTLED: i18n.translate('xpack.watcher.constants.watchStateComments.throttledStateCommentText', { - defaultMessage: 'Throttled' - }), - - PARTIALLY_ACKNOWLEDGED: i18n.translate('xpack.watcher.constants.watchStateComments.partiallyAcknowledgedStateCommentText', { - defaultMessage: 'Partially Acked' - }), - - ACKNOWLEDGED: i18n.translate('xpack.watcher.constants.watchStateComments.acknowledgedStateCommentText', { - defaultMessage: 'Acked' - }), - - FAILING: i18n.translate('xpack.watcher.constants.watchStateComments.executionFailingStateCommentText', { - defaultMessage: 'Execution Failing' - }), - -}; diff --git a/x-pack/legacy/plugins/watcher/common/constants/watch_state_comments.ts b/x-pack/legacy/plugins/watcher/common/constants/watch_state_comments.ts new file mode 100644 index 0000000000000..c88425c7c1616 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/common/constants/watch_state_comments.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const WATCH_STATE_COMMENTS: { [key: string]: string } = { + OK: '', + + PARTIALLY_THROTTLED: i18n.translate( + 'xpack.watcher.constants.watchStateComments.partiallyThrottledStateCommentText', + { + defaultMessage: 'Partially throttled', + } + ), + + THROTTLED: i18n.translate( + 'xpack.watcher.constants.watchStateComments.throttledStateCommentText', + { + defaultMessage: 'Throttled', + } + ), + + PARTIALLY_ACKNOWLEDGED: i18n.translate( + 'xpack.watcher.constants.watchStateComments.partiallyAcknowledgedStateCommentText', + { + defaultMessage: 'Partially acked', + } + ), + + ACKNOWLEDGED: i18n.translate( + 'xpack.watcher.constants.watchStateComments.acknowledgedStateCommentText', + { + defaultMessage: 'Acked', + } + ), + + FAILING: i18n.translate( + 'xpack.watcher.constants.watchStateComments.executionFailingStateCommentText', + { + defaultMessage: 'Execution failing', + } + ), +}; diff --git a/x-pack/legacy/plugins/watcher/common/constants/watch_states.js b/x-pack/legacy/plugins/watcher/common/constants/watch_states.ts similarity index 77% rename from x-pack/legacy/plugins/watcher/common/constants/watch_states.js rename to x-pack/legacy/plugins/watcher/common/constants/watch_states.ts index 1c546139b688c..6215b492975cf 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/watch_states.js +++ b/x-pack/legacy/plugins/watcher/common/constants/watch_states.ts @@ -6,26 +6,24 @@ import { i18n } from '@kbn/i18n'; -export const WATCH_STATES = { - +export const WATCH_STATES: { [key: string]: string } = { DISABLED: i18n.translate('xpack.watcher.constants.watchStates.disabledStateText', { - defaultMessage: 'Disabled' + defaultMessage: 'Disabled', }), OK: i18n.translate('xpack.watcher.constants.watchStates.okStateText', { - defaultMessage: 'OK' + defaultMessage: 'OK', }), FIRING: i18n.translate('xpack.watcher.constants.watchStates.firingStateText', { - defaultMessage: 'Firing' + defaultMessage: 'Firing', }), ERROR: i18n.translate('xpack.watcher.constants.watchStates.errorStateText', { - defaultMessage: 'Error!' + defaultMessage: 'Error', }), CONFIG_ERROR: i18n.translate('xpack.watcher.constants.watchStates.configErrorStateText', { - defaultMessage: 'Config error' + defaultMessage: 'Config error', }), - }; diff --git a/x-pack/legacy/plugins/watcher/common/constants/watch_types.js b/x-pack/legacy/plugins/watcher/common/constants/watch_types.ts similarity index 77% rename from x-pack/legacy/plugins/watcher/common/constants/watch_types.js rename to x-pack/legacy/plugins/watcher/common/constants/watch_types.ts index 51e89a19b6ca0..f0ebd3efc4078 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/watch_types.js +++ b/x-pack/legacy/plugins/watcher/common/constants/watch_types.ts @@ -4,12 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -export const WATCH_TYPES = { - +export const WATCH_TYPES: { [key: string]: string } = { JSON: 'json', THRESHOLD: 'threshold', - MONITORING: 'monitoring' - + MONITORING: 'monitoring', }; diff --git a/x-pack/legacy/plugins/watcher/common/lib/get_action_type/__tests__/get_action_type.js b/x-pack/legacy/plugins/watcher/common/lib/get_action_type/__tests__/get_action_type.js index 7b2fd7bd84062..c342c45b0c6c2 100644 --- a/x-pack/legacy/plugins/watcher/common/lib/get_action_type/__tests__/get_action_type.js +++ b/x-pack/legacy/plugins/watcher/common/lib/get_action_type/__tests__/get_action_type.js @@ -56,17 +56,6 @@ describe('get_action_type', () => { expect(type).to.be(ACTION_TYPES.LOGGING); }); - it(`correctly calculates ACTION_TYPES.HIPCHAT`, () => { - const actionJson = { - hipchat: { - 'foo': 'bar' - } - }; - const type = getActionType(actionJson); - - expect(type).to.be(ACTION_TYPES.HIPCHAT); - }); - it(`correctly calculates ACTION_TYPES.SLACK`, () => { const actionJson = { slack: { diff --git a/x-pack/legacy/plugins/watcher/common/lib/get_action_type/get_action_type.js b/x-pack/legacy/plugins/watcher/common/lib/get_action_type/get_action_type.ts similarity index 57% rename from x-pack/legacy/plugins/watcher/common/lib/get_action_type/get_action_type.js rename to x-pack/legacy/plugins/watcher/common/lib/get_action_type/get_action_type.ts index 6fdaa81c64260..95aaef71cd2be 100644 --- a/x-pack/legacy/plugins/watcher/common/lib/get_action_type/get_action_type.js +++ b/x-pack/legacy/plugins/watcher/common/lib/get_action_type/get_action_type.ts @@ -4,14 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { keys, values, intersection } from 'lodash'; +import { intersection, keys, values } from 'lodash'; import { ACTION_TYPES } from '../../constants'; -export function getActionType(action) { - const type = intersection( - keys(action), - values(ACTION_TYPES) - )[0] || ACTION_TYPES.UNKNOWN; +export function getActionType(action: { [key: string]: { [key: string]: any } }) { + const type = intersection(keys(action), values(ACTION_TYPES))[0] || ACTION_TYPES.UNKNOWN; return type; } diff --git a/x-pack/legacy/plugins/watcher/common/lib/get_action_type/index.js b/x-pack/legacy/plugins/watcher/common/lib/get_action_type/index.ts similarity index 100% rename from x-pack/legacy/plugins/watcher/common/lib/get_action_type/index.js rename to x-pack/legacy/plugins/watcher/common/lib/get_action_type/index.ts diff --git a/x-pack/legacy/plugins/watcher/common/types/action_types.ts b/x-pack/legacy/plugins/watcher/common/types/action_types.ts new file mode 100644 index 0000000000000..8247df3293d17 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/common/types/action_types.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +type EmailActionType = 'email'; +type LoggingActionType = 'logging'; +type WebhookActionType = 'webhook'; +type IndexActionType = 'index'; +type SlackActionType = 'slack'; +type JiraActionType = 'jira'; +type PagerDutyActionType = 'pagerduty'; + +export interface BaseAction { + id: string; + typeName: string; + isNew: boolean; + simulateMessage: string; + simulateFailMessage: string; + simulatePrompt: string; + selectMessage: string; + validate: () => { [key: string]: string[] }; + isEnabled: boolean; +} + +export interface EmailAction extends BaseAction { + type: EmailActionType; + iconClass: 'email'; + to: string[]; + subject?: string; + body: string; +} + +export interface LoggingAction extends BaseAction { + type: LoggingActionType; + iconClass: 'loggingApp'; + text: string; +} + +export interface IndexAction extends BaseAction { + type: IndexActionType; + iconClass: 'indexOpen'; + index: string; +} + +export interface PagerDutyAction extends BaseAction { + type: PagerDutyActionType; + iconClass: 'apps'; + description: string; +} + +export interface WebhookAction extends BaseAction { + type: WebhookActionType; + iconClass: 'logoWebhook'; + method?: 'head' | 'get' | 'post' | 'put' | 'delete'; + host: string; + port: number; + path?: string; + body?: string; + username?: string; + password?: string; +} + +export interface SlackAction extends BaseAction { + type: SlackActionType; + iconClass: 'logoSlack'; + text?: string; + to?: string[]; +} + +export interface JiraAction extends BaseAction { + type: JiraActionType; + iconClass: 'apps'; + projectKey: string; + issueType: string; + summary: string; +} + +export type ActionType = + | EmailAction + | LoggingAction + | IndexAction + | SlackAction + | JiraAction + | PagerDutyAction; diff --git a/x-pack/legacy/plugins/watcher/common/types/watch_types.ts b/x-pack/legacy/plugins/watcher/common/types/watch_types.ts new file mode 100644 index 0000000000000..0850911cdab6c --- /dev/null +++ b/x-pack/legacy/plugins/watcher/common/types/watch_types.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export interface ExecutedWatchResults { + id: string; + watchId: string; + details: any; + startTime: Date; + watchStatus: { + state: string; + actionStatuses: Array<{ state: string; lastExecutionReason: string; id: string }>; + }; +} + +export interface ExecutedWatchDetails { + scheduledTimeValue: string | undefined; + scheduledTimeUnit: string; + triggeredTimeValue: string | undefined; + triggeredTimeUnit: string; + ignoreCondition: boolean; + alternativeInput: any; + actionModes: { + [key: string]: string; + }; + recordExecution: boolean; + upstreamJson: any; +} + +export interface BaseWatch { + id: string; + type: string; + isNew: boolean; + name: string; + isSystemWatch: boolean; + watchStatus: any; + watchErrors: any; + typeName: string; + displayName: string; + upstreamJson: any; + resetActions: () => void; + createAction: (type: string, actionProps: {}) => void; + validate: () => { warning: { message: string; title?: string } }; + actions: [ + { + id: string; + type: string; + } + ]; + watch: { + actions: { + [key: string]: { [key: string]: any }; + }; + }; +} diff --git a/x-pack/legacy/plugins/watcher/plugin_definition.js b/x-pack/legacy/plugins/watcher/plugin_definition.js index 269170b845c39..4a5946cc4974d 100644 --- a/x-pack/legacy/plugins/watcher/plugin_definition.js +++ b/x-pack/legacy/plugins/watcher/plugin_definition.js @@ -5,6 +5,7 @@ */ import { resolve } from 'path'; +import { i18n } from '@kbn/i18n'; import { registerFieldsRoutes } from './server/routes/api/fields'; import { registerSettingsRoutes } from './server/routes/api/settings'; import { registerHistoryRoutes } from './server/routes/api/history'; @@ -12,7 +13,7 @@ import { registerIndicesRoutes } from './server/routes/api/indices'; import { registerLicenseRoutes } from './server/routes/api/license'; import { registerWatchesRoutes } from './server/routes/api/watches'; import { registerWatchRoutes } from './server/routes/api/watch'; -import { registerLicenseChecker } from './server/lib/register_license_checker'; +import { registerLicenseChecker } from '../../server/lib/register_license_checker'; import { PLUGIN } from './common/constants'; export const pluginDefinition = { @@ -21,17 +22,18 @@ export const pluginDefinition = { publicDir: resolve(__dirname, 'public'), require: ['kibana', 'elasticsearch', 'xpack_main'], uiExports: { - managementSections: [ - 'plugins/watcher/sections/watch_detail', - 'plugins/watcher/sections/watch_edit', - 'plugins/watcher/sections/watch_list', - 'plugins/watcher/sections/watch_history_item', - ], styleSheetPaths: resolve(__dirname, 'public/index.scss'), - home: ['plugins/watcher/register_feature'] + managementSections: ['plugins/watcher'], + home: ['plugins/watcher/register_feature'], }, init: function (server) { - registerLicenseChecker(server); + // Register license checker + registerLicenseChecker( + server, + PLUGIN.ID, + PLUGIN.getI18nName(i18n), + PLUGIN.MINIMUM_LICENSE_REQUIRED + ); registerFieldsRoutes(server); registerHistoryRoutes(server); @@ -40,5 +42,5 @@ export const pluginDefinition = { registerSettingsRoutes(server); registerWatchesRoutes(server); registerWatchRoutes(server); - } + }, }; diff --git a/x-pack/legacy/plugins/watcher/public/_hacks.scss b/x-pack/legacy/plugins/watcher/public/_hacks.scss deleted file mode 100644 index 3b42e2d3a7642..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/_hacks.scss +++ /dev/null @@ -1,4 +0,0 @@ -.mgtWatcher__list { - display: flex; - flex-grow: 1; -} \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/app.html b/x-pack/legacy/plugins/watcher/public/app.html new file mode 100644 index 0000000000000..8c7c3eb946aef --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/app.html @@ -0,0 +1,3 @@ + +
+
\ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/app.js b/x-pack/legacy/plugins/watcher/public/app.js new file mode 100644 index 0000000000000..b206348547966 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/app.js @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { HashRouter, Switch, Route, Redirect } from 'react-router-dom'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { WatchStatus } from './sections/watch_status/components/watch_status'; +import { WatchEdit } from './sections/watch_edit/components/watch_edit'; +import { WatchList } from './sections/watch_list/components/watch_list'; +import { registerRouter } from './lib/navigation'; +import { BASE_PATH } from './constants'; +import { LICENSE_STATUS_VALID } from '../../../common/constants'; +import { EuiCallOut, EuiLink } from '@elastic/eui'; + +class ShareRouter extends Component { + static contextTypes = { + router: PropTypes.shape({ + history: PropTypes.shape({ + push: PropTypes.func.isRequired, + createHref: PropTypes.func.isRequired + }).isRequired + }).isRequired + } + constructor(...args) { + super(...args); + this.registerRouter(); + } + + registerRouter() { + // Share the router with the app without requiring React or context. + const { router } = this.context; + registerRouter(router); + } + + render() { + return this.props.children; + } +} +export const App = ({ licenseStatus }) => { + const { status, message } = licenseStatus; + + if (status !== LICENSE_STATUS_VALID) { + return ( + + )} + color="warning" + iconType="help" + > + {message}{' '} + + + + + ); + } + + return ( + + + + + + ); +}; + +// Export this so we can test it with a different router. +export const AppWithoutRouter = () => ( + + + + + + + +); diff --git a/x-pack/legacy/plugins/watcher/public/components/action_state_icon/action_state_icon.html b/x-pack/legacy/plugins/watcher/public/components/action_state_icon/action_state_icon.html deleted file mode 100644 index 67c8c703aecf0..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/action_state_icon/action_state_icon.html +++ /dev/null @@ -1,22 +0,0 @@ -
-
-
-
-
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/action_state_icon/action_state_icon.js b/x-pack/legacy/plugins/watcher/public/components/action_state_icon/action_state_icon.js deleted file mode 100644 index bbf46b95a8196..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/action_state_icon/action_state_icon.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './action_state_icon.html'; -import { ACTION_STATES } from 'plugins/watcher/../common/constants'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('actionStateIcon', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - actionStatus: '=' - }, - bindToController: true, - controllerAs: 'actionStateIcon', - controller: class ActionStateIconController { - constructor() { - this.ACTION_STATES = ACTION_STATES; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/action_type_select/_action_type_select.scss b/x-pack/legacy/plugins/watcher/public/components/action_type_select/_action_type_select.scss deleted file mode 100644 index a500e13811aed..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/action_type_select/_action_type_select.scss +++ /dev/null @@ -1,44 +0,0 @@ -.actionTypeChoice { - display: flex; -} - -.actionTypeChoice--disabled { - opacity: 0.5; - cursor: default; -} - -.actionTypeIcon { - flex: 1 0 auto; - margin-right: $euiSizeM; - font-size: $euiFontSizeL; -} - -.actionTypeDescription { - flex: 1 1 auto; - width: 100%; -} - -.watchActionsActionType { - flex: 1 1 auto; - width: 450px; - - .ui-select-bootstrap > .ui-select-choices { - min-width: 350px; - } - - // Bootstrap ui select tweaks - .ui-select-bootstrap .ui-select-choices-row>span { - padding: $euiSizeM $euiSizeL; - } - - .ui-select-match .btn { - border-color: $euiColorPrimary; - color: $euiColorPrimary; - } - - .ui-select-placeholder { - color: $euiColorPrimary !important; - padding-right: $euiSizeM; - } - -} diff --git a/x-pack/legacy/plugins/watcher/public/components/action_type_select/_index.scss b/x-pack/legacy/plugins/watcher/public/components/action_type_select/_index.scss deleted file mode 100644 index d903343351387..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/action_type_select/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'action_type_select'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/action_type_select/action_type_select.html b/x-pack/legacy/plugins/watcher/public/components/action_type_select/action_type_select.html deleted file mode 100644 index dd8df4dba383a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/action_type_select/action_type_select.html +++ /dev/null @@ -1,34 +0,0 @@ - - - {{$select.selected.typeName}} - - -
-
- -
-
-
-
- - -
-
-
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/action_type_select/action_type_select.js b/x-pack/legacy/plugins/watcher/public/components/action_type_select/action_type_select.js deleted file mode 100644 index bfe48dbb872ab..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/action_type_select/action_type_select.js +++ /dev/null @@ -1,72 +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 { map } from 'lodash'; -import { uiModules } from 'ui/modules'; -import template from './action_type_select.html'; -import 'ui/angular_ui_select'; -import { Action } from 'plugins/watcher/models/action'; -import 'plugins/watcher/services/settings'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('actionTypeSelect', function ($injector) { - const watcherSettingsService = $injector.get('xpackWatcherSettingsService'); - - return { - restrict: 'E', - template: template, - scope: { - onChange: '=' - }, - controllerAs: 'actionTypeSelect', - bindToController: true, - controller: class ActionTypeSelectController { - constructor() { - this.selectedItem = { value: null }; - - this.loadActionTypes() - .then(actionTypes => { - this.actionTypes = actionTypes.filter((actionType) => { - // 'Action' is the default action type. If an action has the default then it's - // not fully implemented and shouldn't be presented to the user. - return actionType.typeName !== 'Action'; - }); - }); - } - - loadActionTypes() { - const allActionTypes = Action.getActionTypes(); - - // load the configuration settings to determine which actions are enabled - return watcherSettingsService.getSettings() - .then(settings => { - const result = map(allActionTypes, ({ typeName, iconClass, selectMessage }, type) => { - const isEnabled = settings.actionTypes[type].enabled; - - return { - type, - typeName, - iconClass, - selectMessage, - isEnabled - }; - }); - - return result; - }); - } - - onSelect(actionType) { - this.selectedItem = { value: null }; - - if (actionType.isEnabled) { - this.onChange(actionType.type); - } - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/_chart_tooltip.scss b/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/_chart_tooltip.scss deleted file mode 100644 index f2eb9246dfd3e..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/_chart_tooltip.scss +++ /dev/null @@ -1,21 +0,0 @@ -.chartTooltip { - // The following rules adapted from ui/vislib/styles/_tooltip.less - line-height: 1.1; - font-size: $euiFontSize; - font-weight: normal; - background-color: $euiColorDarkestShade; - color: $euiColorEmptyShade; - border-radius: $euiBorderRadius; - position: fixed; - z-index: 120; - word-wrap: break-word; - max-width: 40%; - overflow: hidden; - pointer-events: none; - margin: $euiSizeS; - padding: $euiSizeXS; -} - -.chartTooltip__label { - font-weight: bold; -} diff --git a/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/_index.scss b/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/_index.scss deleted file mode 100644 index 11b36a0a21001..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'chart_tooltip'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/chart_tooltip.html b/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/chart_tooltip.html deleted file mode 100644 index e0f163a2212df..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/chart_tooltip.html +++ /dev/null @@ -1,5 +0,0 @@ -
diff --git a/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/chart_tooltip.js b/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/chart_tooltip.js deleted file mode 100644 index 7102567fb3cbf..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/chart_tooltip.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { get } from 'lodash'; -import { uiModules } from 'ui/modules'; -import template from './chart_tooltip.html'; - -function calculateTooltipPosition(pointPosition, plotPosition, tooltipWidth, tooltipHeight) { - - const tooltipPosition = { - top: get(pointPosition, 'top'), - left: get(pointPosition, 'left') - }; - - const tooltipPositionBottom = pointPosition.top + tooltipHeight; - const isTooltipBeyondPlotBottom = tooltipPositionBottom > get(plotPosition, 'bottom'); - if (isTooltipBeyondPlotBottom) { - tooltipPosition.top -= tooltipHeight; - } - - const tooltipPositionRight = pointPosition.left + tooltipWidth; - const isTooltipBeyondPlotRight = tooltipPositionRight > get(plotPosition, 'right'); - if (isTooltipBeyondPlotRight) { - tooltipPosition.left -= tooltipWidth; - } - - return tooltipPosition; -} - -const app = uiModules.get('xpack/watcher'); - -app.directive('chartTooltip', function () { - return { - restrict: 'E', - replace: true, - transclude: true, - template: template, - scope: { - pointPosition: '=', - plotPosition: '=' - }, - controllerAs: 'chartTooltip', - bindToController: true, - link: ($scope, element) => { - $scope.$watchGroup([ - 'chartTooltip.pointPosition', - 'chartTooltip.plotPosition' - ], () => { - // Calculate tooltip position. This is especially necessary to make sure the tooltip - // stays within the bounds of the chart. - const pointPosition = $scope.chartTooltip.pointPosition; - const plotPosition = $scope.chartTooltip.plotPosition; - - const tooltipMargin = parseInt(element.css('margin')); // assumption: value is in px - const tooltipWidth = element[0].scrollWidth + (2 * tooltipMargin); - const tooltipHeight = element[0].scrollHeight + (2 * tooltipMargin); - - const tooltipPosition = calculateTooltipPosition(pointPosition, plotPosition, tooltipWidth, tooltipHeight); - $scope.chartTooltip.style = tooltipPosition; - }); - }, - controller: class ChartTooltipController { - constructor() { - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/confirm_watches_modal.tsx b/x-pack/legacy/plugins/watcher/public/components/confirm_watches_modal.tsx new file mode 100644 index 0000000000000..2ab21d34d648e --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/components/confirm_watches_modal.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; + +export const ConfirmWatchesModal = ({ + modalOptions, + callback, +}: { + modalOptions: { + title: string; + message: string; + buttonLabel?: string; + buttonType?: 'primary' | 'danger'; + } | null; + callback: (isConfirmed?: boolean) => void; +}) => { + if (!modalOptions) { + return null; + } + const { title, message, buttonType, buttonLabel } = modalOptions; + return ( + + callback()} + onConfirm={() => { + callback(true); + }} + cancelButtonText={i18n.translate( + 'xpack.watcher.sections.watchEdit.json.saveConfirmModal.cancelButtonLabel', + { defaultMessage: 'Cancel' } + )} + confirmButtonText={ + buttonLabel + ? buttonLabel + : i18n.translate( + 'xpack.watcher.sections.watchEdit.json.saveConfirmModal.saveButtonLabel', + { defaultMessage: 'Save watch' } + ) + } + > + {message} + + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/components/delete_watches_modal.tsx b/x-pack/legacy/plugins/watcher/public/components/delete_watches_modal.tsx new file mode 100644 index 0000000000000..6d75495cbfc20 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/components/delete_watches_modal.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { toastNotifications } from 'ui/notify'; +import { deleteWatches } from '../lib/api'; + +export const DeleteWatchesModal = ({ + watchesToDelete, + callback, +}: { + watchesToDelete: string[]; + callback: (deleted?: string[]) => void; +}) => { + const numWatchesToDelete = watchesToDelete.length; + if (!numWatchesToDelete) { + return null; + } + const confirmModalText = i18n.translate( + 'xpack.watcher.deleteSelectedWatchesConfirmModal.descriptionText', + { + defaultMessage: + "You can't recover {numWatchesToDelete, plural, one {a deleted watch} other {deleted watches}}.", + values: { numWatchesToDelete }, + } + ); + const confirmButtonText = i18n.translate( + 'xpack.watcher.deleteSelectedWatchesConfirmModal.deleteButtonLabel', + { + defaultMessage: 'Delete {numWatchesToDelete, plural, one {watch} other {# watches}} ', + values: { numWatchesToDelete }, + } + ); + const cancelButtonText = i18n.translate( + 'xpack.watcher.deleteSelectedWatchesConfirmModal.cancelButtonLabel', + { + defaultMessage: 'Cancel', + } + ); + return ( + + callback()} + onConfirm={async () => { + const { successes, errors } = await deleteWatches(watchesToDelete); + const numSuccesses = successes.length; + const numErrors = errors.length; + callback(successes); + if (numSuccesses > 0) { + toastNotifications.addSuccess( + i18n.translate( + 'xpack.watcher.sections.watchList.deleteSelectedWatchesSuccessNotification.descriptionText', + { + defaultMessage: + 'Deleted {numSuccesses, number} {numSuccesses, plural, one {watch} other {watches}}', + values: { numSuccesses }, + } + ) + ); + } + + if (numErrors > 0) { + toastNotifications.addDanger( + i18n.translate( + 'xpack.watcher.sections.watchList.deleteSelectedWatchesErrorNotification.descriptionText', + { + defaultMessage: + 'Failed to delete {numErrors, number} {numErrors, plural, one {watch} other {watches}}', + values: { numErrors }, + } + ) + ); + } + }} + cancelButtonText={cancelButtonText} + confirmButtonText={confirmButtonText} + > + {confirmModalText} + + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/components/duration_select/_duration_select.scss b/x-pack/legacy/plugins/watcher/public/components/duration_select/_duration_select.scss deleted file mode 100644 index 74d564f01cb6c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/duration_select/_duration_select.scss +++ /dev/null @@ -1,11 +0,0 @@ -.durationSelect { - white-space: nowrap; -} - -.durationSelectSize { - display: inline-block; -} - -.durationSelectUnit { - display: inline-block; -} diff --git a/x-pack/legacy/plugins/watcher/public/components/duration_select/_index.scss b/x-pack/legacy/plugins/watcher/public/components/duration_select/_index.scss deleted file mode 100644 index 281271ef39519..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/duration_select/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'duration_select'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/duration_select/duration_select.html b/x-pack/legacy/plugins/watcher/public/components/duration_select/duration_select.html deleted file mode 100644 index a19123cf17f43..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/duration_select/duration_select.html +++ /dev/null @@ -1,24 +0,0 @@ -
- - - -
diff --git a/x-pack/legacy/plugins/watcher/public/components/duration_select/duration_select.js b/x-pack/legacy/plugins/watcher/public/components/duration_select/duration_select.js deleted file mode 100644 index 9fab3504ac638..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/duration_select/duration_select.js +++ /dev/null @@ -1,76 +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 'ui/fancy_forms'; -import { uiModules } from 'ui/modules'; -import { InitAfterBindingsWorkaround } from 'ui/compat'; -import { TIME_UNITS } from 'plugins/watcher/constants'; -import template from './duration_select.html'; -import moment from 'moment'; -import 'plugins/watcher/services/html_id_generator'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('durationSelect', function ($injector) { - const htmlIdGeneratorFactory = $injector.get('xpackWatcherHtmlIdGeneratorFactory'); - - return { - require: '^form', - scope: { - durationId: '@', - minimumUnit: '=', - minimumSize: '=', - unit: '=', - size: '=', - describedBy: '@', - }, - template, - replace: true, - controllerAs: 'durationSelect', - bindToController: true, - link: function ($scope, $element, $attrs, $ctrl) { - $scope.durationSelect.form = $ctrl; - }, - controller: class DurationSelectController extends InitAfterBindingsWorkaround { - initAfterBindings($scope) { - this.timeUnits = TIME_UNITS; - this.makeId = htmlIdGeneratorFactory.create(['durationSelect', this.durationId]); - - $scope.$watchMulti([ - 'durationSelect.minimumSize', - 'durationSelect.minimumUnit' - ], ([minimumSize, minimumUnit]) => { - this.minimumDuration = moment.duration(Number(minimumSize), minimumUnit).asMilliseconds(); - this.checkValidity(); - }); - - $scope.$watchMulti([ - `durationSelect.size`, - `durationSelect.unit` - ], ([size, unit]) => { - this.duration = moment.duration(Number(size), unit).asMilliseconds(); - this.checkValidity(); - }); - } - - checkValidity = () => { - const isValid = this.duration >= this.minimumDuration; - const sizeName = this.makeId('size'); - const unitName = this.makeId('unit'); - - if (this.form[sizeName]) { - this.form[sizeName].$setTouched(true); - this.form[sizeName].$setValidity('minimumDuration', isValid); - } - - if (this.form[unitName]) { - this.form[unitName].$setTouched(true); - this.form[unitName].$setValidity('minimumDuration', isValid); - } - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/duration_select/index.js b/x-pack/legacy/plugins/watcher/public/components/duration_select/index.js deleted file mode 100644 index 2cad6b06e4694..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/duration_select/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './duration_select'; diff --git a/x-pack/legacy/plugins/watcher/public/components/errors_display_modal/errors_display_modal.html b/x-pack/legacy/plugins/watcher/public/components/errors_display_modal/errors_display_modal.html deleted file mode 100644 index 3d4f25e7abc7d..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/errors_display_modal/errors_display_modal.html +++ /dev/null @@ -1,18 +0,0 @@ -
-
-

{{ vm.title }}

-
-
-
    -
  • {{ error.message }}
  • -
-
-
- -
-
\ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/errors_display_modal/errors_display_modal.js b/x-pack/legacy/plugins/watcher/public/components/errors_display_modal/errors_display_modal.js deleted file mode 100644 index b8a2346c7450d..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/errors_display_modal/errors_display_modal.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; - -const app = uiModules.get('xpack/watcher'); - -app.controller('WatcherErrorsDisplayController', function WatcherErrorsDisplayController($modalInstance, params) { - this.title = params.title; - this.errors = params.errors; - - this.close = function close() { - $modalInstance.close(); - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/errors_display_modal/index.js b/x-pack/legacy/plugins/watcher/public/components/errors_display_modal/index.js deleted file mode 100644 index 07daabc6bf185..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/errors_display_modal/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './errors_display_modal'; diff --git a/x-pack/legacy/plugins/watcher/public/components/expression_builder/_index.scss b/x-pack/legacy/plugins/watcher/public/components/expression_builder/_index.scss deleted file mode 100644 index 20d5f2db36e73..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/expression_builder/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'components/expression_popover/expression_popover'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/expression_item.html b/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/expression_item.html deleted file mode 100644 index 062374903fa95..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/expression_item.html +++ /dev/null @@ -1,26 +0,0 @@ -
- - - -
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/expression_item.js b/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/expression_item.js deleted file mode 100644 index 895a26280dbeb..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/expression_item.js +++ /dev/null @@ -1,98 +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 'ui/fancy_forms'; -import { uiModules } from 'ui/modules'; -import { keyMap } from 'ui/utils/key_map'; -import template from './expression_item.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('expressionItem', function ($injector) { - const $document = $injector.get('$document'); - const $timeout = $injector.get('$timeout'); - - return { - restrict: 'E', - replace: true, - require: '^expressionBuilder', - transclude: true, - template: template, - scope: { - itemId: '@', - description: '=', - value: '=', - isOpen: '=', - isVisible: '=', - onOpen: '=', - onClose: '=', - isValid: '=', - isDirty: '=', - form: '=' - }, - bindToController: true, - controllerAs: 'expressionItem', - link: function ($scope, $el) { - $scope.expressionItem.$firstInput = $el.find('[data-id="expressionItemPopoverContent"]').find(':input:first'); - const $button = $el.find('[data-id="expressionItemButton"]'); - - const buttonFocusOrClick = () => { - $scope.$apply(() => { - $scope.expressionItem.onPopoverOpen(); - }); - }; - - const documentClick = (event) => { - if ($el.find(event.originalEvent.target).length === 0) { - $scope.$apply(() => { - $scope.expressionItem.onPopoverClose(); - }); - } - }; - - const documentKeydown = (event) => { - if ($scope.expressionItem.isOpen && keyMap[event.keyCode] === 'escape') { - $scope.$apply(() => { - $scope.expressionItem.onPopoverClose(); - }); - } - }; - - $button.on('focus', buttonFocusOrClick); - $button.on('click', buttonFocusOrClick); - $document.on('click', documentClick); - $document.on('keydown', documentKeydown); - $scope.$on('$destroy', () => { - $button.off('focus', buttonFocusOrClick); - $button.off('click', buttonFocusOrClick); - $document.off('click', documentClick); - $document.off('keydown', documentKeydown); - }); - }, - controller: class ExpressionItemController { - constructor($scope) { - $scope.$watch('expressionItem.isOpen', (isOpen, wasOpen) => { - if (isOpen) { - $timeout(() => { - $scope.expressionItem.$firstInput.focus(); - }); - } else if (wasOpen) { - this.form.$setTouched(true); - this.form.$setDirty(true); - } - }); - } - - onPopoverClose = () => { - this.onClose(this.itemId); - } - - onPopoverOpen = () => { - this.onOpen(this.itemId); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/index.js b/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/index.js deleted file mode 100644 index 1891cc2c53244..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_item/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './expression_item'; diff --git a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/_expression_popover.scss b/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/_expression_popover.scss deleted file mode 100644 index 9bf4389f438d4..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/_expression_popover.scss +++ /dev/null @@ -1,3 +0,0 @@ -.watcherExpressionPopover { - z-index: 1; // Addresses conflict between the expression popover and the visualization -} diff --git a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/expression_popover.html b/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/expression_popover.html deleted file mode 100644 index 2dea0d67c3a9a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/expression_popover.html +++ /dev/null @@ -1,13 +0,0 @@ -
-
- {{ expressionPopover.popoverTitle }} -
- -
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/expression_popover.js b/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/expression_popover.js deleted file mode 100644 index e5488b8be132e..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/expression_popover.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './expression_popover.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('expressionPopover', function () { - return { - restrict: 'E', - replace: true, - require: '^expressionItem', - transclude: true, - template: template, - scope: { - popoverTitle: '=' - }, - bindToController: true, - controllerAs: 'expressionPopover', - controller: class ExpressionPopoverController { - constructor() { } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/index.js b/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/index.js deleted file mode 100644 index 21cfc6ecb4bfe..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/expression_builder/components/expression_popover/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './expression_popover'; diff --git a/x-pack/legacy/plugins/watcher/public/components/expression_builder/expression_builder.html b/x-pack/legacy/plugins/watcher/public/components/expression_builder/expression_builder.html deleted file mode 100644 index f3c04c234b85f..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/expression_builder/expression_builder.html +++ /dev/null @@ -1,3 +0,0 @@ -
diff --git a/x-pack/legacy/plugins/watcher/public/components/expression_builder/expression_builder.js b/x-pack/legacy/plugins/watcher/public/components/expression_builder/expression_builder.js deleted file mode 100644 index da1c3dcbc9943..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/expression_builder/expression_builder.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './expression_builder.html'; -import './components/expression_item'; -import './components/expression_popover'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('expressionBuilder', function () { - return { - restrict: 'E', - replace: true, - transclude: true, - template: template, - scope: {}, - bindToController: true, - controllerAs: 'expressionBuilder', - controller: class BuilderController { - constructor() { - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/expression_builder/index.js b/x-pack/legacy/plugins/watcher/public/components/expression_builder/index.js deleted file mode 100644 index cbfc2e75ed0aa..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/expression_builder/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './expression_builder'; diff --git a/x-pack/legacy/plugins/watcher/public/components/flot_chart/_flot_chart.scss b/x-pack/legacy/plugins/watcher/public/components/flot_chart/_flot_chart.scss deleted file mode 100644 index db1100510a39b..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/flot_chart/_flot_chart.scss +++ /dev/null @@ -1,3 +0,0 @@ -.flotChart { - height: 100%; -} \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/flot_chart/_index.scss b/x-pack/legacy/plugins/watcher/public/components/flot_chart/_index.scss deleted file mode 100644 index 518b820c3e5b3..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/flot_chart/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'flot_chart'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/flot_chart/constants.js b/x-pack/legacy/plugins/watcher/public/components/flot_chart/constants.js deleted file mode 100644 index 9ca626d4c29f2..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/flot_chart/constants.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export const FLOT_EVENT_PLOT_HOVER_DEBOUNCE_MS = 20; // milliseconds diff --git a/x-pack/legacy/plugins/watcher/public/components/flot_chart/flot_chart.html b/x-pack/legacy/plugins/watcher/public/components/flot_chart/flot_chart.html deleted file mode 100644 index 73580867892ea..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/flot_chart/flot_chart.html +++ /dev/null @@ -1 +0,0 @@ -
\ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/flot_chart/flot_chart.js b/x-pack/legacy/plugins/watcher/public/components/flot_chart/flot_chart.js deleted file mode 100644 index be35b82dce61b..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/flot_chart/flot_chart.js +++ /dev/null @@ -1,65 +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 { isFunction, debounce } from 'lodash'; -import { uiModules } from 'ui/modules'; -import template from './flot_chart.html'; -import $ from 'plugins/xpack_main/jquery_flot'; -import { FLOT_EVENT_PLOT_HOVER_DEBOUNCE_MS } from './constants'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('flotChart', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - - // See https://github.com/flot/flot/blob/master/API.md#data-format - data: '=', - - // See https://github.com/flot/flot/blob/master/API.md#plot-options - options: '=', - - // Search for "plothover" in https://github.com/flot/flot/blob/master/API.md - onPlotHover: '=', - - }, - controllerAs: 'flotChart', - bindToController: true, - link: ($scope, element) => { - $scope.flotChart.container = element; - }, - controller: class FlotChartController { - constructor($scope) { - - $scope.$watchMulti([ - 'flotChart.data', - 'flotChart.options' - ], ([data, options]) => { - this.plot = $.plot(this.container, data, options); - }); - - $scope.$watch('flotChart.onPlotHover', (onPlotHover) => { - this.container.unbind('plothover'); - - if (isFunction(onPlotHover)) { - this.container.bind('plothover', debounce((...params) => { - // We use $scope.$apply to tell Angular to trigger a digest whenever - // the supplied event handler function is called - $scope.$apply(() => onPlotHover(...params, this.plot)); - }, FLOT_EVENT_PLOT_HOVER_DEBOUNCE_MS)); - } - }); - - $scope.$on('$destroy', () => { - this.container.unbind('plothover'); - }); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/flot_chart/index.js b/x-pack/legacy/plugins/watcher/public/components/flot_chart/index.js deleted file mode 100644 index c95a866839fab..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/flot_chart/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './flot_chart'; diff --git a/x-pack/legacy/plugins/watcher/public/components/forbidden_message/forbidden_message.html b/x-pack/legacy/plugins/watcher/public/components/forbidden_message/forbidden_message.html deleted file mode 100644 index 72ee8d9a397c1..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/forbidden_message/forbidden_message.html +++ /dev/null @@ -1,17 +0,0 @@ -
-
- - -
- -
-
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/forbidden_message/forbidden_message.js b/x-pack/legacy/plugins/watcher/public/components/forbidden_message/forbidden_message.js deleted file mode 100644 index 7101885ef7dfb..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/forbidden_message/forbidden_message.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './forbidden_message.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('forbiddenMessage', function () { - return { - restrict: 'E', - replace: true, - template: template, - transclude: true, - scope: {}, - controllerAs: 'forbiddenMessage', - bindToController: true, - controller: class ForbiddenMessageController { - constructor() { } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/forbidden_message/index.js b/x-pack/legacy/plugins/watcher/public/components/forbidden_message/index.js deleted file mode 100644 index 21819994ef8dd..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/forbidden_message/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './forbidden_message'; diff --git a/x-pack/legacy/plugins/watcher/public/components/form_errors.tsx b/x-pack/legacy/plugins/watcher/public/components/form_errors.tsx new file mode 100644 index 0000000000000..0a35d6cb5d104 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/components/form_errors.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFormRow } from '@elastic/eui'; +import React, { Children, cloneElement, Fragment, ReactElement } from 'react'; +export const ErrableFormRow = ({ + errorKey, + isShowingErrors, + errors, + children, + ...rest +}: { + errorKey: string; + isShowingErrors: boolean; + errors: { [key: string]: string[] }; + children: ReactElement; + [key: string]: any; +}) => { + return ( + 0} + error={errors[errorKey]} + {...rest} + > + {Children.map(children, child => cloneElement(child))} + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/components/index.ts b/x-pack/legacy/plugins/watcher/public/components/index.ts new file mode 100644 index 0000000000000..8217950a7597f --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/components/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { getPageErrorCode, PageError } from './page_error'; +export { ConfirmWatchesModal } from './confirm_watches_modal'; +export { DeleteWatchesModal } from './delete_watches_modal'; +export { ErrableFormRow } from './form_errors'; +export { WatchStatus } from './watch_status'; +export { SectionLoading } from './section_loading'; +export { SectionError } from './section_error'; diff --git a/x-pack/legacy/plugins/watcher/public/components/index_select/_index.scss b/x-pack/legacy/plugins/watcher/public/components/index_select/_index.scss deleted file mode 100644 index 6e2d69436be14..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/index_select/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'index_select'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/index_select/_index_select.scss b/x-pack/legacy/plugins/watcher/public/components/index_select/_index_select.scss deleted file mode 100644 index 2b17cd3925313..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/index_select/_index_select.scss +++ /dev/null @@ -1,55 +0,0 @@ -@mixin indexSelectSubText() { - color: $euiColorDarkShade; - font-size: $euiFontSizeXS; - padding: $euiSizeXS $euiSizeL; - line-height: $euiLineHeight; -} - -.indexSelectNoChoice { - @include indexSelectSubText; -} - -/** - * 1. To ensure a smooth UX, prevent the dropdown from hiding while we async search for more results - * 2. ui-select likes to clear the result set out of the UI when you enter - * a new search query which can cause a jarring UX. This helps mitigate - * that by at least providing something in the box so it's not empty - */ -.indexSelect--activeSearch { - .ui-select-dropdown.ng-hide { - display: block !important; /* 1 */ - - &:after { - content: 'Choose...'; /* 2 */ - @include indexSelectSubText; /* 2 */ - } - } -} - -/** - * 1. By default, hide this until the user searches or there are results - */ -.indexSelect--noSearch { - .ui-select-no-choice { - display: none !important; /* 1 */ - } -} - -/** - * 1. Provide a loading indicator to the user - */ -.indexSelect--fetchingWithNoIndices { - .ui-select-choices:before { - content: 'Loading...'; /* 1 */ - margin-left: $euiSize; /* 1 */ - font-style: italic; /* 1 */ - color: $euiColorDarkShade; /* 1 */ - } -} - -/** - * 1. Ensure that an invalid ui-select shows a red border - */ -.ui-select-multiple.ui-select-bootstrap.ng-invalid { - border-color: $euiColorDanger !important; /* 1 */ -} diff --git a/x-pack/legacy/plugins/watcher/public/components/index_select/index.js b/x-pack/legacy/plugins/watcher/public/components/index_select/index.js deleted file mode 100644 index a9230cb82c7d1..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/index_select/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './index_select'; diff --git a/x-pack/legacy/plugins/watcher/public/components/index_select/index_select.html b/x-pack/legacy/plugins/watcher/public/components/index_select/index_select.html deleted file mode 100644 index 1720af6d41f05..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/index_select/index_select.html +++ /dev/null @@ -1,46 +0,0 @@ -
- - - {{$item.indexName}} - - -
-
-
-
-
-
-
- -

-
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/index_select/index_select.js b/x-pack/legacy/plugins/watcher/public/components/index_select/index_select.js deleted file mode 100644 index f81d1ce65dd2d..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/index_select/index_select.js +++ /dev/null @@ -1,207 +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 { sortBy, pluck, map, startsWith, endsWith } from 'lodash'; -import 'plugins/watcher/services/indices'; -import { InitAfterBindingsWorkaround } from 'ui/compat'; -import { uiModules } from 'ui/modules'; -import template from './index_select.html'; - -function mapIndex(indexName, isFromIndexPattern = false, isUserEntered = false) { - return { indexName, isFromIndexPattern, isUserEntered }; -} - -function collapseIndices(allIndices, allIndexPatterns) { - const indices = map(allIndices, indexName => mapIndex(indexName, false)); - const indexPatterns = map(allIndexPatterns, indexName => mapIndex(indexName, true)); - indices.push(...indexPatterns); - return indices; -} - -const INDICES_FROM_INDEX_PATTERNS_HEADER_COPY = 'Based on your index patterns'; -const INDICES_FOR_CREATION_HEADER_COPY = 'Choose...'; -const INDICES_FROM_INDICES_HEADER_COPY = 'Based on your indices and aliases'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('indexSelect', ($injector) => { - const indicesService = $injector.get('xpackWatcherIndicesService'); - const indexPatternsService = $injector.get('indexPatterns'); - const $timeout = $injector.get('$timeout'); - - return { - restrict: 'E', - template, - scope: { - index: '=', - onChange: '=', - onTouched: '=' - }, - controllerAs: 'indexSelect', - bindToController: true, - link: ($scope, $ele) => { - const $searchBox = $ele.find('input[type="search"]'); - $scope.indexSelect.$searchBox = $searchBox; - - $searchBox.attr('id', 'indexSelectSearchBox'); - }, - controller: class IndexSelectController extends InitAfterBindingsWorkaround { - initAfterBindings($scope) { - this.$scope = $scope; - this.indexPattern = undefined; - this.fetchingWithNoIndices = true; - this.selectedIndices = []; - this.fetchedIndices = []; - - if (Boolean(this.index)) { - if (Array.isArray(this.index)) { - this.selectedIndices.push(...this.index.map(mapIndex)); - } else { - this.selectedIndices.push(mapIndex(this.index)); - } - } - - if (this.onTouched) { - $timeout(() => { - this.$searchBox.on('blur', () => { - $scope.$apply(this.onTouched); - }); - }); - $scope.$on('$destroy', () => { - this.$searchBox.off('blur'); - }); - } - - $scope.$watch('indexSelect.index', () => { - $timeout(() => { - // Hack that forces the ui-select to resize itself - $scope.$$childHead.$select.sizeSearchInput(); - }, 100); - }); - } - - get hasIndexPattern() { - return Boolean(this.indexPattern); - } - - /** - * This method powers the `on-select` and `on-remove` within ui-select - * to handle when an index is added or removed from the list - */ - onIndicesChanged() { - const indexNames = pluck(this.selectedIndices, 'indexName'); - this.onChange(indexNames); - } - - /** - * This method powers the `tagging` within ui-select to format - * a search query that has no results into a valid result so it - * can be selected - * - * @param {object} item - */ - onNewItem(unmatchedIndexPattern) { - return mapIndex(unmatchedIndexPattern); - } - - /** - * This method powers the `group-by` within ui-select to group - * our indices array based on the source - * - * @param {object} index - */ - groupIndices(index) { - if (index.isFromIndexPattern) { - return INDICES_FROM_INDEX_PATTERNS_HEADER_COPY; - } - - if (index.isUserEntered) { - return INDICES_FOR_CREATION_HEADER_COPY; - } - - return INDICES_FROM_INDICES_HEADER_COPY; - } - - /** - * This method powers the `group-filter` within ui-select to allow - * us to sort the grouped object so we can control which group - * is shown first - * - * @param {object} grouped - */ - sortGroupedIndices(grouped) { - return sortBy(grouped, group => group.name); - } - - /** - * This method powers the `uis-open-close` within ui-select to ensure - * we reset the search state once the dropdown is closed. The default - * behavior of ui-select is to clear the input field when the dropdown - * is closed and if we fail to reset the search state at the same time - * it will lead to a poor UX. - * - * @param {bool} isOpen - */ - onDropdownToggled(isOpen) { - if (!isOpen) { - this.reset(); - } - } - - /** - * Resets the search state so we have no stored query or results - */ - reset() { - this.fetchedIndices.length = 0; - this.indexPattern = undefined; - } - - /** - * This powers the `refresh` within ui-select which is called - * as a way to async fetch results based on the typed in query - * - * @param {string} indexPattern - */ - fetchIndices(indexPattern) { - if (!Boolean(indexPattern)) { - this.reset(); - return; - } - - // Store this so we can display it if there are no matches - this.indexPattern = indexPattern; - - let pattern = indexPattern; - - // Use wildcards religiously to ensure partial matches - if (!endsWith(pattern, '*')) { - pattern += '*'; - } - if (!startsWith(pattern, '*')) { - pattern = '*' + pattern; - } - - const promises = [ - indicesService.getMatchingIndices(pattern), - indexPatternsService.getTitles() - ]; - - if (this.fetchedIndices.length === 0) { - this.fetchingWithNoIndices = true; - } - - Promise.all(promises) - .then(([allIndices, allIndexPatterns]) => { - const indices = collapseIndices(allIndices, allIndexPatterns); - this.fetchedIndices = sortBy(indices, 'indexName'); - this.fetchedIndices.push(mapIndex(this.indexPattern, false, true)); - this.fetchingWithNoIndices = false; - this.$scope.$apply(); - }); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/json_editor/_index.scss b/x-pack/legacy/plugins/watcher/public/components/json_editor/_index.scss deleted file mode 100644 index a00b67f580531..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/json_editor/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'json_editor'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/json_editor/_json_editor.scss b/x-pack/legacy/plugins/watcher/public/components/json_editor/_json_editor.scss deleted file mode 100644 index 4176467cc2f60..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/json_editor/_json_editor.scss +++ /dev/null @@ -1,3 +0,0 @@ -.json-editor { - border: 1px solid $euiColorLightShade; -} diff --git a/x-pack/legacy/plugins/watcher/public/components/json_editor/index.js b/x-pack/legacy/plugins/watcher/public/components/json_editor/index.js deleted file mode 100644 index 2e2a91f13a085..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/json_editor/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './json_editor'; diff --git a/x-pack/legacy/plugins/watcher/public/components/json_editor/json_editor.html b/x-pack/legacy/plugins/watcher/public/components/json_editor/json_editor.html deleted file mode 100644 index bef39e5e74c5c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/json_editor/json_editor.html +++ /dev/null @@ -1,12 +0,0 @@ -
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/json_editor/json_editor.js b/x-pack/legacy/plugins/watcher/public/components/json_editor/json_editor.js deleted file mode 100644 index e392a96f2e70a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/json_editor/json_editor.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './json_editor.html'; -import 'plugins/watcher/directives/json_input'; -import 'ace'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('jsonEditor', function () { - return { - restrict: 'E', - template: template, - replace: true, - scope: { - json: '=', - onChange: '=', - onValid: '=', - onInvalid: '=' - }, - bindToController: true, - controllerAs: 'jsonEditor', - controller: class JsonEditorController { - constructor($scope) { - $scope.aceLoaded = (editor) => { - this.editor = editor; - editor.$blockScrolling = Infinity; - }; - - $scope.$watch('jsonEditor.form.$valid', () => { - if (this.form.$invalid) { - this.onInvalid(); - } else { - this.onValid(); - } - }); - - $scope.$watch('jsonEditor.json', () => { - this.onChange(this.json); - }); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/index.js b/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/index.js deleted file mode 100644 index 2b38a644a7e9f..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './kbn_tab'; diff --git a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/kbn_tab.html b/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/kbn_tab.html deleted file mode 100644 index cfd76bae49967..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/kbn_tab.html +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/kbn_tab.js b/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/kbn_tab.js deleted file mode 100644 index 4b2cffefb661a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tab/kbn_tab.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './kbn_tab.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('kbnTab', function () { - return { - require: '^^kbnTabs', - restrict: 'E', - transclude: true, - replace: true, - template: template, - scope: { - tabId: '@', - title: '@' - }, - controllerAs: 'kbnTab', - bindToController: true, - controller: class KbnTabController {}, - link: function ($scope, $ele, attrs, kbnTabs) { - $scope.kbnTabs = kbnTabs; - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/index.js b/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/index.js deleted file mode 100644 index 2ed8988b5323e..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './kbn_tabs'; diff --git a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/kbn_tabs.html b/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/kbn_tabs.html deleted file mode 100644 index 34a2ff3cd3a39..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/kbn_tabs.html +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/kbn_tabs.js b/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/kbn_tabs.js deleted file mode 100644 index d9f70ad6b9abd..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/components/kbn_tabs/kbn_tabs.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './kbn_tabs.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('kbnTabs', function () { - return { - restrict: 'E', - replace: true, - transclude: true, - template: template, - scope: { - selectedTabId: '=', - onTabSelect: '=' - }, - controllerAs: 'kbnTabs', - bindToController: true, - controller: class KbnTabsController { - constructor() {} - - isTabSelected = (tabId) => { - return this.selectedTabId === tabId; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/index.js b/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/index.js deleted file mode 100644 index 30953c4759223..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/kbn_tabs/index.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './components/kbn_tabs'; -import './components/kbn_tab'; diff --git a/x-pack/legacy/plugins/watcher/common/constants/watch_history.js b/x-pack/legacy/plugins/watcher/public/components/page_error/index.ts similarity index 80% rename from x-pack/legacy/plugins/watcher/common/constants/watch_history.js rename to x-pack/legacy/plugins/watcher/public/components/page_error/index.ts index 3fad569ab7878..cf350b1b98981 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/watch_history.js +++ b/x-pack/legacy/plugins/watcher/public/components/page_error/index.ts @@ -4,6 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const WATCH_HISTORY = { - INITIAL_RANGE: 'now-1h' -}; +export { getPageErrorCode, PageError } from './page_error'; diff --git a/x-pack/legacy/plugins/watcher/public/components/page_error/page_error.tsx b/x-pack/legacy/plugins/watcher/public/components/page_error/page_error.tsx new file mode 100644 index 0000000000000..e10ed198a4e74 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/components/page_error/page_error.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { PageErrorNotExist } from './page_error_not_exist'; +import { PageErrorForbidden } from './page_error_forbidden'; + +export function getPageErrorCode(errorOrErrors: any) { + const errors = Array.isArray(errorOrErrors) ? errorOrErrors : [errorOrErrors]; + const firstError = errors.find((error: any) => { + if (error) { + return [403, 404].includes(error.status); + } + + return false; + }); + + if (firstError) { + return firstError.status; + } +} + +export function PageError({ errorCode, id }: { errorCode?: any; id?: any }) { + switch (errorCode) { + case 404: + return ; + + case 403: + default: + return ; + } +} diff --git a/x-pack/legacy/plugins/watcher/public/components/page_error/page_error_forbidden.tsx b/x-pack/legacy/plugins/watcher/public/components/page_error/page_error_forbidden.tsx new file mode 100644 index 0000000000000..1561660aaee80 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/components/page_error/page_error_forbidden.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export function PageErrorForbidden() { + return ( + + + + } + /> + ); +} diff --git a/x-pack/legacy/plugins/watcher/public/components/page_error/page_error_not_exist.tsx b/x-pack/legacy/plugins/watcher/public/components/page_error/page_error_not_exist.tsx new file mode 100644 index 0000000000000..662ecaafadbb4 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/components/page_error/page_error_not_exist.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export function PageErrorNotExist({ id }: { id: any }) { + return ( + + + + } + body={ +

+ +

+ } + /> + ); +} diff --git a/x-pack/legacy/plugins/watcher/public/components/panel_pager/_index.scss b/x-pack/legacy/plugins/watcher/public/components/panel_pager/_index.scss deleted file mode 100644 index 326d073b77e91..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/panel_pager/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'panel_pager'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/panel_pager/_panel_pager.scss b/x-pack/legacy/plugins/watcher/public/components/panel_pager/_panel_pager.scss deleted file mode 100644 index 98ec7854ce868..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/panel_pager/_panel_pager.scss +++ /dev/null @@ -1,31 +0,0 @@ -.kuiPanelPager { - height: 0px; -} - -.panelPagerPrevious, -.panelPagerNext { - position: relative; - top: -155px; - border-radius: 20px; - border: 1px solid $euiColorLightShade; - background-color: $euiColorEmptyShade; - height: 40px; - width: 40px; -} - -.panelPagerPrevious { - left: -20px; -} - -.panelPagerPrevious__icon { - margin-left: 2px; // positions the icon horizontally inside the circle container -} - -.panelPagerNext { - float: right; - right: -20px; -} - -.panelPagerNext__icon { - margin-left: 4px; // positions the icon horizontally inside the circle container -} diff --git a/x-pack/legacy/plugins/watcher/public/components/panel_pager/index.js b/x-pack/legacy/plugins/watcher/public/components/panel_pager/index.js deleted file mode 100644 index 3c81126a17033..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/panel_pager/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './panel_pager'; diff --git a/x-pack/legacy/plugins/watcher/public/components/panel_pager/panel_pager.html b/x-pack/legacy/plugins/watcher/public/components/panel_pager/panel_pager.html deleted file mode 100644 index 8a70fa2135028..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/panel_pager/panel_pager.html +++ /dev/null @@ -1,28 +0,0 @@ -
- - -
diff --git a/x-pack/legacy/plugins/watcher/public/components/panel_pager/panel_pager.js b/x-pack/legacy/plugins/watcher/public/components/panel_pager/panel_pager.js deleted file mode 100644 index 0511147a6ce1a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/panel_pager/panel_pager.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './panel_pager.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('panelPager', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - onNextPage: '=', - onPreviousPage: '=' - }, - bindToController: true, - controllerAs: 'panelPager', - controller: class PanelPagerController { - constructor() { - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/section_error.tsx b/x-pack/legacy/plugins/watcher/public/components/section_error.tsx new file mode 100644 index 0000000000000..fea655107731f --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/components/section_error.tsx @@ -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 { EuiCallOut, EuiSpacer } from '@elastic/eui'; +import React, { Fragment } from 'react'; + +interface Props { + title: React.ReactNode; + error: { + data: { + error: string; + cause?: string[]; + message?: string; + }; + }; +} + +export const SectionError: React.FunctionComponent = ({ title, error, ...rest }) => { + const { + error: errorString, + cause, // wrapEsError() on the server adds a "cause" array + message, + } = error.data; + + return ( + +
{message || errorString}
+ {cause && ( + + +
    + {cause.map((causeMsg, i) => ( +
  • {causeMsg}
  • + ))} +
+
+ )} +
+ ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/components/section_loading.tsx b/x-pack/legacy/plugins/watcher/public/components/section_loading.tsx new file mode 100644 index 0000000000000..4c6273682a0e4 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/components/section_loading.tsx @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { EuiEmptyPrompt, EuiLoadingSpinner, EuiText } from '@elastic/eui'; + +interface Props { + children: React.ReactNode; +} + +export const SectionLoading: React.FunctionComponent = ({ children }) => { + return ( + } + body={{children}} + data-test-subj="sectionLoading" + /> + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/_index.scss b/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/_index.scss deleted file mode 100644 index 4661c99f48e40..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'threshold_preview_chart'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/_threshold_preview_chart.scss b/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/_threshold_preview_chart.scss deleted file mode 100644 index 87c3c0f3fd328..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/_threshold_preview_chart.scss +++ /dev/null @@ -1,15 +0,0 @@ -.thresholdPreviewChart { - height: 100%; - position: relative; - - .flot-tick-label { - color: $euiColorDarkShade; - } -} - -.thresholdPreviewChartNoData { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/constants.js b/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/constants.js deleted file mode 100644 index 74a5ede0e10a1..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/constants.js +++ /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 const COLORS = { - AREA_FILL: '#eff6f8', - SERIES_LINE: '#25a1d1', - THRESHOLD_LINE: '#dd1c74', - CHART_BORDER: '#676767' -}; - -export const LINE_WIDTHS = { - THRESHOLD_LINE: 2 -}; - -export const MARGINS = { - AXES_LABELS: 10 -}; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/index.js b/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/index.js deleted file mode 100644 index fbf2da1b51526..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './threshold_preview_chart'; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/threshold_preview_chart.html b/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/threshold_preview_chart.html deleted file mode 100644 index b31dda450d5ad..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/threshold_preview_chart.html +++ /dev/null @@ -1,22 +0,0 @@ -
-

- - -
{{ thresholdPreviewChart.dataPointTooltip.yValue }}
-
{{ thresholdPreviewChart.formatAsMoment(thresholdPreviewChart.dataPointTooltip.xValue) | moment }}
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/threshold_preview_chart.js b/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/threshold_preview_chart.js deleted file mode 100644 index 5d31a198a2946..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_preview_chart/threshold_preview_chart.js +++ /dev/null @@ -1,188 +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 '../../directives/moment'; -import { isNumber, forEach, merge } from 'lodash'; -import { uiModules } from 'ui/modules'; -import { TimeBuckets } from 'ui/time_buckets'; -import 'plugins/watcher/components/flot_chart'; -import 'plugins/watcher/components/chart_tooltip'; -import template from './threshold_preview_chart.html'; -import { COLORS, LINE_WIDTHS, MARGINS } from './constants'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('thresholdPreviewChart', function ($injector) { - const config = $injector.get('config'); - - moment.tz.setDefault(config.get('dateFormat:tz')); - - return { - restrict: 'E', - replace: true, - template: template, - scope: { - // A single series (array) of (x, y) points - // - Format: [ [ xTimestamp1, yValue1 ], [ xTimestamp2, yValue2 ], ... ] - // - Units for timestamp values (xTimestamp1, xTimestamp2, etc.) are ms-since-epoch - // - Timestamp values are assumed to be in UTC timezone - // - Series array must be sorted in ascending order of timestamp values - series: '=', - - startDate: '=', - endDate: '=', - - // A single y-axis value - thresholdValue: '=' - }, - controllerAs: 'thresholdPreviewChart', - bindToController: true, - controller: class ThresholdPreviewChartController { - constructor($scope) { - - this.options = { - colors: [ COLORS.SERIES_LINE ], - grid: { - aboveData: true, - borderColor: COLORS.CHART_BORDER, - borderWidth: { top: 0, right: 0, bottom: 2, left: 2 }, - hoverable: true, - labelMargin: MARGINS.AXES_LABELS - }, - series: { - lines: { - show: true, - fill: true, - fillColor: COLORS.AREA_FILL - } - }, - yaxis: { - tickLength: 0 - } - }; - - $scope.$watch('thresholdPreviewChart.series', (series) => { - this.data = [ series ]; - - const timeBuckets = new TimeBuckets(); - timeBuckets.setBounds({ - min: this.startDate, - max: this.endDate - }); - const momentFormat = timeBuckets.getScaledDateFormat(); - - const options = { - xaxis: { - mode: 'time', - min: this.startDate, - max: this.endDate, - tickFormatter: (val) => moment(val).format(momentFormat) - } - }; - - this.updateOptions(options); - }); - - $scope.$watch('thresholdPreviewChart.thresholdValue', (thresholdValue) => { - const parsedThreshold = Number.parseFloat(thresholdValue); - - const options = { - grid: { - markings: [] - } - }; - - if (isNumber(parsedThreshold)) { - const thresholdLine = { - yaxis: { - from: parsedThreshold, - to: parsedThreshold - }, - color: COLORS.THRESHOLD_LINE, - lineWidth: LINE_WIDTHS.THRESHOLD_LINE - }; - - options.grid.markings.push(thresholdLine); - } - - this.updateOptions(options); - }); - } - - updateOptions = (options) => { - const yAxisRange = this.buildYAxisRange(); - this.options = merge({}, this.options, options, yAxisRange); - } - - buildYAxisRange = () => { - const parsedThreshold = Number.parseFloat(this.thresholdValue); - const series = this.data[0]; - - if (!series || !isNumber(parsedThreshold)) { - return { - yaxis: { - min: null, - max: null - } - }; - } - - const VALUE_INDEX = 1; - const options = { - yaxis: { - min: Number.POSITIVE_INFINITY, - max: Number.NEGATIVE_INFINITY - } - }; - - forEach(series, seriesItem => { - const itemValue = seriesItem[VALUE_INDEX]; - - if (itemValue !== null) { - options.yaxis.min = Math.min(options.yaxis.min, itemValue, parsedThreshold); - options.yaxis.max = Math.max(options.yaxis.max, itemValue, parsedThreshold); - } - }); - - return options; - } - - onPlotHover = (event, pos, item, plot) => { // eslint-disable-line no-unused-vars - if (!Boolean(item)) { - this.dataPointTooltip = undefined; - return; - } - - const plotLeft = plot.offset().left - window.pageXOffset; - const plotTop = plot.offset().top - window.pageYOffset; - - this.dataPointTooltip = { - plotPosition: { - left: plotLeft, - top: plotTop, - right: plotLeft + plot.width(), - bottom: plotTop + plot.height() - }, - pointPosition: { - left: item.pageX - window.pageXOffset, - top: item.pageY - window.pageYOffset - }, - xValue: item.datapoint[0], - yValue: item.datapoint[1] - }; - } - - get isDataExists() { - return Boolean(this.series); - } - - formatAsMoment = (msSinceEpoch) => { - return moment(msSinceEpoch); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/_index.scss b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/_index.scss deleted file mode 100644 index b71b8e81da822..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'threshold_watch_expression'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/_threshold_watch_expression.scss b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/_threshold_watch_expression.scss deleted file mode 100644 index 8f6a7d4604b99..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/_threshold_watch_expression.scss +++ /dev/null @@ -1,17 +0,0 @@ -.watcherNumberInput { - width: 85px; -} - -.thresholdWatchExpression { - .kuiExpressionButton { - margin-right: $euiSizeM; - } - - .kuiExpressionButton-isError { - border-color: $euiColorDanger; - } - - .kuiInputNote { - margin-top: $euiSizeS * -1; - } -} \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/index.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/index.js deleted file mode 100644 index 0d5da15d8b784..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './threshold_watch_agg_field'; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/threshold_watch_agg_field.html b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/threshold_watch_agg_field.html deleted file mode 100644 index a103fbecfc588..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/threshold_watch_agg_field.html +++ /dev/null @@ -1,37 +0,0 @@ - -
-
- -
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/threshold_watch_agg_field.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/threshold_watch_agg_field.js deleted file mode 100644 index 5b55baf9484b3..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_field/threshold_watch_agg_field.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { i18n } from '@kbn/i18n'; -import template from './threshold_watch_agg_field.html'; -import { ThresholdWatchBaseController } from '../threshold_watch_base'; -import 'plugins/watcher/services/html_id_generator'; -import 'plugins/watcher/components/xpack_aria_describes'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('thresholdWatchAggField', function ($injector) { - const htmlIdGeneratorFactory = $injector.get('xpackWatcherHtmlIdGeneratorFactory'); - - return { - restrict: 'E', - template: template, - scope: { - itemId: '@', - aggFields: '=', - aggField: '=', - isOpen: '=', - isVisible: '=', - onOpen: '=', - onClose: '=', - onChange: '=', - onValid: '=', - onInvalid: '=', - onDirty: '=', - onPristine: '=' - }, - bindToController: true, - controllerAs: 'thresholdWatchAggField', - controller: class ThresholdWatchAggFieldController extends ThresholdWatchBaseController { - initAfterBindings($scope) { - this.makeId = htmlIdGeneratorFactory.create(); - - $scope.$watch('thresholdWatchAggField.aggField', this.onChange); - - $scope.$watch('thresholdWatchAggField.form.$valid', this.checkValidity); - $scope.$watch('thresholdWatchAggField.form.$dirty', this.checkDirty); - - $scope.$watch('thresholdWatchAggField.isVisible', (isVisible, wasVisible) => { - this.checkValidity(); - if (!isVisible && wasVisible) { - this.resetForm(); - this.checkDirty(); - } - }); - - this.itemDescription = i18n.translate('xpack.watcher.thresholdWatchExpression.aggField.itemDescription', { - defaultMessage: 'Of', - }); - } - - get itemValue() { - return this.aggField ? this.aggField.name : i18n.translate( - 'xpack.watcher.thresholdWatchExpression.aggField.selectFieldTextMessage', { - defaultMessage: 'select a field', - }); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/index.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/index.js deleted file mode 100644 index 9e649d220f405..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './threshold_watch_agg_type'; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/threshold_watch_agg_type.html b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/threshold_watch_agg_type.html deleted file mode 100644 index 981fcf12aa131..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/threshold_watch_agg_type.html +++ /dev/null @@ -1,31 +0,0 @@ - -
-
- -
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/threshold_watch_agg_type.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/threshold_watch_agg_type.js deleted file mode 100644 index 3c9a5de318b26..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_agg_type/threshold_watch_agg_type.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { i18n } from '@kbn/i18n'; -import template from './threshold_watch_agg_type.html'; -import { ThresholdWatchBaseController } from '../threshold_watch_base'; -import 'plugins/watcher/services/html_id_generator'; -import 'plugins/watcher/components/xpack_aria_describes'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('thresholdWatchAggType', function ($injector) { - const htmlIdGeneratorFactory = $injector.get('xpackWatcherHtmlIdGeneratorFactory'); - - return { - restrict: 'E', - template: template, - scope: { - itemId: '@', - aggTypes: '=', - aggType: '=', - isOpen: '=', - isVisible: '=', - onOpen: '=', - onClose: '=', - onChange: '=', - onValid: '=', - onInvalid: '=', - onDirty: '=', - onPristine: '=' - }, - bindToController: true, - controllerAs: 'thresholdWatchAggType', - controller: class ThresholdWatchAggTypeController extends ThresholdWatchBaseController { - initAfterBindings($scope) { - this.makeId = htmlIdGeneratorFactory.create(); - - $scope.$watch('thresholdWatchAggType.aggType', this.onChange); - - $scope.$watch('thresholdWatchAggType.form.$valid', this.checkValidity); - $scope.$watch('thresholdWatchAggType.form.$dirty', this.checkDirty); - - this.itemDescription = i18n.translate('xpack.watcher.thresholdWatchExpression.aggType.itemDescription', { - defaultMessage: 'When', - }); - } - - get itemValue() { - return this.aggType ? this.aggType.label : ''; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_base/index.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_base/index.js deleted file mode 100644 index fa9e583936183..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_base/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { ThresholdWatchBaseController } from './threshold_watch_base'; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_base/threshold_watch_base.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_base/threshold_watch_base.js deleted file mode 100644 index dfa3f5f01f530..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_base/threshold_watch_base.js +++ /dev/null @@ -1,57 +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 { InitAfterBindingsWorkaround } from 'ui/compat'; -import { forEach, size } from 'lodash'; - -export class ThresholdWatchBaseController extends InitAfterBindingsWorkaround { - checkValidity = () => { - if (this.isValid()) { - this.onValid(this.itemId); - } else { - this.onInvalid(this.itemId); - } - } - - checkDirty = () => { - if (this.form.$dirty) { - this.onDirty(this.itemId); - } else { - this.onPristine(this.itemId); - } - } - - resetForm = () => { - forEach(this.form, (control) => { - if (Boolean(control) && typeof control.$setViewValue === 'function') { - control.$setViewValue(undefined); - } - }); - - this.form.$setPristine(); - this.form.$setUntouched(); - } - - isValid = () => { - return !(this.form.$invalid); - } - - isDirty = () => { - return this.form.$dirty; - } - - isValidationMessageVisible = (fieldName, errorType, showIfOtherErrors = true) => { - let showMessage = this.form[fieldName] && - (this.form[fieldName].$touched || this.form[fieldName].$dirty) && - this.form[fieldName].$error[errorType]; - - if (showMessage && !showIfOtherErrors && size(this.form[fieldName].$error) > 1) { - showMessage = false; - } - - return showMessage; - } -} diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/index.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/index.js deleted file mode 100644 index 581da9ac49477..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './threshold_watch_group_by'; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/threshold_watch_group_by.html b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/threshold_watch_group_by.html deleted file mode 100644 index b978b55e58f66..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/threshold_watch_group_by.html +++ /dev/null @@ -1,77 +0,0 @@ - -
-
-
-
-
- - - -
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/threshold_watch_group_by.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/threshold_watch_group_by.js deleted file mode 100644 index 3f395bbb4e7e5..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_group_by/threshold_watch_group_by.js +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { i18n } from '@kbn/i18n'; -import template from './threshold_watch_group_by.html'; -import { ThresholdWatchBaseController } from '../threshold_watch_base'; -import 'plugins/watcher/services/html_id_generator'; -import 'plugins/watcher/components/xpack_aria_describes'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('thresholdWatchGroupBy', function ($injector) { - const htmlIdGeneratorFactory = $injector.get('xpackWatcherHtmlIdGeneratorFactory'); - - return { - restrict: 'E', - template: template, - scope: { - itemId: '@', - groupByTypes: '=', - groupByType: '=', - termSize: '=', - termFields: '=', - termField: '=', - isOpen: '=', - isVisible: '=', - onOpen: '=', - onClose: '=', - onChange: '=', - onValid: '=', - onInvalid: '=', - onDirty: '=', - onPristine: '=' - }, - bindToController: true, - controllerAs: 'thresholdWatchGroupBy', - controller: class ThresholdWatchGroupByController extends ThresholdWatchBaseController { - initAfterBindings($scope) { - this.makeId = htmlIdGeneratorFactory.create(); - - $scope.$watchMulti([ - 'thresholdWatchGroupBy.groupByType', - 'thresholdWatchGroupBy.termSize', - 'thresholdWatchGroupBy.termField' - ], this.onChange); - - $scope.$watch('thresholdWatchGroupBy.groupByType', () => { - this.resetForm(); - this.checkDirty(); - }); - - $scope.$watch('thresholdWatchGroupBy.form.$valid', this.checkValidity); - $scope.$watch('thresholdWatchGroupBy.form.$dirty', this.checkDirty); - } - - get itemDescription() { - const itemDescription = (this.groupByType && this.groupByType.sizeRequired) - ? i18n.translate('xpack.watcher.thresholdWatchExpression.groupBy.groupedItemDescription', { - defaultMessage: 'Grouped over', - }) - : i18n.translate('xpack.watcher.thresholdWatchExpression.groupBy.itemDescription', { - defaultMessage: 'Over', - }); - - return itemDescription; - } - - get itemValue() { - if (!this.groupByType) { - return; - } - - const sizeRequired = this.groupByType.sizeRequired; - const typeLabel = this.groupByType.label; - const sizeLabel = (sizeRequired && this.termSize) ? ` ${this.termSize}` : ''; - const fieldLabel = (sizeRequired && this.termField) ? ` '${this.termField.name}'` : ''; - - return `${typeLabel}${sizeLabel}${fieldLabel}`; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/index.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/index.js deleted file mode 100644 index c19db9116509c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './threshold_watch_threshold_level'; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/threshold_watch_threshold_level.html b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/threshold_watch_threshold_level.html deleted file mode 100644 index 76be0699679e0..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/threshold_watch_threshold_level.html +++ /dev/null @@ -1,45 +0,0 @@ - -
-
-
- - -
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/threshold_watch_threshold_level.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/threshold_watch_threshold_level.js deleted file mode 100644 index bbb52b7c9723b..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_threshold_level/threshold_watch_threshold_level.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './threshold_watch_threshold_level.html'; -import { ThresholdWatchBaseController } from '../threshold_watch_base'; -const app = uiModules.get('xpack/watcher'); -import 'plugins/watcher/services/html_id_generator'; -import 'plugins/watcher/components/xpack_aria_describes'; - -app.directive('thresholdWatchThresholdLevel', function ($injector) { - const htmlIdGeneratorFactory = $injector.get('xpackWatcherHtmlIdGeneratorFactory'); - - return { - restrict: 'E', - template: template, - scope: { - itemId: '@', - comparators: '=', - thresholdComparator: '=', - threshold: '=', - isOpen: '=', - isVisible: '=', - onOpen: '=', - onClose: '=', - onChange: '=', - onValid: '=', - onInvalid: '=', - onDirty: '=', - onPristine: '=' - }, - bindToController: true, - controllerAs: 'thresholdWatchThresholdLevel', - controller: class ThresholdWatchThresholdLevelController extends ThresholdWatchBaseController { - initAfterBindings($scope) { - this.makeId = htmlIdGeneratorFactory.create(); - - $scope.$watchMulti([ - 'thresholdWatchThresholdLevel.thresholdComparator', - 'thresholdWatchThresholdLevel.threshold' - ], this.onChange); - - $scope.$watch('thresholdWatchThresholdLevel.form.$valid', this.checkValidity); - $scope.$watch('thresholdWatchThresholdLevel.form.$dirty', this.checkDirty); - } - - get itemDescription() { - return this.thresholdComparator ? this.thresholdComparator.label : ''; - } - - get itemValue() { - return this.threshold; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/index.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/index.js deleted file mode 100644 index b642cad45924f..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './threshold_watch_time_window'; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/threshold_watch_time_window.html b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/threshold_watch_time_window.html deleted file mode 100644 index 9972b9f5fc5ec..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/threshold_watch_time_window.html +++ /dev/null @@ -1,63 +0,0 @@ - -
-
-
-
-
-
- -
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/threshold_watch_time_window.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/threshold_watch_time_window.js deleted file mode 100644 index a5979cf306aac..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/components/threshold_watch_time_window/threshold_watch_time_window.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { i18n } from '@kbn/i18n'; -import template from './threshold_watch_time_window.html'; -import { TIME_UNITS } from 'plugins/watcher/../common/constants'; -import { getTimeUnitsLabel } from 'plugins/watcher/lib/get_time_units_label'; -import { ThresholdWatchBaseController } from '../threshold_watch_base'; -import 'plugins/watcher/components/duration_select'; -import 'plugins/watcher/services/html_id_generator'; -import 'plugins/watcher/components/xpack_aria_describes'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('thresholdWatchTimeWindow', function ($injector) { - const htmlIdGeneratorFactory = $injector.get('xpackWatcherHtmlIdGeneratorFactory'); - - return { - restrict: 'E', - template: template, - scope: { - itemId: '@', - timeWindowUnits: '=', - timeWindowSize: '=', - timeWindowUnit: '=', - isOpen: '=', - isVisible: '=', - onOpen: '=', - onClose: '=', - onChange: '=', - onValid: '=', - onInvalid: '=', - onDirty: '=', - onPristine: '=' - }, - bindToController: true, - controllerAs: 'thresholdWatchTimeWindow', - controller: class ThresholdWatchTimeWindowController extends ThresholdWatchBaseController { - initAfterBindings($scope) { - this.makeId = htmlIdGeneratorFactory.create(); - - this.timeWindowMinimumUnit = TIME_UNITS.SECOND; - this.timeWindowMinimumSize = 10; - - $scope.$watchMulti([ - 'thresholdWatchTimeWindow.timeWindowSize', - 'thresholdWatchTimeWindow.timeWindowUnit' - ], this.onChange); - - $scope.$watch('thresholdWatchTimeWindow.form.$valid', this.checkValidity); - $scope.$watch('thresholdWatchTimeWindow.form.$dirty', this.checkDirty); - } - - get itemDescription() { - return i18n.translate('xpack.watcher.thresholdWatchExpression.timeWindow.itemDescription', { - defaultMessage: 'For the last', - }); - } - - get itemValue() { - const sizeLabel = !isNaN(this.timeWindowSize) - ? `${this.timeWindowSize} ` - : '0'; - - const unitLabel = Boolean(this.timeWindowUnit) - ? getTimeUnitsLabel(this.timeWindowUnit, this.timeWindowSize) - : ''; - - return `${sizeLabel} ${unitLabel}`; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/index.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/index.js deleted file mode 100644 index d7751c33ed7a6..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './threshold_watch_expression'; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/comparators.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/comparators.js deleted file mode 100644 index 12e0cbd169445..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/comparators.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -import { COMPARATORS } from 'plugins/watcher/../common/constants'; - -export const comparators = { - 'above': { - label: i18n.translate('xpack.watcher.thresholdWatchExpression.comparators.isAboveLabel', { - defaultMessage: 'Is above', - }), - value: COMPARATORS.GREATER_THAN - }, - 'below': { - label: i18n.translate('xpack.watcher.thresholdWatchExpression.comparators.isBelowLabel', { - defaultMessage: 'Is below', - }), - value: COMPARATORS.LESS_THAN - } -}; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/group_by_types.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/group_by_types.js deleted file mode 100644 index 44e9f00fcebf4..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/group_by_types.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export const groupByTypes = { - 'all': { - label: i18n.translate('xpack.watcher.thresholdWatchExpression.groupByLabel.allDocumentsLabel', { - defaultMessage: 'all documents', - }), - sizeRequired: false, - value: 'all', - validNormalizedTypes: [] - }, - 'top': { - label: i18n.translate('xpack.watcher.thresholdWatchExpression.groupByLabel.topLabel', { - defaultMessage: 'top', - }), - sizeRequired: true, - value: 'top', - validNormalizedTypes: ['number', 'date', 'keyword'] - } -}; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/time_units.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/time_units.js deleted file mode 100644 index 4048bc11813f8..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/time_units.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export const timeUnits = { - 'second': { - labelPlural: i18n.translate('xpack.watcher.thresholdWatchExpression.timeUnits.secondPluralLabel', { - defaultMessage: 'seconds', - }), - labelSingular: i18n.translate('xpack.watcher.thresholdWatchExpression.timeUnits.secondSingularLabel', { - defaultMessage: 'second', - }), - value: 's' - }, - 'minute': { - labelPlural: i18n.translate('xpack.watcher.thresholdWatchExpression.timeUnits.minutePluralLabel', { - defaultMessage: 'minutes', - }), - labelSingular: i18n.translate('xpack.watcher.thresholdWatchExpression.timeUnits.minuteSingularLabel', { - defaultMessage: 'minute', - }), - value: 'm' - }, - 'hour': { - labelPlural: i18n.translate('xpack.watcher.thresholdWatchExpression.timeUnits.hourPluralLabel', { - defaultMessage: 'hours', - }), - labelSingular: i18n.translate('xpack.watcher.thresholdWatchExpression.timeUnits.hourSingularLabel', { - defaultMessage: 'hour', - }), - value: 'h' - }, - 'day': { - labelPlural: i18n.translate('xpack.watcher.thresholdWatchExpression.timeUnits.dayPluralLabel', { - defaultMessage: 'days', - }), - labelSingular: i18n.translate('xpack.watcher.thresholdWatchExpression.timeUnits.daySingularLabel', { - defaultMessage: 'day', - }), - value: 'd' - } -}; diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/threshold_watch_expression.html b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/threshold_watch_expression.html deleted file mode 100644 index e69d5edc9499a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/threshold_watch_expression.html +++ /dev/null @@ -1,89 +0,0 @@ - -
- - - - - - - - - - - -
diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/threshold_watch_expression.js b/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/threshold_watch_expression.js deleted file mode 100644 index aefb61525a162..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/threshold_watch_expression.js +++ /dev/null @@ -1,228 +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 { find, filter, includes, forEach, every, some } from 'lodash'; -import { uiModules } from 'ui/modules'; -import template from './threshold_watch_expression.html'; -import './components/threshold_watch_agg_type'; -import './components/threshold_watch_agg_field'; -import './components/threshold_watch_group_by'; -import './components/threshold_watch_threshold_level'; -import './components/threshold_watch_time_window'; -import 'plugins/watcher/components/expression_builder'; -import { aggTypes } from './lib/agg_types'; -import { comparators } from './lib/comparators'; -import { groupByTypes } from './lib/group_by_types'; -import { TIME_UNITS } from 'plugins/watcher/constants'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('thresholdWatchExpression', function () { - - return { - restrict: 'E', - template: template, - scope: { - thresholdWatch: '=', - fields: '=', - onChange: '=', - onValid: '=', - onInvalid: '=', - onDirty: '=' - }, - bindToController: true, - controllerAs: 'thresholdWatchExpression', - controller: class ThresholdWatchExpressionController { - constructor($scope) { - this.items = { - 'agg-type': { isOpen: false, isVisible: true, isValid: true }, - 'agg-field': { isOpen: false, isVisible: false, isValid: true }, - 'group-by': { isOpen: false, isVisible: true, isValid: true }, - 'threshold-level': { isOpen: false, isVisible: true, isValid: true }, - 'time-window': { isOpen: false, isVisible: true, isValid: true } - }; - - this.aggTypes = aggTypes; - this.groupByTypes = groupByTypes; - this.comparators = comparators; - this.timeUnits = TIME_UNITS; - this.initialized = false; - this.dirty = false; - - $scope.$watchGroup([ - 'thresholdWatchExpression.thresholdWatch', - 'thresholdWatchExpression.fields' - ], ([thresholdWatch, fields]) => { - if (!thresholdWatch || !fields) { - return; - } - - this.aggType = find(this.aggTypes, { value: this.thresholdWatch.aggType }); - this.aggField = find(this.fields, { name: this.thresholdWatch.aggField }); - this.groupByType = this.thresholdWatch.termField ? this.groupByTypes.top : this.groupByTypes.all; - this.termSize = this.thresholdWatch.termSize; - this.termField = find(this.fields, { name: this.thresholdWatch.termField }); - this.threshold = this.thresholdWatch.threshold; - this.thresholdComparator = find(this.comparators, { value: this.thresholdWatch.thresholdComparator }); - this.timeWindowSize = this.thresholdWatch.timeWindowSize; - this.timeWindowUnit = this.thresholdWatch.timeWindowUnit; - - this.initialized = true; - }); - } - - onAggTypeChange = (aggType) => { - if (!this.initialized) { - return; - } - - this.aggType = aggType; - - if (aggType.fieldRequired) { - this.items['agg-field'].isVisible = true; - this.aggFields = filter( - this.fields, field => includes(aggType.validNormalizedTypes, field.normalizedType) - ); - // The selected aggField may no longer be in the new array of aggFields. If so, - // unset it via the handler, so additional business logic is fired. - if (!includes(this.aggFields, this.aggField)) { - this.onAggFieldChange(null); - } - } else { - this.items['agg-field'].isVisible = false; - this.onAggFieldChange(null); - } - - this.thresholdWatch.aggType = aggType ? aggType.value : null; - - this.onChange(this.thresholdWatch); - }; - - onAggFieldChange = (aggField) => { - if (!this.initialized) { - return; - } - - this.aggField = aggField; - this.thresholdWatch.aggField = aggField ? aggField.name : null; - - this.onChange(this.thresholdWatch); - } - - onGroupByChange = ([groupByType, termSize, termField], [oldGroupByType]) => { - if (!this.initialized) { - return; - } - - this.groupByType = groupByType; - this.termSize = termSize; - this.termField = termField; - - if (groupByType !== oldGroupByType) { - this.termFields = filter( - this.fields, field => includes(this.groupByType.validNormalizedTypes, field.normalizedType) - ); - - if (Boolean(oldGroupByType)) { - if (this.groupByType === this.groupByTypes.all) { - this.termSize = null; - this.termField = null; - } else { - this.termSize = this.thresholdWatch.DEFAULT_VALUES.TERM_SIZE; - } - } - } - - this.thresholdWatch.termSize = this.termSize; - this.thresholdWatch.termField = this.termField ? this.termField.name : null; - - this.onChange(this.thresholdWatch); - } - - onThresholdLevelChange = ([thresholdComparator, threshold]) => { - if (!this.initialized) { - return; - } - - this.thresholdComparator = thresholdComparator; - this.threshold = threshold; - - this.thresholdWatch.thresholdComparator = this.thresholdComparator.value; - this.thresholdWatch.threshold = this.threshold; - - this.onChange(this.thresholdWatch); - } - - onTimeWindowChange = ([timeWindowSize, timeWindowUnit]) => { - if (!this.initialized) { - return; - } - - this.timeWindowSize = timeWindowSize; - this.timeWindowUnit = timeWindowUnit; - - this.thresholdWatch.timeWindowSize = this.timeWindowSize; - this.thresholdWatch.timeWindowUnit = this.timeWindowUnit; - - this.onChange(this.thresholdWatch); - } - - isItemVisible = (itemId) => { - return this.items[itemId].isVisible; - } - - isItemOpen = (itemId) => { - return this.items[itemId].isOpen; - } - - onItemOpen = (itemId) => { - forEach(this.items, (item, id) => { - item.isOpen = (itemId === id); - }); - } - - onItemClose = (itemId) => { - this.items[itemId].isOpen = false; - } - - isExpressionValid = () => { - return every(this.items, item => item.isValid || !item.isVisible); - } - - showExpressionValidation = () => { - return some(this.items, item => !item.isValid && item.isVisible && item.isDirty); - } - - checkValidity = () => { - if (this.isExpressionValid()) { - this.onValid(); - } else { - this.onInvalid(); - } - } - - onItemValid = (itemId) => { - this.items[itemId].isValid = true; - - this.checkValidity(); - } - - onItemInvalid = (itemId) => { - this.items[itemId].isValid = false; - - this.checkValidity(); - } - - onItemDirty = (itemId) => { - this.items[itemId].isDirty = true; - } - - onItemPristine = (itemId) => { - this.items[itemId].isDirty = false; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/index.js b/x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/index.js deleted file mode 100644 index 1deb0b2d23559..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './tool_bar_selected_count'; diff --git a/x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/tool_bar_selected_count.html b/x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/tool_bar_selected_count.html deleted file mode 100644 index 94b385d759ecd..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/tool_bar_selected_count.html +++ /dev/null @@ -1,4 +0,0 @@ -
- {{ toolBarSelectedCount.count }} {{ toolBarSelectedCount.count === 1 ? toolBarSelectedCount.singularName : toolBarSelectedCount.pluralName}} - {{ 'xpack.watcher.toolBarSelectedCountText' | i18n: { defaultMessage: 'selected' } }} -
diff --git a/x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/tool_bar_selected_count.js b/x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/tool_bar_selected_count.js deleted file mode 100644 index 0cd5e60bd9ed4..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/tool_bar_selected_count/tool_bar_selected_count.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './tool_bar_selected_count.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('toolBarSelectedCount', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - count: '=', - singularName: '@', - pluralName: '@', - }, - controllerAs: 'toolBarSelectedCount', - bindToController: true, - controller: class ToolBarSelectedCountController { - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/_index.scss b/x-pack/legacy/plugins/watcher/public/components/watch_actions/_index.scss deleted file mode 100644 index 10fdcef69f31c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'watch_actions'; -@import 'components/watch_action/watch_action'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/_watch_actions.scss b/x-pack/legacy/plugins/watcher/public/components/watch_actions/_watch_actions.scss deleted file mode 100644 index f44cb85e8302d..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/_watch_actions.scss +++ /dev/null @@ -1,14 +0,0 @@ -.watchActionsHeader { - display: flex; - - .watchActionsHeaderTitle { - flex: 1 1 auto; - font-size: 18px; - width: 100%; - line-height: $euiSizeXL; - } -} - -.watchAction { - margin-top: $euiSize; -} diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/lib/watch_action_controller_base/index.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/lib/watch_action_controller_base/index.js deleted file mode 100644 index f33bd49bae9ca..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/lib/watch_action_controller_base/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { WatchActionControllerBase } from './watch_action_controller_base'; diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/lib/watch_action_controller_base/watch_action_controller_base.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/lib/watch_action_controller_base/watch_action_controller_base.js deleted file mode 100644 index 22f85efe8c214..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/lib/watch_action_controller_base/watch_action_controller_base.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export class WatchActionControllerBase { - constructor($scope) { - this.action = $scope.action; - this.form = $scope.form; - this.onChange = $scope.onChange; - } - - isValidationMessageVisible = (fieldName, errorType) => { - return this.form[fieldName].$touched && - this.form[fieldName].$error[errorType]; - } -} diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/_watch_action.scss b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/_watch_action.scss deleted file mode 100644 index c5c0c12a5e691..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/_watch_action.scss +++ /dev/null @@ -1,56 +0,0 @@ -.watchAction { - border-top: 1px dashed $euiColorMediumShade; -} - -.watchAction__error { - background-color: $euiColorLightestShade; -} - -.watchActionErrorMessage { - color: $euiColorDanger; -} - -.watchActionSummaryButton { - padding: $euiSize; - width: 100%; - background: transparent; - border: transparent; - text-align: left; - - &:hover { - background: tint($euiColorPrimary, 95%); - } -} - -.watchActionSummaryToggleIcon { - margin-right: $euiSizeS; - width: $euiSizeM; -} - -.watchActionSummaryTypeIcon { - margin-right: $euiSizeXS; -} - -.watchActionSummaryType { - font-size: $euiFontSizeXS; -} - -.watchActionHeader { - display: block; -} - -.watchActionDescription { - display: block; - color: $euiColorDarkShade; - margin-left: $euiSizeL; - margin-top: $euiSizeS; -} - -.watchActionDetails { - background-color: inherit; - padding: 0 $euiSizeM $euiSizeM $euiSizeXXL; -} - -.watchActionSimulateButton { - float: right; -} diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/index.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/index.js deleted file mode 100644 index d6b193cff929a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_action'; diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/watch_action.html b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/watch_action.html deleted file mode 100644 index 7c5c569f60f2b..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/watch_action.html +++ /dev/null @@ -1,57 +0,0 @@ -
- -
-
-
-
-
-
-
- - -
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/watch_action.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/watch_action.js deleted file mode 100644 index 021d4a5ef7e17..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_action/watch_action.js +++ /dev/null @@ -1,76 +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 'ui/fancy_forms'; -import { uiModules } from 'ui/modules'; -import template from './watch_action.html'; -import angular from 'angular'; -import '../watch_email_action'; -import '../watch_logging_action'; -import '../watch_slack_action'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchAction', function ($injector) { - const $compile = $injector.get('$compile'); - - return { - restrict: 'E', - template: template, - replace: true, - scope: { - action: '=', - isCollapsed: '=', - isValid: '=', - onChange: '=', - onToggle: '=', - onSimulate: '=', - onDelete: '=', - onValid: '=', - onInvalid: '=' - }, - bindToController: true, - controllerAs: 'watchAction', - link: function ($scope, $el) { - const $container = $el.find('.watchActionDetailsContent'); - $container.empty(); - - const newScope = $scope.$new(); - newScope.action = $scope.watchAction.action; - newScope.form = $scope.watchAction.form; - newScope.onChange = $scope.watchAction.onChange; - - const $innerEl = angular.element(newScope.action.constructor.template); - const postLink = $compile($innerEl); - $container.append($innerEl); - postLink(newScope); - }, - controller: class WatchActionController { - constructor($scope) { - $scope.$watch('watchAction.isCollapsed', (isCollapsed) => { - if (isCollapsed) { - this.form.$setTouched(true); - } - }); - - $scope.$watch('watchAction.form.$valid', () => { - this.checkValidity(); - }); - } - - checkValidity = () => { - const isValid = !(this.form.$invalid); - - if (isValid) { - this.onValid(this.action); - } else { - this.onInvalid(this.action); - } - } - - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/index.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/index.js deleted file mode 100644 index 56abbca3c50bc..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_email_action'; diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/watch_email_action.html b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/watch_email_action.html deleted file mode 100644 index 8d395448ecfde..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/watch_email_action.html +++ /dev/null @@ -1,72 +0,0 @@ -
- - -
-
-
- - -
-
-
- - -
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/watch_email_action.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/watch_email_action.js deleted file mode 100644 index 104d800869cb6..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_email_action/watch_email_action.js +++ /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 { uiModules } from 'ui/modules'; -import { WatchActionControllerBase } from '../lib/watch_action_controller_base'; -import template from './watch_email_action.html'; -import 'plugins/watcher/services/html_id_generator'; -import 'ui/directives/input_focus'; -import 'plugins/watcher/components/xpack_aria_describes'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchEmailAction', function ($injector) { - const htmlIdGeneratorFactory = $injector.get('xpackWatcherHtmlIdGeneratorFactory'); - - return { - restrict: 'E', - template: template, - bindToController: true, - controllerAs: 'watchEmailAction', - controller: class WatchEmailActionController extends WatchActionControllerBase { - constructor($scope) { - super($scope); - - this.makeId = htmlIdGeneratorFactory.create(this.action.id); - - this.to = this.action.to.join(','); - - $scope.$watch('watchEmailAction.to', (toList) => { - const toArray = (toList || '').split(',').map(to => to.trim()); - this.to = toArray.join(', '); - - this.action.to = toArray; - }); - - $scope.$watchGroup([ - 'action.to', - 'action.subject', - 'action.body' - ], () => { this.onChange(this.action); }); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/index.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/index.js deleted file mode 100644 index 349efc682f74e..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_logging_action'; diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/watch_logging_action.html b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/watch_logging_action.html deleted file mode 100644 index d2a29d733efaa..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/watch_logging_action.html +++ /dev/null @@ -1,25 +0,0 @@ -
- - -
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/watch_logging_action.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/watch_logging_action.js deleted file mode 100644 index 96924b36c17b6..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_logging_action/watch_logging_action.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { WatchActionControllerBase } from '../lib/watch_action_controller_base'; -import template from './watch_logging_action.html'; -import 'ui/directives/input_focus'; -import 'plugins/watcher/services/html_id_generator'; -import 'plugins/watcher/components/xpack_aria_describes'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchLoggingAction', function ($injector) { - const htmlIdGeneratorFactory = $injector.get('xpackWatcherHtmlIdGeneratorFactory'); - - return { - restrict: 'E', - template: template, - bindToController: true, - controllerAs: 'watchLoggingAction', - controller: class WatchLoggingActionController extends WatchActionControllerBase { - constructor($scope) { - super($scope); - - this.makeId = htmlIdGeneratorFactory.create(this.action.id); - - $scope.$watch([ - 'action.text' - ], () => { this.onChange(this.action); }); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/index.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/index.js deleted file mode 100644 index d553c4de48ca8..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_slack_action'; diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/watch_slack_action.html b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/watch_slack_action.html deleted file mode 100644 index 6d6272a2f8e10..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/watch_slack_action.html +++ /dev/null @@ -1,48 +0,0 @@ -
- - -
-
-
- - -
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/watch_slack_action.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/watch_slack_action.js deleted file mode 100644 index ab107b3ad85bb..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/components/watch_slack_action/watch_slack_action.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { WatchActionControllerBase } from '../lib/watch_action_controller_base'; -import template from './watch_slack_action.html'; -import 'ui/directives/input_focus'; -import 'plugins/watcher/services/html_id_generator'; -import 'plugins/watcher/components/xpack_aria_describes'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchSlackAction', function ($injector) { - const htmlIdGeneratorFactory = $injector.get('xpackWatcherHtmlIdGeneratorFactory'); - - return { - restrict: 'E', - template: template, - bindToController: true, - controllerAs: 'watchSlackAction', - controller: class WatchSlackActionController extends WatchActionControllerBase { - constructor($scope) { - super($scope); - - this.makeId = htmlIdGeneratorFactory.create(this.action.id); - - this.to = this.action.to.join(', '); - - $scope.$watch('watchSlackAction.to', (toList) => { - const toArray = (toList || '').split(',').map(to => to.trim()); - this.to = toArray.join(', '); - - this.action.to = toArray; - }); - - $scope.$watchGroup([ - 'action.to', - 'action.text' - ], () => { this.onChange(this.action); }); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/index.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/index.js deleted file mode 100644 index 6a56a78a9737f..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_actions'; diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/watch_actions.html b/x-pack/legacy/plugins/watcher/public/components/watch_actions/watch_actions.html deleted file mode 100644 index 974d2b6a6843e..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/watch_actions.html +++ /dev/null @@ -1,27 +0,0 @@ -
-
-
- -
- - -
diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_actions/watch_actions.js b/x-pack/legacy/plugins/watcher/public/components/watch_actions/watch_actions.js deleted file mode 100644 index 55ee5b5afbedf..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_actions/watch_actions.js +++ /dev/null @@ -1,104 +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 { find, remove, map, difference, forEach } from 'lodash'; -import { uiModules } from 'ui/modules'; -import template from './watch_actions.html'; -import './components/watch_action'; -import 'plugins/watcher/components/action_type_select'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchActions', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - actions: '=', - onActionAdd: '=', - onActionChange: '=', - onActionSimulate: '=', - onActionDelete: '=', - onValid: '=', - onInvalid: '=' - }, - bindToController: true, - controllerAs: 'watchActions', - controller: class WatchActionsController { - constructor($scope) { - this.items = []; - - $scope.$watch('watchActions.actions', (newVal) => { - if (!Boolean(newVal)) { - return; - } - - this.items = map(newVal, action => { - return { - action, - isCollapsed: true, - isValid: true - }; - }); - this.checkValidity(); - }); - } - - onToggle = (action) => { - const item = find(this.items, { action }); - item.isCollapsed = !item.isCollapsed; - } - - onDelete = (action) => { - this.onActionDelete(action); - - const item = find(this.items, { action }); - remove(this.items, item); - - this.checkValidity(); - } - - onActionTypeChange = (type) => { - this.onActionAdd(type); - - const oldActions = map(this.items, item => item.action); - const newActions = difference(this.actions, oldActions); - - forEach(newActions, action => { - this.items.push({ - action, - isCollapsed: false - }); - }); - } - - checkValidity = () => { - const isValid = this.items.length === 0 || this.items.every(i => i.isValid); - - if (isValid) { - this.onValid(this.action); - } else { - this.onInvalid(this.action); - } - } - - onActionValid = (action) => { - const item = find(this.items, { action }); - item.isValid = true; - - this.checkValidity(); - } - - onActionInvalid = (action) => { - const item = find(this.items, { action }); - item.isValid = false; - - this.checkValidity(); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/_index.scss b/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/_index.scss deleted file mode 100644 index 546ba813f2f0d..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'watch_history_item_detail'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/_watch_history_item_detail.scss b/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/_watch_history_item_detail.scss deleted file mode 100644 index 12aa399dab3f5..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/_watch_history_item_detail.scss +++ /dev/null @@ -1,6 +0,0 @@ -watch-history-item-detail { - .json-editor { - height: 400px; - border: 1px solid $euiColorLightShade; - } -} diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/index.js b/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/index.js deleted file mode 100644 index 0efe696d243fd..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_history_item_detail'; diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/watch_history_item_detail.html b/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/watch_history_item_detail.html deleted file mode 100644 index 7a988f3121a45..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/watch_history_item_detail.html +++ /dev/null @@ -1,14 +0,0 @@ -
diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/watch_history_item_detail.js b/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/watch_history_item_detail.js deleted file mode 100644 index 51f5193bff294..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_history_item_detail/watch_history_item_detail.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import 'plugins/watcher/directives/json_input'; -import template from './watch_history_item_detail.html'; -import 'ace'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchHistoryItemDetail', function () { - return { - restrict: 'E', - template: template, - scope: { - watchHistoryItem: '=' - }, - bindToController: true, - controllerAs: 'watchHistoryItemDetail', - controller: class WatchHistoryItemDetailController { - constructor($scope) { - $scope.aceLoaded = (editor) => { - this.editor = editor; - editor.$blockScrolling = Infinity; - }; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_state_icon/index.js b/x-pack/legacy/plugins/watcher/public/components/watch_state_icon/index.js deleted file mode 100644 index a1501d9a72e68..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_state_icon/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_state_icon'; diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_state_icon/watch_state_icon.html b/x-pack/legacy/plugins/watcher/public/components/watch_state_icon/watch_state_icon.html deleted file mode 100644 index 0f50077157811..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_state_icon/watch_state_icon.html +++ /dev/null @@ -1,18 +0,0 @@ -
-
-
-
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_state_icon/watch_state_icon.js b/x-pack/legacy/plugins/watcher/public/components/watch_state_icon/watch_state_icon.js deleted file mode 100644 index 27717ac6ad82c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/watch_state_icon/watch_state_icon.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './watch_state_icon.html'; -import { WATCH_STATES } from 'plugins/watcher/../common/constants'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchStateIcon', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - watchStatus: '=' - }, - bindToController: true, - controllerAs: 'watchStateIcon', - controller: class WatchStateIconController { - constructor() { - this.WATCH_STATES = WATCH_STATES; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/components/watch_status.tsx b/x-pack/legacy/plugins/watcher/public/components/watch_status.tsx new file mode 100644 index 0000000000000..39e6a5247b4a6 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/components/watch_status.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiIcon, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { ACTION_STATES, WATCH_STATES } from '../../common/constants'; + +function StatusIcon({ status }: { status: string }) { + switch (status) { + case WATCH_STATES.FIRING: + case ACTION_STATES.FIRING: + return ; + case WATCH_STATES.OK: + case ACTION_STATES.OK: + case ACTION_STATES.ACKNOWLEDGED: + return ; + case ACTION_STATES.THROTTLED: + return ; + case WATCH_STATES.DISABLED: + return ; + case WATCH_STATES.CONFIG_ERROR: + case WATCH_STATES.ERROR: + case ACTION_STATES.CONFIG_ERROR: + case ACTION_STATES.ERROR: + return ; + } + return null; +} + +export function WatchStatus({ status, size = 's' }: { status: string; size?: 'xs' | 's' | 'm' }) { + return ( + + + + + + {status} + + + ); +} diff --git a/x-pack/legacy/plugins/watcher/public/components/xpack_aria_describes/index.js b/x-pack/legacy/plugins/watcher/public/components/xpack_aria_describes/index.js deleted file mode 100644 index 6dac96d340be9..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/xpack_aria_describes/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './xpack_aria_describes'; diff --git a/x-pack/legacy/plugins/watcher/public/components/xpack_aria_describes/xpack_aria_describes.js b/x-pack/legacy/plugins/watcher/public/components/xpack_aria_describes/xpack_aria_describes.js deleted file mode 100644 index 2fbbe42f7e5a2..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/components/xpack_aria_describes/xpack_aria_describes.js +++ /dev/null @@ -1,81 +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 'jquery'; -import { uiModules } from 'ui/modules'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('xpackAriaDescribes', function () { - return { - restrict: 'A', - link: function ($scope, $ele, attr) { - if (!Object.keys(attr).includes('id')) { - throw new Error(`An element with xpack-aria-describes="${attr.xpackAriaDescribes}" does not have an id attribute defined`); - } - - let selector = ''; - - function updateAriaDescribedBy(elementId, targetIdList, targetLength, remove = false) { - if (!elementId || !targetIdList || targetLength === 0) { - return; - } - - const targetIds = targetIdList.split(' '); - targetIds.forEach(targetId => { - const $target = $(`#${targetId}`); - - // Preserve any existing ids in the target's aria-describedby attribute - const describedby = $target.attr('aria-describedby'); - const describedByParts = describedby ? describedby.split(' ') : []; - const targetIndex = describedByParts.indexOf(elementId); - - if (remove) { - // remove this control's id from the list if it is there. - if (targetIndex !== -1) { - describedByParts.splice(targetIndex, 1); - } - } else { - // add this control's id to the list if it is not already there. - if (targetIndex === -1) { - describedByParts.push(elementId); - } - } - - if (describedByParts.length === 0) { - $target.removeAttr('aria-describedby'); - } else { - const newDescribedBy = describedByParts.join(' '); - $target.attr('aria-describedby', newDescribedBy); - } - }); - } - - $scope.$watch(() => attr.xpackAriaDescribes, (xpackAriaDescribes) => { - const ids = xpackAriaDescribes.split(' '); - selector = ids.map(id => `#${id}`).join(','); - - $scope.targetIdList = xpackAriaDescribes; - }); - - $scope.$watchMulti([ - () => attr.id, - 'targetIdList', - () => $(selector).length - ], ([elementId, targetIdList, targetLength]) => { - updateAriaDescribedBy(elementId, targetIdList, targetLength); - }); - - $scope.$on('$destroy', () => { - const elementId = attr.id; - const targetIdList = $scope.targetIdList; - const targetLength = $(selector).length; - - updateAriaDescribedBy(elementId, targetIdList, targetLength, true); - }); - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/constants/base_path.ts b/x-pack/legacy/plugins/watcher/public/constants/base_path.ts new file mode 100644 index 0000000000000..f5a8c96b68d7c --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/constants/base_path.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 const BASE_PATH = '/management/elasticsearch/watcher/'; diff --git a/x-pack/legacy/plugins/watcher/public/constants/index.js b/x-pack/legacy/plugins/watcher/public/constants/index.js deleted file mode 100644 index c8b655abf167c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/constants/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { TIME_UNITS } from './time_units'; diff --git a/x-pack/legacy/plugins/watcher/public/components/action_type_select/index.js b/x-pack/legacy/plugins/watcher/public/constants/index.ts similarity index 85% rename from x-pack/legacy/plugins/watcher/public/components/action_type_select/index.js rename to x-pack/legacy/plugins/watcher/public/constants/index.ts index 5c476dc06f40e..3120bed81fdf2 100644 --- a/x-pack/legacy/plugins/watcher/public/components/action_type_select/index.js +++ b/x-pack/legacy/plugins/watcher/public/constants/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import './action_type_select'; +export { BASE_PATH } from './base_path'; diff --git a/x-pack/legacy/plugins/watcher/public/constants/time_units.js b/x-pack/legacy/plugins/watcher/public/constants/time_units.js deleted file mode 100644 index 0545788247269..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/constants/time_units.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { TIME_UNITS as COMMON_TIME_UNITS } from 'plugins/watcher/../common/constants/time_units'; -import { i18n } from '@kbn/i18n'; - -export const TIME_UNITS = { - [COMMON_TIME_UNITS.SECOND]: { - labelPlural: i18n.translate('xpack.watcher.timeUnits.secondPluralLabel', { - defaultMessage: 'seconds', - }), - labelSingular: i18n.translate('xpack.watcher.timeUnits.secondSingularLabel', { - defaultMessage: 'second', - }), - }, - [COMMON_TIME_UNITS.MINUTE]: { - labelPlural: i18n.translate('xpack.watcher.timeUnits.minutePluralLabel', { - defaultMessage: 'minutes', - }), - labelSingular: i18n.translate('xpack.watcher.timeUnits.minuteSingularLabel', { - defaultMessage: 'minute', - }) - }, - [COMMON_TIME_UNITS.HOUR]: { - labelPlural: i18n.translate('xpack.watcher.timeUnits.hourPluralLabel', { - defaultMessage: 'hours', - }), - labelSingular: i18n.translate('xpack.watcher.timeUnits.hourSingularLabel', { - defaultMessage: 'hour', - }), - }, - [COMMON_TIME_UNITS.DAY]: { - labelPlural: i18n.translate('xpack.watcher.timeUnits.dayPluralLabel', { - defaultMessage: 'days', - }), - labelSingular: i18n.translate('xpack.watcher.timeUnits.daySingularLabel', { - defaultMessage: 'day', - }), - } -}; diff --git a/x-pack/legacy/plugins/watcher/public/directives/check_box/check_box.html b/x-pack/legacy/plugins/watcher/public/directives/check_box/check_box.html deleted file mode 100644 index a9c7e904c5aa6..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/check_box/check_box.html +++ /dev/null @@ -1,6 +0,0 @@ - diff --git a/x-pack/legacy/plugins/watcher/public/directives/check_box/check_box.js b/x-pack/legacy/plugins/watcher/public/directives/check_box/check_box.js deleted file mode 100644 index 19ee351b0717e..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/check_box/check_box.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './check_box.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('checkBox', function () { - return { - restrict: 'E', - replace: true, - template, - scope: { - id: '=', - isSelected: '=', - onSelectChange: '=', - }, - controllerAs: 'checkBox', - bindToController: true, - controller: class CheckBoxController { - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/directives/check_box/index.js b/x-pack/legacy/plugins/watcher/public/directives/check_box/index.js deleted file mode 100644 index 15972a56dcc36..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/check_box/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './check_box'; diff --git a/x-pack/legacy/plugins/watcher/public/directives/json_input.js b/x-pack/legacy/plugins/watcher/public/directives/json_input.js deleted file mode 100644 index a4ee023f93328..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/json_input.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import _ from 'lodash'; -import angular from 'angular'; -import { uiModules } from 'ui/modules'; - -uiModules - .get('xpack/watcher') - .directive('jsonInput', function () { - return { - restrict: 'A', - require: 'ngModel', - link: function (scope, $el, attrs, ngModelCntrl) { - ngModelCntrl.$formatters.push(toJSON); - ngModelCntrl.$parsers.push(fromJSON); - - function fromJSON(value) { - try { - value = JSON.parse(value); - const validity = !scope.$eval(attrs.requireKeys) ? true : _.keys(value).length > 0; - ngModelCntrl.$setValidity('json', validity); - } catch (e) { - ngModelCntrl.$setValidity('json', false); - } - return value; - } - - function toJSON(value) { - return angular.toJson(value, 2); - } - } - }; - }); - diff --git a/x-pack/legacy/plugins/watcher/public/directives/moment.js b/x-pack/legacy/plugins/watcher/public/directives/moment.js deleted file mode 100644 index e6c0aa27b6914..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/moment.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import moment from 'moment'; -import _ from 'lodash'; -import { uiModules } from 'ui/modules'; - -uiModules - .get('xpack/watcher') - .filter('moment', function (config) { - return function (datetime) { - const format = config.get('dateFormat'); - if (moment.isMoment(datetime)) return datetime.format(format); - if (_.isDate(datetime)) return moment(datetime).format(format); - return datetime; - }; - }); diff --git a/x-pack/legacy/plugins/watcher/public/directives/sortable_column/index.js b/x-pack/legacy/plugins/watcher/public/directives/sortable_column/index.js deleted file mode 100644 index dde1c9b375f83..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/sortable_column/index.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - -import './sortable_column'; diff --git a/x-pack/legacy/plugins/watcher/public/directives/sortable_column/sortable_column.html b/x-pack/legacy/plugins/watcher/public/directives/sortable_column/sortable_column.html deleted file mode 100644 index 25a14d553fb30..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/sortable_column/sortable_column.html +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/x-pack/legacy/plugins/watcher/public/directives/sortable_column/sortable_column.js b/x-pack/legacy/plugins/watcher/public/directives/sortable_column/sortable_column.js deleted file mode 100644 index e93e19db72aee..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/sortable_column/sortable_column.js +++ /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 { uiModules } from 'ui/modules'; -import { i18n } from '@kbn/i18n'; -import template from './sortable_column.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('sortableColumn', function () { - return { - restrict: 'E', - transclude: true, - template: template, - scope: { - field: '@', - sortField: '=', - sortReverse: '=', - onSortChange: '=', - }, - controllerAs: 'sortableColumn', - bindToController: true, - controller: class SortableColumnController { - toggle = () => { - if (this.sortField === this.field) { - this.onSortChange(this.field, !this.sortReverse); - } else { - this.onSortChange(this.field, false); - } - } - - getAriaLabel() { - const isAscending = this.isSortedAscending(); - if(isAscending) { - return i18n.translate('xpack.watcher.sortableColumn.sortAscendingAriaLabel', { - defaultMessage: 'Sort {field} ascending', - values: { field: this.field }, - }); - } - return i18n.translate('xpack.watcher.sortableColumn.sortDescendingAriaLabel', { - defaultMessage: 'Sort {field} descending', - values: { field: this.field }, - }); - } - - isSorted() { - return this.sortField === this.field; - } - - isSortedAscending() { - return this.isSorted() && !this.sortReverse; - } - - isSortedDescending() { - return this.isSorted() && this.sortReverse; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/directives/toggle_button/index.js b/x-pack/legacy/plugins/watcher/public/directives/toggle_button/index.js deleted file mode 100644 index e88875670a782..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/toggle_button/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './toggle_button'; diff --git a/x-pack/legacy/plugins/watcher/public/directives/toggle_button/toggle_button.html b/x-pack/legacy/plugins/watcher/public/directives/toggle_button/toggle_button.html deleted file mode 100644 index d47103f758d70..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/toggle_button/toggle_button.html +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/x-pack/legacy/plugins/watcher/public/directives/toggle_button/toggle_button.js b/x-pack/legacy/plugins/watcher/public/directives/toggle_button/toggle_button.js deleted file mode 100644 index 90abd3adfcd14..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/toggle_button/toggle_button.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './toggle_button.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('toggleButton', function () { - return { - restrict: 'E', - replace: true, - transclude: true, - template: template, - scope: { - isDisabled: '=', - isCollapsed: '=', - onClick: '=', - }, - controllerAs: 'toggleButton', - bindToController: true, - controller: class ToggleButtonController { - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/directives/toggle_panel/index.js b/x-pack/legacy/plugins/watcher/public/directives/toggle_panel/index.js deleted file mode 100644 index d273e60278a62..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/toggle_panel/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './toggle_panel'; diff --git a/x-pack/legacy/plugins/watcher/public/directives/toggle_panel/toggle_panel.html b/x-pack/legacy/plugins/watcher/public/directives/toggle_panel/toggle_panel.html deleted file mode 100644 index 6b2e6b8506337..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/toggle_panel/toggle_panel.html +++ /dev/null @@ -1,15 +0,0 @@ -
-
- - {{togglePanel.buttonText}} - -
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/directives/toggle_panel/toggle_panel.js b/x-pack/legacy/plugins/watcher/public/directives/toggle_panel/toggle_panel.js deleted file mode 100644 index a4e5656bf451b..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/toggle_panel/toggle_panel.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './toggle_panel.html'; -import '../toggle_button'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('togglePanel', function () { - return { - restrict: 'E', - replace: true, - transclude: true, - template: template, - scope: { - togglePanelId: '@', - buttonText: '@', - isDisabled: '=', - isCollapsed: '=', - onToggle: '=' - }, - controllerAs: 'togglePanel', - bindToController: true, - controller: class TogglePanelController { - toggle = () => { - this.onToggle(this.togglePanelId); - }; - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/directives/tooltip/index.js b/x-pack/legacy/plugins/watcher/public/directives/tooltip/index.js deleted file mode 100644 index 4812ceef7dc30..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/tooltip/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './tooltip'; diff --git a/x-pack/legacy/plugins/watcher/public/directives/tooltip/tooltip.html b/x-pack/legacy/plugins/watcher/public/directives/tooltip/tooltip.html deleted file mode 100644 index 6701d47e7692b..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/tooltip/tooltip.html +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/directives/tooltip/tooltip.js b/x-pack/legacy/plugins/watcher/public/directives/tooltip/tooltip.js deleted file mode 100644 index a6f5a8ca28126..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/directives/tooltip/tooltip.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import 'ui/angular-bootstrap'; -import html from './tooltip.html'; -import chrome from 'ui/chrome'; -import { uiModules } from 'ui/modules'; - -uiModules.get('xpack/watcher') - .config(function ($tooltipProvider) { - // we use the uiSettings client because the config service is not available in the config phase - const uiSettings = chrome.getUiSettingsClient(); - - $tooltipProvider.setTriggers({ 'mouseenter': 'mouseleave click' }); - - $tooltipProvider.options({ - placement: 'bottom', - animation: !uiSettings.get('accessibility:disableAnimations'), - popupDelay: 150, - appendToBody: false - }); - }) - .directive('kbnTooltip', function () { - return { - restrict: 'E', - template: html, - transclude: true, - replace: true, - scope: true, - link: function ($scope, $el, attr) { - $scope.text = attr.text; - $scope.placement = attr.placement || 'top'; - $scope.delay = attr.delay || 400; - $scope.appendToBody = attr.appendToBody || 0; - } - }; - }); diff --git a/x-pack/legacy/plugins/watcher/public/components/action_state_icon/index.js b/x-pack/legacy/plugins/watcher/public/index.js similarity index 78% rename from x-pack/legacy/plugins/watcher/public/components/action_state_icon/index.js rename to x-pack/legacy/plugins/watcher/public/index.js index dd97a047c4555..c1b84e76d0008 100644 --- a/x-pack/legacy/plugins/watcher/public/components/action_state_icon/index.js +++ b/x-pack/legacy/plugins/watcher/public/index.js @@ -4,4 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -import './action_state_icon'; +import './register_route'; +import './register_management_sections'; diff --git a/x-pack/legacy/plugins/watcher/public/index.scss b/x-pack/legacy/plugins/watcher/public/index.scss index b66733cc06c7e..33ebf21326c7b 100644 --- a/x-pack/legacy/plugins/watcher/public/index.scss +++ b/x-pack/legacy/plugins/watcher/public/index.scss @@ -1,29 +1,25 @@ // Import the EUI global scope so we can use EUI constants @import 'src/legacy/ui/public/styles/_styling_constants'; -// Temporary hacks -@import 'hacks'; - // Watcher plugin styles // Prefix all styles with "watcher" to avoid conflicts. // Examples -// watcher__ +// watcherChart +// watcherChart__legend +// watcherChart__legend--small +// watcherChart__legend-isLoading + +.watcherThresholdWatchActionDropdownContainer { + justify-content: flex-end; + flex-direction: row; +} + +.watcherThresholdWatchActionContextMenuItem, +.watcherThresholdWatchInBetweenComparatorText { + align-self: center; +} -@import 'components/action_type_select/index'; -@import 'components/chart_tooltip/index'; -@import 'components/duration_select/index'; -@import 'components/expression_builder/index'; -@import 'components/flot_chart/index'; -@import 'components/index_select/index'; -@import 'components/json_editor/index'; -@import 'components/panel_pager/index'; -@import 'components/threshold_preview_chart/index'; -@import 'components/threshold_watch_expression/index'; -@import 'components/watch_actions/index'; -@import 'components/watch_history_item_detail/index'; -@import 'sections/watch_edit/components/threshold_watch_edit/index'; -@import 'sections/watch_edit/components/watch_edit_detail/index'; -@import 'sections/watch_edit/components/watch_edit_execute_detail/index'; -@import 'sections/watch_edit/components/watch_edit_title_panel/index'; -@import 'sections/watch_list/components/watch_list/index'; +.watcherThresholdAlertAggFieldContainer { + width: 300px; +} diff --git a/x-pack/legacy/plugins/watcher/public/lib/api.ts b/x-pack/legacy/plugins/watcher/public/lib/api.ts new file mode 100644 index 0000000000000..359725f5540da --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/lib/api.ts @@ -0,0 +1,193 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { Settings } from 'plugins/watcher/models/settings'; +import { Watch } from 'plugins/watcher/models/watch'; +import { WatchHistoryItem } from 'plugins/watcher/models/watch_history_item'; +import { WatchStatus } from 'plugins/watcher/models/watch_status'; + +import { __await } from 'tslib'; +import chrome from 'ui/chrome'; +import { ROUTES } from '../../common/constants'; +import { BaseWatch, ExecutedWatchDetails } from '../../common/types/watch_types'; +import { useRequest, sendRequest } from './use_request'; + +let httpClient: ng.IHttpService; + +export const setHttpClient = (anHttpClient: ng.IHttpService) => { + httpClient = anHttpClient; +}; + +export const getHttpClient = () => { + return httpClient; +}; + +let savedObjectsClient: any; + +export const setSavedObjectsClient = (aSavedObjectsClient: any) => { + savedObjectsClient = aSavedObjectsClient; +}; + +export const getSavedObjectsClient = () => { + return savedObjectsClient; +}; + +const basePath = chrome.addBasePath(ROUTES.API_ROOT); + +export const loadWatches = (interval: number) => { + return useRequest({ + path: `${basePath}/watches`, + method: 'get', + interval, + processData: ({ watches = [] }: { watches: any[] }) => { + return watches.map((watch: any) => Watch.fromUpstreamJson(watch)); + }, + }); +}; + +export const loadWatchDetail = (id: string) => { + return useRequest({ + path: `${basePath}/watch/${id}`, + method: 'get', + processData: ({ watch = {} }: { watch: any }) => Watch.fromUpstreamJson(watch), + }); +}; + +export const loadWatchHistory = (id: string, startTime: string) => { + let path = `${basePath}/watch/${id}/history`; + + if (startTime) { + path += `?startTime=${startTime}`; + } + + return useRequest({ + path, + method: 'get', + processData: ({ watchHistoryItems = [] }: { watchHistoryItems: any }) => { + return watchHistoryItems.map((historyItem: any) => + WatchHistoryItem.fromUpstreamJson(historyItem) + ); + }, + }); +}; + +export const loadWatchHistoryDetail = (id: string | undefined) => { + return useRequest({ + path: !id ? undefined : `${basePath}/history/${id}`, + method: 'get', + processData: ({ watchHistoryItem }: { watchHistoryItem: any }) => + WatchHistoryItem.fromUpstreamJson(watchHistoryItem), + }); +}; + +export const deleteWatches = async (watchIds: string[]) => { + const body = { + watchIds, + }; + const { + data: { results }, + } = await getHttpClient().post(`${basePath}/watches/delete`, body); + return results; +}; + +export const deactivateWatch = async (id: string) => { + return sendRequest({ + path: `${basePath}/watch/${id}/deactivate`, + method: 'put', + }); +}; + +export const activateWatch = async (id: string) => { + return sendRequest({ + path: `${basePath}/watch/${id}/activate`, + method: 'put', + }); +}; + +export const loadWatch = async (id: string) => { + const { data: watch } = await getHttpClient().get(`${basePath}/watch/${id}`); + return Watch.fromUpstreamJson(watch.watch); +}; + +export const getMatchingIndices = async (pattern: string) => { + if (!pattern.startsWith('*')) { + pattern = `*${pattern}`; + } + if (!pattern.endsWith('*')) { + pattern = `${pattern}*`; + } + const { + data: { indices }, + } = await getHttpClient().post(`${basePath}/indices`, { pattern }); + return indices; +}; + +export const fetchFields = async (indexes: string[]) => { + const { + data: { fields }, + } = await getHttpClient().post(`${basePath}/fields`, { indexes }); + return fields; +}; + +export const createWatch = async (watch: BaseWatch) => { + const { data } = await getHttpClient().put(`${basePath}/watch/${watch.id}`, watch.upstreamJson); + return data; +}; + +export const executeWatch = async (executeWatchDetails: ExecutedWatchDetails, watch: BaseWatch) => { + return sendRequest({ + path: `${basePath}/watch/execute`, + method: 'put', + body: { + executeDetails: executeWatchDetails.upstreamJson, + watch: watch.upstreamJson, + }, + }); +}; + +export const loadIndexPatterns = async () => { + const { savedObjects } = await getSavedObjectsClient().find({ + type: 'index-pattern', + fields: ['title'], + perPage: 10000, + }); + return savedObjects; +}; + +export const getWatchVisualizationData = (watchModel: BaseWatch, visualizeOptions: any) => { + return useRequest({ + path: `${basePath}/watch/visualize`, + method: 'post', + body: { + watch: watchModel.upstreamJson, + options: visualizeOptions.upstreamJson, + }, + processData: ({ visualizeData }: { visualizeData: any }) => visualizeData, + }); +}; + +export const loadSettings = () => { + return useRequest({ + path: `${basePath}/settings`, + method: 'get', + processData: (data: { + action_types: { + [key: string]: { + enabled: boolean; + }; + }; + }) => Settings.fromUpstreamJson(data), + }); +}; + +export const ackWatchAction = async (watchId: string, actionId: string) => { + const { + data: { watchStatus }, + } = await getHttpClient().put( + `${basePath}/watch/${watchId}/action/${actionId}/acknowledge`, + null + ); + return WatchStatus.fromUpstreamJson(watchStatus); +}; diff --git a/x-pack/legacy/plugins/watcher/public/lib/breadcrumbs.js b/x-pack/legacy/plugins/watcher/public/lib/breadcrumbs.js deleted file mode 100644 index c30bc4175375a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/breadcrumbs.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 chrome from 'ui/chrome'; -import { i18n } from '@kbn/i18n'; - -import { MANAGEMENT_BREADCRUMB } from 'ui/management'; - -const uiSettings = chrome.getUiSettingsClient(); - -export function getWatchListBreadcrumbs() { - return [ - MANAGEMENT_BREADCRUMB, - { - text: i18n.translate('xpack.watcher.list.breadcrumb', { - defaultMessage: 'Watcher' - }), - href: '#/management/elasticsearch/watcher/watches/' - } - ]; -} - -export function getWatchDetailBreadcrumbs($route) { - const watch = $route.current.locals.watch || $route.current.locals.xpackWatch; - - return [ - ...getWatchListBreadcrumbs(), - { - text: !watch.isNew - ? watch.name - : i18n.translate('xpack.watcher.create.breadcrumb', { defaultMessage: 'Create' }), - href: '#/management/elasticsearch/watcher/watches/watch/23eebf28-94fd-47e9-ac44-6fee6e427c33' - } - ]; -} - -export function getWatchHistoryBreadcrumbs($route) { - const { watchHistoryItem } = $route.current.locals; - - return [ - ...getWatchDetailBreadcrumbs($route), - { - text: watchHistoryItem.startTime.format(uiSettings.get('dateFormat')) - } - ]; -} diff --git a/x-pack/legacy/plugins/watcher/public/lib/breadcrumbs.ts b/x-pack/legacy/plugins/watcher/public/lib/breadcrumbs.ts new file mode 100644 index 0000000000000..c8c5d66e37d8f --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/lib/breadcrumbs.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +interface Breadcrumb { + text: string; + href?: string; +} + +export const listBreadcrumb: Breadcrumb = { + text: i18n.translate('xpack.watcher.breadcrumb.listLabel', { + defaultMessage: 'Watcher', + }), + href: '#/management/elasticsearch/watcher/watches/', +}; + +export const createBreadcrumb: Breadcrumb = { + text: i18n.translate('xpack.watcher.breadcrumb.createLabel', { + defaultMessage: 'Create', + }), +}; + +export const editBreadcrumb: Breadcrumb = { + text: i18n.translate('xpack.watcher.breadcrumb.editLabel', { + defaultMessage: 'Edit', + }), +}; + +export const statusBreadcrumb: Breadcrumb = { + text: i18n.translate('xpack.watcher.breadcrumb.statusLabel', { + defaultMessage: 'Status', + }), +}; diff --git a/x-pack/legacy/plugins/watcher/public/lib/clamp/clamp.js b/x-pack/legacy/plugins/watcher/public/lib/clamp/clamp.js deleted file mode 100644 index 00a0b1a49c389..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/clamp/clamp.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export function clamp(val, min, max) { - return Math.min(Math.max(min, val), max); -} diff --git a/x-pack/legacy/plugins/watcher/public/lib/clamp/index.js b/x-pack/legacy/plugins/watcher/public/lib/clamp/index.js deleted file mode 100644 index 58c55bae647b5..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/clamp/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { clamp } from './clamp'; diff --git a/x-pack/legacy/plugins/watcher/public/lib/documentation_links/documentation_links.js b/x-pack/legacy/plugins/watcher/public/lib/documentation_links/documentation_links.js deleted file mode 100644 index 4a807d3749394..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/documentation_links/documentation_links.js +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { makeDocumentationLink } from './make_documentation_link'; - -export const documentationLinks = { - watcher: { - putWatchApi: makeDocumentationLink('{baseUrl}guide/en/elasticsearch/reference/{urlVersion}/watcher-api-put-watch.html') - } -}; diff --git a/x-pack/legacy/plugins/watcher/public/lib/documentation_links/documentation_links.ts b/x-pack/legacy/plugins/watcher/public/lib/documentation_links/documentation_links.ts new file mode 100644 index 0000000000000..88f23465d33e8 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/lib/documentation_links/documentation_links.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; +import { ACTION_TYPES } from '../../../common/constants'; + +const elasticDocLinkBase = `${ELASTIC_WEBSITE_URL}guide/en/`; + +const esBase = `${elasticDocLinkBase}elasticsearch/reference/${DOC_LINK_VERSION}`; +const esStackBase = `${elasticDocLinkBase}elastic-stack-overview/${DOC_LINK_VERSION}`; +const kibanaBase = `${elasticDocLinkBase}kibana/${DOC_LINK_VERSION}`; + +export const putWatchApiUrl = `${esBase}/watcher-api-put-watch.html`; +export const executeWatchApiUrl = `${esBase}/watcher-api-execute-watch.html#watcher-api-execute-watch-action-mode`; +export const watcherGettingStartedUrl = `${kibanaBase}/watcher-ui.html`; + +export const watchActionsConfigurationMap = { + [ACTION_TYPES.SLACK]: `${esStackBase}/actions-slack.html#configuring-slack`, + [ACTION_TYPES.PAGERDUTY]: `${esStackBase}/actions-pagerduty.html#configuring-pagerduty`, + [ACTION_TYPES.JIRA]: `${esStackBase}/actions-jira.html#configuring-jira`, +}; diff --git a/x-pack/legacy/plugins/watcher/public/lib/documentation_links/index.js b/x-pack/legacy/plugins/watcher/public/lib/documentation_links/index.js deleted file mode 100644 index 2972d0ca80603..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/documentation_links/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { documentationLinks } from './documentation_links.js'; diff --git a/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/index.js b/x-pack/legacy/plugins/watcher/public/lib/documentation_links/index.ts similarity index 86% rename from x-pack/legacy/plugins/watcher/public/components/chart_tooltip/index.js rename to x-pack/legacy/plugins/watcher/public/lib/documentation_links/index.ts index 09d3ddf9ae8d6..81e0c494e28b3 100644 --- a/x-pack/legacy/plugins/watcher/public/components/chart_tooltip/index.js +++ b/x-pack/legacy/plugins/watcher/public/lib/documentation_links/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -import './chart_tooltip'; +export * from './documentation_links'; diff --git a/x-pack/legacy/plugins/watcher/public/lib/documentation_links/make_documentation_link.js b/x-pack/legacy/plugins/watcher/public/lib/documentation_links/make_documentation_link.js deleted file mode 100644 index ebe81ae0f3a0f..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/documentation_links/make_documentation_link.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import semver from 'semver'; -import { metadata } from 'ui/metadata'; - -const major = semver.major(metadata.version); -const minor = semver.minor(metadata.version); - -const urlVersion = `${major}.${minor}`; -const baseUrl = 'https://www.elastic.co/'; - -/** - * - * @param {string} linkTemplate Link template containing {baseUrl} and {urlVersion} placeholders - * @return {string} Actual link, with placeholders in template replaced - */ -export function makeDocumentationLink(linkTemplate) { - return linkTemplate - .replace('{baseUrl}', baseUrl) - .replace('{urlVersion}', urlVersion); -} diff --git a/x-pack/legacy/plugins/watcher/public/lib/format_date.ts b/x-pack/legacy/plugins/watcher/public/lib/format_date.ts new file mode 100644 index 0000000000000..f300ce73ad71a --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/lib/format_date.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const getDateMathFormat = (timeUnit: string, timeValue: number) => { + const now = 'now'; + + if (timeValue === 0) { + return now; + } + + return `${now}+${timeValue}${timeUnit}`; +}; diff --git a/x-pack/legacy/plugins/watcher/public/lib/get_search_value/get_search_value.js b/x-pack/legacy/plugins/watcher/public/lib/get_search_value.ts similarity index 90% rename from x-pack/legacy/plugins/watcher/public/lib/get_search_value/get_search_value.js rename to x-pack/legacy/plugins/watcher/public/lib/get_search_value.ts index 5c900437db119..27b2e0229286b 100644 --- a/x-pack/legacy/plugins/watcher/public/lib/get_search_value/get_search_value.js +++ b/x-pack/legacy/plugins/watcher/public/lib/get_search_value.ts @@ -11,6 +11,6 @@ import { pick, values } from 'lodash'; * @param array of property keys to use to generate the search value * @return newline delimited string built from the specified properties */ -export function getSearchValue(obj, fields) { +export function getSearchValue(obj: {}, fields: any[]) { return values(pick(obj, fields)).join('\n'); } diff --git a/x-pack/legacy/plugins/watcher/public/lib/get_search_value/index.js b/x-pack/legacy/plugins/watcher/public/lib/get_search_value/index.js deleted file mode 100644 index f3f1b49908edb..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/get_search_value/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { getSearchValue } from './get_search_value'; diff --git a/x-pack/legacy/plugins/watcher/public/lib/get_time_unit_label.ts b/x-pack/legacy/plugins/watcher/public/lib/get_time_unit_label.ts new file mode 100644 index 0000000000000..35bd19e7007c6 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/lib/get_time_unit_label.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { TIME_UNITS } from '../../common/constants'; + +export function getTimeUnitLabel(timeUnit = TIME_UNITS.SECOND, timeValue = '0') { + switch (timeUnit) { + case TIME_UNITS.SECOND: + return i18n.translate('xpack.watcher.timeUnits.secondLabel', { + defaultMessage: '{timeValue, plural, one {second} other {seconds}}', + values: { timeValue }, + }); + case TIME_UNITS.MINUTE: + return i18n.translate('xpack.watcher.timeUnits.minuteLabel', { + defaultMessage: '{timeValue, plural, one {minute} other {minutes}}', + values: { timeValue }, + }); + case TIME_UNITS.HOUR: + return i18n.translate('xpack.watcher.timeUnits.hourLabel', { + defaultMessage: '{timeValue, plural, one {hour} other {hours}}', + values: { timeValue }, + }); + case TIME_UNITS.DAY: + return i18n.translate('xpack.watcher.timeUnits.dayLabel', { + defaultMessage: '{timeValue, plural, one {day} other {days}}', + values: { timeValue }, + }); + } +} diff --git a/x-pack/legacy/plugins/watcher/public/lib/get_time_units_label/get_time_units_label.js b/x-pack/legacy/plugins/watcher/public/lib/get_time_units_label/get_time_units_label.js deleted file mode 100644 index 0388740684a41..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/get_time_units_label/get_time_units_label.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { TIME_UNITS } from 'plugins/watcher/constants'; - -export const getTimeUnitsLabel = (unit, size) => { - const timeUnit = TIME_UNITS[unit]; - return size === 1 - ? timeUnit.labelSingular - : timeUnit.labelPlural; -}; diff --git a/x-pack/legacy/plugins/watcher/public/lib/get_time_units_label/index.js b/x-pack/legacy/plugins/watcher/public/lib/get_time_units_label/index.js deleted file mode 100644 index 19fbb4d1fde28..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/get_time_units_label/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { getTimeUnitsLabel } from './get_time_units_label'; diff --git a/x-pack/legacy/plugins/watcher/public/lib/manage_angular_lifecycle.js b/x-pack/legacy/plugins/watcher/public/lib/manage_angular_lifecycle.js new file mode 100644 index 0000000000000..3813e632a0a73 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/lib/manage_angular_lifecycle.js @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { unmountComponentAtNode } from 'react-dom'; + +export const manageAngularLifecycle = ($scope, $route, elem) => { + const lastRoute = $route.current; + + const deregister = $scope.$on('$locationChangeSuccess', () => { + const currentRoute = $route.current; + if (lastRoute.$$route.template === currentRoute.$$route.template) { + $route.current = lastRoute; + } + }); + + $scope.$on('$destroy', () => { + deregister && deregister(); + elem && unmountComponentAtNode(elem); + }); +}; diff --git a/x-pack/legacy/plugins/watcher/public/lib/navigation.ts b/x-pack/legacy/plugins/watcher/public/lib/navigation.ts new file mode 100644 index 0000000000000..87fa95ce53410 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/lib/navigation.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { BASE_PATH } from '../constants'; +let router: any; +export const registerRouter = (aRouter: any) => { + router = aRouter; +}; + +export const goToWatchList = () => { + router.history.push({ pathname: `${BASE_PATH}watches` }); +}; + +export const goToCreateThresholdAlert = () => { + router.history.push({ pathname: `${BASE_PATH}watches/new-watch/threshold` }); +}; + +export const goToCreateAdvancedWatch = () => { + router.history.push({ pathname: `${BASE_PATH}watches/new-watch/json` }); +}; diff --git a/x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/__tests__/sortable_boolean.js b/x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/__tests__/sortable_boolean.js deleted file mode 100644 index b51130c836459..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/__tests__/sortable_boolean.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { sortableBoolean } from '../sortable_boolean'; - -describe('sortable_boolean', () => { - - const trueExpected = { value: true, sortOrder: -1 }; - const falseExpected = { value: false, sortOrder: 0 }; - - it('sets the sortOrder property correctly', () => { - const trueActual = sortableBoolean(true); - const falseActual = sortableBoolean(false); - - expect(trueActual.sortOrder).to.be(trueExpected.sortOrder); - expect(falseActual.sortOrder).to.be(falseExpected.sortOrder); - }); - - it('sets the value property correctly', () => { - expect(sortableBoolean().value).to.be(falseExpected.value); - expect(sortableBoolean(0).value).to.be(falseExpected.value); - expect(sortableBoolean(null).value).to.be(falseExpected.value); - expect(sortableBoolean('').value).to.be(falseExpected.value); - expect(sortableBoolean(false).value).to.be(falseExpected.value); - - expect(sortableBoolean(true).value).to.be(trueExpected.value); - expect(sortableBoolean(1).value).to.be(trueExpected.value); - expect(sortableBoolean('true').value).to.be(trueExpected.value); - expect(sortableBoolean('false').value).to.be(trueExpected.value); - expect(sortableBoolean('foo').value).to.be(trueExpected.value); - expect(sortableBoolean([]).value).to.be(trueExpected.value); - expect(sortableBoolean({}).value).to.be(trueExpected.value); - }); - -}); diff --git a/x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/index.js b/x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/index.js deleted file mode 100644 index eb77a0c9795c8..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { sortableBoolean } from './sortable_boolean'; diff --git a/x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/sortable_boolean.js b/x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/sortable_boolean.js deleted file mode 100644 index 498b456e9d588..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/sortable_boolean/sortable_boolean.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export function sortableBoolean(val) { - const boolVal = Boolean(val); - - return { - value: boolVal, - sortOrder: boolVal ? -1 : 0 - }; -} diff --git a/x-pack/legacy/plugins/watcher/public/lib/update_management_sections/index.js b/x-pack/legacy/plugins/watcher/public/lib/update_management_sections/index.js deleted file mode 100644 index eebe0e41ae8a3..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/update_management_sections/index.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { updateWatchSections } from './update_watch_sections'; -export { updateHistorySection } from './update_history_section'; diff --git a/x-pack/legacy/plugins/watcher/public/lib/update_management_sections/update_history_section.js b/x-pack/legacy/plugins/watcher/public/lib/update_management_sections/update_history_section.js deleted file mode 100644 index 0b5275811d666..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/update_management_sections/update_history_section.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { management } from 'ui/management'; - -export function updateHistorySection(display) { - const statusSection = management.getSection('elasticsearch/watcher/watch/status'); - const editSection = management.getSection('elasticsearch/watcher/watch/edit'); - const newSection = management.getSection('elasticsearch/watcher/watch/new'); - const historySection = management.getSection('elasticsearch/watcher/watch/history-item'); - - newSection.hide(); - statusSection.hide(); - editSection.hide(); - historySection.show(); - historySection.display = display; -} diff --git a/x-pack/legacy/plugins/watcher/public/lib/update_management_sections/update_watch_sections.js b/x-pack/legacy/plugins/watcher/public/lib/update_management_sections/update_watch_sections.js deleted file mode 100644 index 2e78a94a0e6a8..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/lib/update_management_sections/update_watch_sections.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { management } from 'ui/management'; - -export function updateWatchSections(watchId) { - const statusSection = management.getSection('elasticsearch/watcher/watch/status'); - const editSection = management.getSection('elasticsearch/watcher/watch/edit'); - const newSection = management.getSection('elasticsearch/watcher/watch/new'); - const historySection = management.getSection('elasticsearch/watcher/watch/history-item'); - - newSection.hide(); - statusSection.hide(); - editSection.hide(); - historySection.hide(); - - if (watchId) { - statusSection.url = `#/management/elasticsearch/watcher/watches/watch/${watchId}/status`; - editSection.url = `#/management/elasticsearch/watcher/watches/watch/${watchId}/edit`; - - statusSection.show(); - editSection.show(); - } else { - newSection.show(); - } -} diff --git a/x-pack/legacy/plugins/watcher/public/lib/use_request.ts b/x-pack/legacy/plugins/watcher/public/lib/use_request.ts new file mode 100644 index 0000000000000..d13990f7d5ccc --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/lib/use_request.ts @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect, useState } from 'react'; +import { getHttpClient } from './api'; + +interface SendRequest { + path?: string; + method: string; + body?: any; +} + +interface SendRequestResponse { + data: any; + error: Error; +} + +export const sendRequest = async ({ + path, + method, + body, +}: SendRequest): Promise> => { + try { + const response = await (getHttpClient() as any)[method](path, body); + + if (typeof response.data === 'undefined') { + throw new Error(response.statusText); + } + + return { + data: response.data, + }; + } catch (e) { + return { + error: e.response ? e.response : e, + }; + } +}; + +interface UseRequest extends SendRequest { + interval?: number; + initialData?: any; + processData?: any; +} + +export const useRequest = ({ + path, + method, + body, + interval, + initialData, + processData, +}: UseRequest) => { + const [error, setError] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [data, setData] = useState(initialData); + + // Tied to every render and bound to each request. + let isOutdatedRequest = false; + + const createRequest = async (isInitialRequest = true) => { + // Set a neutral state for a non-request. + if (!path) { + setError(null); + setData(initialData); + setIsLoading(false); + return; + } + + setError(null); + + // Only set loading state to true and initial data on the first request + if (isInitialRequest) { + setIsLoading(true); + setData(initialData); + } + + const { data: responseData, error: responseError } = await sendRequest({ + path, + method, + body, + }); + + // Don't update state if an outdated request has resolved. + if (isOutdatedRequest) { + return; + } + + setError(responseError); + setData(processData && responseData ? processData(responseData) : responseData); + setIsLoading(false); + }; + + useEffect( + () => { + function cancelOutdatedRequest() { + isOutdatedRequest = true; + } + + createRequest(); + + if (interval) { + const intervalRequest = setInterval(createRequest.bind(null, false), interval); + + return () => { + cancelOutdatedRequest(); + clearInterval(intervalRequest); + }; + } + + // Called when a new render will trigger this effect. + return cancelOutdatedRequest; + }, + [path] + ); + + return { + error, + isLoading, + data, + createRequest, + }; +}; diff --git a/x-pack/legacy/plugins/watcher/public/models/action/action.js b/x-pack/legacy/plugins/watcher/public/models/action/action.js index f3cdfeef7fab7..2f1850c3a434c 100644 --- a/x-pack/legacy/plugins/watcher/public/models/action/action.js +++ b/x-pack/legacy/plugins/watcher/public/models/action/action.js @@ -5,15 +5,14 @@ */ import { get, set } from 'lodash'; -import { ACTION_TYPES } from 'plugins/watcher/../common/constants'; +import { ACTION_TYPES } from '../../../common/constants'; import { EmailAction } from './email_action'; import { LoggingAction } from './logging_action'; import { SlackAction } from './slack_action'; -import { WebhookAction } from './webhook.action'; -import { IndexAction } from './index.action'; -import { HipchatAction } from './hipchat.action'; -import { PagerDutyAction } from './pagerduty.action'; -import { JiraAction } from './jira.action'; +import { WebhookAction } from './webhook_action'; +import { IndexAction } from './index_action'; +import { PagerDutyAction } from './pagerduty_action'; +import { JiraAction } from './jira_action'; import { UnknownAction } from './unknown_action'; const ActionTypes = {}; @@ -22,7 +21,6 @@ set(ActionTypes, ACTION_TYPES.LOGGING, LoggingAction); set(ActionTypes, ACTION_TYPES.SLACK, SlackAction); set(ActionTypes, ACTION_TYPES.WEBHOOK, WebhookAction); set(ActionTypes, ACTION_TYPES.INDEX, IndexAction); -set(ActionTypes, ACTION_TYPES.HIPCHAT, HipchatAction); set(ActionTypes, ACTION_TYPES.PAGERDUTY, PagerDutyAction); set(ActionTypes, ACTION_TYPES.JIRA, JiraAction); diff --git a/x-pack/legacy/plugins/watcher/public/models/action/base_action.js b/x-pack/legacy/plugins/watcher/public/models/action/base_action.js index 50d37dcde2edc..3b358b9560376 100644 --- a/x-pack/legacy/plugins/watcher/public/models/action/base_action.js +++ b/x-pack/legacy/plugins/watcher/public/models/action/base_action.js @@ -11,6 +11,7 @@ export class BaseAction { constructor(props = {}) { this.id = get(props, 'id'); this.type = get(props, 'type'); + this.isNew = get(props, 'isNew', false); } get upstreamJson() { @@ -47,15 +48,10 @@ export class BaseAction { return this.constructor.simulatePrompt; } - get template() { - return this.constructor.template; - } - static typeName = i18n.translate('xpack.watcher.models.baseAction.typeName', { defaultMessage: 'Action', }); - static iconClass = 'fa-cog'; - static template = ''; + static iconClass = 'apps'; static selectMessage = i18n.translate('xpack.watcher.models.baseAction.selectMessageText', { defaultMessage: 'Perform an action.', }); diff --git a/x-pack/legacy/plugins/watcher/public/models/action/email_action.js b/x-pack/legacy/plugins/watcher/public/models/action/email_action.js index 31fe6a9405e0b..0ca70e1ed3415 100644 --- a/x-pack/legacy/plugins/watcher/public/models/action/email_action.js +++ b/x-pack/legacy/plugins/watcher/public/models/action/email_action.js @@ -7,15 +7,52 @@ import { get, isArray } from 'lodash'; import { BaseAction } from './base_action'; import { i18n } from '@kbn/i18n'; +import chrome from 'ui/chrome'; export class EmailAction extends BaseAction { constructor(props = {}) { super(props); - const toArray = get(props, 'to', []); - this.to = isArray(toArray) ? toArray : [ toArray ]; - this.subject = get(props, 'subject', ''); - this.body = get(props, 'body', ''); + const uiSettings = chrome.getUiSettingsClient(); + const defaultToEmail = uiSettings.get('xPack:defaultAdminEmail'); + const toArray = get(props, 'to', defaultToEmail); + + this.to = isArray(toArray) ? toArray : toArray && [ toArray ]; + + const defaultSubject = i18n.translate('xpack.watcher.models.emailAction.defaultSubjectText', { + defaultMessage: 'Watch [{context}] has exceeded the threshold', + values: { + context: '{{ctx.metadata.name}}', + } + }); + + this.subject = get(props, 'subject', props.ignoreDefaults ? null : defaultSubject); + + this.body = get(props, 'body'); + } + + validate() { + const errors = { + to: [], + body: [], + }; + + if (!this.to || !this.to.length) { + errors.to.push( + i18n.translate('xpack.watcher.watchActions.email.emailRecipientIsRequiredValidationMessage', { + defaultMessage: '"To" email address is required.', + }) + ); + } + if (!this.body) { + errors.body.push( + i18n.translate('xpack.watcher.watchActions.email.emailBodyIsRequiredValidationMessage', { + defaultMessage: 'Email body is required.', + }) + ); + } + + return errors; } get upstreamJson() { @@ -26,29 +63,21 @@ export class EmailAction extends BaseAction { subject: this.subject, body: this.body, email: { - to: this.to.length ? this.to : undefined, + to: this.to && this.to.length > 0 ? this.to : undefined, + subject: this.subject, + body: { + text: this.body, + }, } }); return result; } - get description() { - const toList = this.to.join(', '); - const subject = this.subject || ''; - return i18n.translate('xpack.watcher.models.emailAction.description', { - defaultMessage: '"{subject}" will be sent to {toList}', - values: { - subject, - toList - } - }); - } - get simulateMessage() { const toList = this.to.join(', '); return i18n.translate('xpack.watcher.models.emailAction.simulateMessage', { - defaultMessage: 'Sample e-mail sent to {toList}', + defaultMessage: 'Sample email sent to {toList}', values: { toList } @@ -58,7 +87,7 @@ export class EmailAction extends BaseAction { get simulateFailMessage() { const toList = this.to.join(', '); return i18n.translate('xpack.watcher.models.emailAction.simulateFailMessage', { - defaultMessage: 'Failed to send e-mail to {toList}.', + defaultMessage: 'Failed to send email to {toList}.', values: { toList } @@ -70,14 +99,13 @@ export class EmailAction extends BaseAction { } static typeName = i18n.translate('xpack.watcher.models.emailAction.typeName', { - defaultMessage: 'E-mail', + defaultMessage: 'Email', }); - static iconClass = 'kuiIcon fa-envelope-o'; - static template = ''; + static iconClass = 'email'; static selectMessage = i18n.translate('xpack.watcher.models.emailAction.selectMessageText', { - defaultMessage: 'Send out an e-mail from your server.', + defaultMessage: 'Send an email from your server.', }); static simulatePrompt = i18n.translate('xpack.watcher.models.emailAction.simulateButtonLabel', { - defaultMessage: 'Test fire an e-mail now' + defaultMessage: 'Send test email' }); } diff --git a/x-pack/legacy/plugins/watcher/public/models/action/hipchat.action.js b/x-pack/legacy/plugins/watcher/public/models/action/hipchat.action.js deleted file mode 100644 index bf4eb0d039342..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/models/action/hipchat.action.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - -import { get } from 'lodash'; -import { BaseAction } from './base_action'; -import { i18n } from '@kbn/i18n'; - -const requiredFields = ['message']; -const optionalFields = ['account', 'proxy']; - -const allFields = [...requiredFields, ...optionalFields]; - -export class HipchatAction extends BaseAction { - constructor(props = {}) { - super(props); - - this.fields = {}; - allFields.forEach((field) => { - this.fields[field] = get(props, field); - }); - } - - get upstreamJson() { - // Add all required fields to the request body - let result = requiredFields.reduce((acc, field) => { - acc[field] = this.fields[field]; - return acc; - }, super.upstreamJson); - - // If optional fields have been set, add them to the body - result = optionalFields.reduce((acc, field) => { - if (this[field]) { - acc[field] = this.fields[field]; - } - return acc; - }, result); - - return result; - } - - get description() { - return i18n.translate('xpack.watcher.models.hipchatAction.description', { - defaultMessage: '{body} will be sent through Hipchat', - values: { - body: this.fields.message && this.fields.message.body || '' - } - }); - } - - get simulateMessage() { - return i18n.translate('xpack.watcher.models.hipchatAction.simulateMessage', { - defaultMessage: 'Hipchat message has been sent.', - }); - } - - get simulateFailMessage() { - return i18n.translate('xpack.watcher.models.hipchatAction.simulateFailMessage', { - defaultMessage: 'Failed to send Hipchat message.', - }); - } - - static fromUpstreamJson(upstreamAction) { - return new HipchatAction(upstreamAction); - } -} diff --git a/x-pack/legacy/plugins/watcher/public/models/action/index.action.js b/x-pack/legacy/plugins/watcher/public/models/action/index.action.js deleted file mode 100644 index b4a311501df8c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/models/action/index.action.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - -import { get } from 'lodash'; -import { BaseAction } from './base_action'; -import { i18n } from '@kbn/i18n'; - -const requiredFields = ['host', 'port']; -const optionalFields = [ - 'scheme', - 'path', - 'method', - 'headers', - 'params', - 'auth', - 'body', - 'proxy', - 'connection_timeout', - 'read_timeout', - 'url' -]; - -const allFields = [...requiredFields, ...optionalFields]; - -export class IndexAction extends BaseAction { - constructor(props = {}) { - super(props); - - this.fields = {}; - allFields.forEach((field) => { - this.fields[field] = get(props, field); - }); - } - - get upstreamJson() { - // Add all required fields to the request body - let result = requiredFields.reduce((acc, field) => { - acc[field] = this.fields[field]; - return acc; - }, super.upstreamJson); - - // If optional fields have been set, add them to the body - result = optionalFields.reduce((acc, field) => { - if (this[field]) { - acc[field] = this.fields[field]; - } - return acc; - }, result); - - return result; - } - - get description() { - return i18n.translate('xpack.watcher.models.indexAction.description', { - defaultMessage: 'The {index} will be indexed as {docType}', - values: { - index: this.fields.index, - docType: this.fields.doc_type, - } - }); - } - - get simulateMessage() { - return i18n.translate('xpack.watcher.models.indexAction.simulateMessage', { - defaultMessage: 'Index {index} has been indexed.', - values: { - index: this.index, - } - }); - } - - get simulateFailMessage() { - return i18n.translate('xpack.watcher.models.indexAction.simulateFailMessage', { - defaultMessage: 'Failed to index {index}.', - values: { - index: this.index - } - }); - } - - static fromUpstreamJson(upstreamAction) { - return new IndexAction(upstreamAction); - } -} diff --git a/x-pack/legacy/plugins/watcher/public/models/action/index_action.js b/x-pack/legacy/plugins/watcher/public/models/action/index_action.js new file mode 100644 index 0000000000000..7276ef59a3fc3 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/models/action/index_action.js @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import { get } from 'lodash'; +import { BaseAction } from './base_action'; +import { i18n } from '@kbn/i18n'; + +export class IndexAction extends BaseAction { + constructor(props = {}) { + super(props); + + this.index = get(props, 'index'); + } + + validate() { + const errors = { + index: [], + }; + if (!this.index) { + errors.index.push( + i18n.translate('xpack.watcher.watchActions.index.indexIsRequiredValidationMessage', { + defaultMessage: 'Index name is required.', + }) + ); + } + return errors; + } + + + get upstreamJson() { + const result = super.upstreamJson; + + Object.assign(result, { + index: this.index, + index: { + index: this.index, + } + }); + + return result; + } + + get simulateMessage() { + const index = this.index || ''; + return i18n.translate('xpack.watcher.models.indexAction.simulateMessage', { + defaultMessage: 'Index {index} has been indexed.', + values: { + index, + } + }); + } + + get simulateFailMessage() { + const index = this.index || ''; + return i18n.translate('xpack.watcher.models.indexAction.simulateFailMessage', { + defaultMessage: 'Failed to index {index}.', + values: { + index, + } + }); + } + + static fromUpstreamJson(upstreamAction) { + return new IndexAction(upstreamAction); + } + + static typeName = i18n.translate('xpack.watcher.models.indexAction.typeName', { + defaultMessage: 'Index', + }); + static iconClass = 'indexOpen'; + static selectMessage = i18n.translate('xpack.watcher.models.indexAction.selectMessageText', { + defaultMessage: 'Index data into Elasticsearch.', + }); + static simulatePrompt = i18n.translate('xpack.watcher.models.indexAction.simulateButtonLabel', { + defaultMessage: 'Index data', + }); +} diff --git a/x-pack/legacy/plugins/watcher/public/models/action/jira.action.js b/x-pack/legacy/plugins/watcher/public/models/action/jira.action.js deleted file mode 100644 index e5b8c1a2ab7d9..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/models/action/jira.action.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - -import { get } from 'lodash'; -import { BaseAction } from './base_action'; -import { i18n } from '@kbn/i18n'; - -const requiredFields = ['fields']; -const optionalFields = ['account', 'proxy']; - -const allFields = [...requiredFields, ...optionalFields]; - -export class JiraAction extends BaseAction { - constructor(props = {}) { - super(props); - - this.fields = {}; - allFields.forEach((field) => { - this.fields[field] = get(props, field); - }); - } - - get upstreamJson() { - // Add all required fields to the request body - let result = requiredFields.reduce((acc, field) => { - acc[field] = this.fields[field]; - return acc; - }, super.upstreamJson); - - // If optional fields have been set, add them to the body - result = optionalFields.reduce((acc, field) => { - if (this[field]) { - acc[field] = this.fields[field]; - } - return acc; - }, result); - - return result; - } - - get description() { - return i18n.translate('xpack.watcher.models.jiraAction.description', { - defaultMessage: '{issueName} will be created in Jira', - values: { - issueName: get(this.fields, 'fields.issue.issuetype.name', ''), - } - }); - } - - get simulateMessage() { - return i18n.translate('xpack.watcher.models.jiraAction.simulateMessage', { - defaultMessage: 'Jira issue has been created.', - }); - } - - get simulateFailMessage() { - return i18n.translate('xpack.watcher.models.jiraAction.simulateFailMessage', { - defaultMessage: 'Failed to create Jira issue.', - }); - } - - static fromUpstreamJson(upstreamAction) { - return new JiraAction(upstreamAction); - } -} - diff --git a/x-pack/legacy/plugins/watcher/public/models/action/jira_action.js b/x-pack/legacy/plugins/watcher/public/models/action/jira_action.js new file mode 100644 index 0000000000000..7e3839ea93b97 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/models/action/jira_action.js @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { get } from 'lodash'; +import { BaseAction } from './base_action'; +import { i18n } from '@kbn/i18n'; + +export class JiraAction extends BaseAction { + constructor(props = {}) { + super(props); + + const defaultSummary = i18n.translate('xpack.watcher.models.jiraAction.defaultSummaryText', { + defaultMessage: 'Watch [{context}] has exceeded the threshold', + values: { + context: '{{ctx.metadata.name}}', + } + }); + + this.summary = get(props, 'summary', props.ignoreDefaults ? null : defaultSummary); + this.projectKey = get(props, 'projectKey'); + this.issueType = get(props, 'issueType'); + } + + validate() { + const errors = { + projectKey: [], + issueType: [], + summary: [], + }; + if (!this.projectKey) { + errors.projectKey.push( + i18n.translate('xpack.watcher.watchActions.jira.projectKeyIsRequiredValidationMessage', { + defaultMessage: 'Jira project key is required.', + }) + ); + } + if (!this.issueType) { + errors.issueType.push( + i18n.translate('xpack.watcher.watchActions.jira.issueTypeNameIsRequiredValidationMessage', { + defaultMessage: 'Jira issue type is required.', + }) + ); + } + if (!this.summary) { + errors.summary.push( + i18n.translate('xpack.watcher.watchActions.jira.summaryIsRequiredValidationMessage', { + defaultMessage: 'Jira summary is required.', + }) + ); + } + return errors; + } + + get upstreamJson() { + const result = super.upstreamJson; + + Object.assign(result, { + projectKey: this.projectKey, + issueType: this.issueType, + summary: this.summary, + jira: { + fields: { + project: { + key: this.projectKey, + }, + issuetype: { + name: this.issueType, + }, + summary: this.summary, + }, + } + }); + + return result; + } + + get simulateMessage() { + return i18n.translate('xpack.watcher.models.jiraAction.simulateMessage', { + defaultMessage: 'Jira issue has been created.', + }); + } + + get simulateFailMessage() { + return i18n.translate('xpack.watcher.models.jiraAction.simulateFailMessage', { + defaultMessage: 'Failed to create Jira issue.', + }); + } + + static fromUpstreamJson(upstreamAction) { + return new JiraAction(upstreamAction); + } + + static typeName = i18n.translate('xpack.watcher.models.jiraAction.typeName', { + defaultMessage: 'Jira', + }); + static iconClass = 'apps'; + static selectMessage = i18n.translate('xpack.watcher.models.jiraAction.selectMessageText', { + defaultMessage: 'Create an issue in Atlassian’s Jira Software.', + }); + static simulatePrompt = i18n.translate('xpack.watcher.models.jiraAction.simulateButtonLabel', { + defaultMessage: 'Create a sample Jira issue' + }); +} diff --git a/x-pack/legacy/plugins/watcher/public/models/action/logging_action.js b/x-pack/legacy/plugins/watcher/public/models/action/logging_action.js index 603784053e717..e11ebedc874c0 100644 --- a/x-pack/legacy/plugins/watcher/public/models/action/logging_action.js +++ b/x-pack/legacy/plugins/watcher/public/models/action/logging_action.js @@ -12,7 +12,27 @@ export class LoggingAction extends BaseAction { constructor(props = {}) { super(props); - this.text = get(props, 'text', ''); + const defaultText = i18n.translate('xpack.watcher.models.loggingAction.defaultText', { + defaultMessage: 'Watch [{context}] has exceeded the threshold', + values: { + context: '{{ctx.metadata.name}}', + } + }); + this.text = get(props, 'text', props.ignoreDefaults ? null : defaultText); + } + + validate() { + const errors = { + text: [], + }; + if (!this.text) { + errors.text.push( + i18n.translate('xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage', { + defaultMessage: 'Log text is required.', + }) + ); + } + return errors; } get upstreamJson() { @@ -29,16 +49,6 @@ export class LoggingAction extends BaseAction { return result; } - get description() { - const text = this.text || ''; - return i18n.translate('xpack.watcher.models.loggingAction.description', { - defaultMessage: 'Log message \'{text}\'', - values: { - text - } - }); - } - get simulateMessage() { return i18n.translate('xpack.watcher.models.loggingAction.simulateMessage', { defaultMessage: 'Sample message logged', @@ -58,12 +68,11 @@ export class LoggingAction extends BaseAction { static typeName = i18n.translate('xpack.watcher.models.loggingAction.typeName', { defaultMessage: 'Logging', }); - static iconClass = 'kuiIcon fa-file-text-o'; + static iconClass = 'loggingApp'; static selectMessage = i18n.translate('xpack.watcher.models.loggingAction.selectMessageText', { - defaultMessage: 'Add a new item to the logs.', + defaultMessage: 'Add an item to the logs.', }); - static template = ''; static simulatePrompt = i18n.translate('xpack.watcher.models.loggingAction.simulateButtonLabel', { - defaultMessage: 'Log a sample message now', + defaultMessage: 'Log a sample message', }); } diff --git a/x-pack/legacy/plugins/watcher/public/models/action/pagerduty.action.js b/x-pack/legacy/plugins/watcher/public/models/action/pagerduty.action.js deleted file mode 100644 index d8a497cb3236f..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/models/action/pagerduty.action.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - -import { get } from 'lodash'; -import { BaseAction } from './base_action'; -import { i18n } from '@kbn/i18n'; - -const requiredFields = ['description', 'type']; -const optionalFields = [ - 'event_type', - 'incident_key', - 'client', - 'client_url', - 'attach_payload', - 'contexts', - 'proxy', - 'href', - 'src', -]; - -const allFields = [...requiredFields, ...optionalFields]; - -export class PagerDutyAction extends BaseAction { - constructor(props = {}) { - super(props); - - this.fields = {}; - allFields.forEach((field) => { - this.fields[field] = get(props, field); - }); - } - - get upstreamJson() { - // Add all required fields to the request body - let result = requiredFields.reduce((acc, field) => { - acc[field] = this.fields[field]; - return acc; - }, super.upstreamJson); - - // If optional fields have been set, add them to the body - result = optionalFields.reduce((acc, field) => { - if (this.fields[field]) { - acc[field] = this.fields[field]; - } - return acc; - }, result); - - return result; - } - - get description() { - return i18n.translate('xpack.watcher.models.pagerDutyAction.description', { - defaultMessage: '{description} will be sent to PagerDuty', - values: { - description: this.fields.description, - } - }); - } - - get simulateMessage() { - return i18n.translate('xpack.watcher.models.pagerDutyAction.simulateMessage', { - defaultMessage: 'PagerDuty event has been sent.', - }); - } - - get simulateFailMessage() { - return i18n.translate('xpack.watcher.models.pagerDutyAction.simulateFailMessage', { - defaultMessage: 'Failed to send Hipchat event.', - }); - } - - static fromUpstreamJson(upstreamAction) { - return new PagerDutyAction(upstreamAction); - } -} - diff --git a/x-pack/legacy/plugins/watcher/public/models/action/pagerduty_action.js b/x-pack/legacy/plugins/watcher/public/models/action/pagerduty_action.js new file mode 100644 index 0000000000000..e825cf1f262fc --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/models/action/pagerduty_action.js @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import { get } from 'lodash'; +import { BaseAction } from './base_action'; +import { i18n } from '@kbn/i18n'; + +export class PagerDutyAction extends BaseAction { + constructor(props = {}) { + super(props); + + const defaultDescription = i18n.translate('xpack.watcher.models.pagerdutyAction.defaultDescriptionText', { + defaultMessage: 'Watch [{context}] has exceeded the threshold', + values: { + context: '{{ctx.metadata.name}}', + } + }); + this.description = get(props, 'description', props.ignoreDefaults ? null : defaultDescription); + } + + validate() { + const errors = { + description: [], + }; + + if (!this.description) { + errors.description.push( + i18n.translate('xpack.watcher.watchActions.pagerduty.descriptionIsRequiredValidationMessage', { + defaultMessage: 'PagerDuty description is required.', + }) + ); + } + return errors; + } + + get upstreamJson() { + const result = super.upstreamJson; + + Object.assign(result, { + description: this.description, + pagerduty: { + description: this.description, + } + }); + + return result; + } + + get simulateMessage() { + return i18n.translate('xpack.watcher.models.pagerDutyAction.simulateMessage', { + defaultMessage: 'PagerDuty event has been sent.', + }); + } + + get simulateFailMessage() { + return i18n.translate('xpack.watcher.models.pagerDutyAction.simulateFailMessage', { + defaultMessage: 'Failed to send PagerDuty event.', + }); + } + + static fromUpstreamJson(upstreamAction) { + return new PagerDutyAction(upstreamAction); + } + + static typeName = i18n.translate('xpack.watcher.models.pagerDutyAction.typeName', { + defaultMessage: 'PagerDuty', + }); + static iconClass = 'apps'; + static selectMessage = i18n.translate('xpack.watcher.models.pagerDutyAction.selectMessageText', { + defaultMessage: 'Create an event in PagerDuty.', + }); + static simulatePrompt = i18n.translate('xpack.watcher.models.pagerDutyAction.simulateButtonLabel', { + defaultMessage: 'Send a PagerDuty event' + }); +} + diff --git a/x-pack/legacy/plugins/watcher/public/models/action/slack_action.js b/x-pack/legacy/plugins/watcher/public/models/action/slack_action.js index f94bc2f06b19d..da0f3cf5fb0c7 100644 --- a/x-pack/legacy/plugins/watcher/public/models/action/slack_action.js +++ b/x-pack/legacy/plugins/watcher/public/models/action/slack_action.js @@ -12,71 +12,63 @@ export class SlackAction extends BaseAction { constructor(props = {}) { super(props); - const toArray = get(props, 'to', []); - this.to = isArray(toArray) ? toArray : [ toArray ]; - this.text = props.text; + const toArray = get(props, 'to'); + this.to = isArray(toArray) ? toArray : toArray && [ toArray ]; + + const defaultText = i18n.translate('xpack.watcher.models.slackAction.defaultText', { + defaultMessage: 'Watch [{context}] has exceeded the threshold', + values: { + context: '{{ctx.metadata.name}}', + } + }); + this.text = get(props, 'text', props.ignoreDefaults ? null : defaultText); } validate() { - const errors = []; - - if (!this.to.length) { - errors.push({ - message: i18n.translate('xpack.watcher.sections.watchEdit.json.warningPossibleInvalidSlackAction.description', { - // eslint-disable-next-line max-len - defaultMessage: 'This watch has a Slack action without a "to" property. This watch will only be valid if you specified the "to" property in the Slack "message_default" setting in Elasticsearch.' - }) - }); - } - - return { errors: errors.length ? errors : null }; + // Currently no validation required + const errors = { + to: [], + text: [], + }; + return errors; } get upstreamJson() { const result = super.upstreamJson; - const message = this.text || this.to.length + const to = this.to && this.to.length > 0 ? this.to : undefined; + const message = this.text || to ? { text: this.text, - to: this.to.length ? this.to : undefined + to, } - : undefined; + : {}; Object.assign(result, { - to: this.to, + to, text: this.text, slack: { message - } + }, }); return result; } - get description() { - const toList = this.to.join(', '); - return i18n.translate('xpack.watcher.models.slackAction.description', { - defaultMessage: 'Slack message will be sent to {toList}', - values: { - toList - } - }); - } - get simulateMessage() { - const toList = this.to.join(', '); + const toList = this.to && this.to.join(', '); return i18n.translate('xpack.watcher.models.slackAction.simulateMessage', { - defaultMessage: 'Sample Slack message sent to {toList}.', + defaultMessage: 'Sample Slack message sent {toList}.', values: { - toList + toList: toList ? `to ${toList}` : '', } }); } get simulateFailMessage() { - const toList = this.to.join(', '); + const toList = this.to && this.to.join(', '); return i18n.translate('xpack.watcher.models.slackAction.simulateFailMessage', { - defaultMessage: 'Failed to send sample Slack message to {toList}.', + defaultMessage: 'Failed to send sample Slack message {toList}.', values: { - toList + toList: toList ? `to ${toList}` : '', } }); } @@ -88,12 +80,11 @@ export class SlackAction extends BaseAction { static typeName = i18n.translate('xpack.watcher.models.slackAction.TypeName', { defaultMessage: 'Slack' }); - static iconClass = 'kuiIcon fa-slack'; - static template = ''; + static iconClass = 'logoSlack'; static selectMessage = i18n.translate('xpack.watcher.models.slackAction.selectMessageText', { - defaultMessage: 'Send a message to a slack user or channel.' + defaultMessage: 'Send a message to a Slack user or channel.' }); static simulatePrompt = i18n.translate('xpack.watcher.models.slackAction.simulateButtonLabel', { - defaultMessage: 'Send a sample message now' + defaultMessage: 'Send a sample message' }); } diff --git a/x-pack/legacy/plugins/watcher/public/models/action/webhook.action.js b/x-pack/legacy/plugins/watcher/public/models/action/webhook.action.js deleted file mode 100644 index 8c530c9913538..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/models/action/webhook.action.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 { get } from 'lodash'; -import { BaseAction } from './base_action'; -import { i18n } from '@kbn/i18n'; - -const requiredFields = ['host', 'port']; -const optionalFields = [ - 'scheme', - 'path', - 'method', - 'headers', - 'params', - 'auth', - 'body', - 'proxy', - 'connection_timeout', - 'read_timeout', - 'url' -]; - -const allFields = [...requiredFields, ...optionalFields]; - -export class WebhookAction extends BaseAction { - constructor(props = {}) { - super(props); - - this.fields = {}; - allFields.forEach((field) => { - this.fields[field] = get(props, field); - }); - - const { url, host, port, path } = this.fields; - this.fullPath = url ? url : host + port + path; - } - - get upstreamJson() { - // Add all required fields to the request body - let result = requiredFields.reduce((acc, field) => { - acc[field] = this.fields[field]; - return acc; - }, super.upstreamJson); - - // If optional fields have been set, add them to the body - result = optionalFields.reduce((acc, field) => { - if (this[field]) { - acc[field] = this.fields[field]; - } - return acc; - }, result); - - return result; - } - - get description() { - return i18n.translate('xpack.watcher.models.webhookAction.description', { - defaultMessage: 'Webhook will trigger a {method} request on {fullPath}', - values: { - method: this.method, - fullPath: this.fullPath - } - }); - } - - get simulateMessage() { - return i18n.translate('xpack.watcher.models.webhookAction.simulateMessage', { - defaultMessage: 'Sample request sent to {fullPath}', - values: { - fullPath: this.fullPath - } - }); - } - - get simulateFailMessage() { - return i18n.translate('xpack.watcher.models.webhookAction.simulateFailMessage', { - defaultMessage: 'Failed to send request to {fullPath}.', - values: { - fullPath: this.fullPath - } - }); - } - - static fromUpstreamJson(upstreamAction) { - return new WebhookAction(upstreamAction); - } -} diff --git a/x-pack/legacy/plugins/watcher/public/models/action/webhook_action.js b/x-pack/legacy/plugins/watcher/public/models/action/webhook_action.js new file mode 100644 index 0000000000000..00be91aae4617 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/models/action/webhook_action.js @@ -0,0 +1,145 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import { get } from 'lodash'; +import { BaseAction } from './base_action'; +import { i18n } from '@kbn/i18n'; + +export class WebhookAction extends BaseAction { + constructor(props = {}) { + super(props); + + const defaultJson = JSON.stringify({ message: 'Watch [{{ctx.metadata.name}}] has exceeded the threshold' }, null, 2); + this.body = get(props, 'body', props.ignoreDefaults ? null : defaultJson); + + this.method = get(props, 'method'); + this.host = get(props, 'host'); + this.port = get(props, 'port'); + this.path = get(props, 'path'); + this.username = get(props, 'username'); + this.password = get(props, 'password'); + this.contentType = get(props, 'contentType'); + + this.fullPath = `${this.host}:${this.port}${this.path}`; + } + + validate() { + const errors = { + host: [], + port: [], + body: [], + username: [], + password: [], + }; + + if (!this.host) { + errors.host.push( + i18n.translate('xpack.watcher.watchActions.webhook.hostIsRequiredValidationMessage', { + defaultMessage: 'Webhook host is required.', + }) + ); + } + + if (!this.port) { + errors.port.push( + i18n.translate('xpack.watcher.watchActions.webhook.portIsRequiredValidationMessage', { + defaultMessage: 'Webhook port is required.', + }) + ); + } + + if (this.contentType === 'application/json' && typeof this.body === 'string' && this.body !== '') { + const invalidJsonMessage = i18n.translate('xpack.watcher.watchActions.webhook.invalidJsonValidationMessage', { + defaultMessage: 'Invalid JSON', + }); + + try { + const parsedJson = JSON.parse(this.body); + + if (parsedJson && typeof parsedJson !== 'object') { + errors.body.push(invalidJsonMessage); + } + + } catch (e) { + errors.body.push(invalidJsonMessage); + } + } + + // Password is required if username specified + if (this.username && !this.password) { + errors.password.push( + i18n.translate('xpack.watcher.watchActions.webhook.passwordIsRequiredIfUsernameValidationMessage', { + defaultMessage: 'Password is required.', + }) + ); + } + + // Username is required if password is specified + if (this.password && !this.username) { + errors.username.push( + i18n.translate('xpack.watcher.watchActions.webhook.usernameIsRequiredIfPasswordValidationMessage', { + defaultMessage: 'Username is required.', + }) + ); + } + + return errors; + } + + get upstreamJson() { + const result = super.upstreamJson; + + Object.assign(result, { + method: this.method, + host: this.host, + port: this.port, + path: this.path, + body: this.body, + username: this.username, + password: this.password, + webhook: { + host: this.host, + port: this.port, + } + }); + + return result; + } + + get simulateMessage() { + return i18n.translate('xpack.watcher.models.webhookAction.simulateMessage', { + defaultMessage: 'Sample request sent to {fullPath}', + values: { + fullPath: this.fullPath + } + }); + } + + get simulateFailMessage() { + return i18n.translate('xpack.watcher.models.webhookAction.simulateFailMessage', { + defaultMessage: 'Failed to send request to {fullPath}.', + values: { + fullPath: this.fullPath + } + }); + } + + static fromUpstreamJson(upstreamAction) { + return new WebhookAction(upstreamAction); + } + + static typeName = i18n.translate('xpack.watcher.models.webhookAction.typeName', { + defaultMessage: 'Webhook', + }); + static iconClass = 'logoWebhook'; + static selectMessage = i18n.translate('xpack.watcher.models.webhookAction.selectMessageText', { + defaultMessage: 'Send a request to a web service.', + }); + static simulatePrompt = i18n.translate('xpack.watcher.models.webhookAction.simulateButtonLabel', { + defaultMessage: 'Send request', + }); +} diff --git a/x-pack/legacy/plugins/watcher/public/models/action_status/action_status.js b/x-pack/legacy/plugins/watcher/public/models/action_status/action_status.js index b24893645e975..fa9e056554ab0 100644 --- a/x-pack/legacy/plugins/watcher/public/models/action_status/action_status.js +++ b/x-pack/legacy/plugins/watcher/public/models/action_status/action_status.js @@ -5,7 +5,7 @@ */ import { get } from 'lodash'; -import { getMoment } from 'plugins/watcher/../common/lib/get_moment'; +import { getMoment } from '../../../common/lib/get_moment'; export class ActionStatus { constructor(props = {}) { diff --git a/x-pack/legacy/plugins/watcher/public/models/execute_details/execute_details.js b/x-pack/legacy/plugins/watcher/public/models/execute_details/execute_details.js index c6c27d18b77e2..e41345daa182a 100644 --- a/x-pack/legacy/plugins/watcher/public/models/execute_details/execute_details.js +++ b/x-pack/legacy/plugins/watcher/public/models/execute_details/execute_details.js @@ -4,28 +4,64 @@ * you may not use this file except in compliance with the Elastic License. */ +import { i18n } from '@kbn/i18n'; +import { getDateMathFormat } from '../../lib/format_date'; + export class ExecuteDetails { constructor(props = {}) { - this.triggeredTime = props.triggeredTime; - this.scheduledTime = props.scheduledTime; + this.triggeredTimeValue = props.triggeredTimeValue; + this.triggeredTimeUnit = props.triggeredTimeUnit; + this.scheduledTimeValue = props.scheduledTimeValue; + this.scheduledTimeUnit = props.scheduledTimeUnit; this.ignoreCondition = props.ignoreCondition; - this.alternativeInput = props.alternativeInput; + this.alternativeInput = props.alternativeInput || ''; this.actionModes = props.actionModes; this.recordExecution = props.recordExecution; } - get upstreamJson() { - const triggerData = { - triggeredTime: this.triggeredTime, - scheduledTime: this.scheduledTime, + validate() { + const errors = { + json: [], }; + const invalidJsonFieldError = i18n.translate( + 'xpack.watcher.sections.watchEdit.simulate.form.invalidJsonFieldError', + { + defaultMessage: 'Invalid JSON', + } + ); + + if (this.alternativeInput !== '') { + try { + const parsedJson = JSON.parse(this.alternativeInput); + + if (parsedJson && typeof parsedJson !== 'object') { + errors.json.push(invalidJsonFieldError); + } + } catch (e) { + errors.json.push(invalidJsonFieldError); + } + } + return errors; + } + + get upstreamJson() { + const triggeredTime = this.triggeredTimeValue && this.triggeredTimeValue !== '' + ? getDateMathFormat(this.triggeredTimeUnit, this.triggeredTimeValue) + : undefined; + const scheduledTime = this.scheduledTimeValue && this.scheduledTimeValue !== '' + ? getDateMathFormat(this.scheduledTimeUnit, this.scheduledTimeValue) + : undefined; + return { - triggerData: triggerData, + triggerData: { + triggeredTime, + scheduledTime, + }, ignoreCondition: this.ignoreCondition, - alternativeInput: this.alternativeInput, + alternativeInput: this.alternativeInput !== '' ? JSON.parse(this.alternativeInput) : undefined, actionModes: this.actionModes, - recordExecution: this.recordExecution + recordExecution: this.recordExecution, }; } } diff --git a/x-pack/legacy/plugins/watcher/public/models/index.d.ts b/x-pack/legacy/plugins/watcher/public/models/index.d.ts new file mode 100644 index 0000000000000..d96d8d192e166 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/models/index.d.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +declare module 'plugins/watcher/models/visualize_options' { + export const VisualizeOptions: any; +} + +declare module 'plugins/watcher/models/watch' { + export const Watch: any; +} + +declare module 'plugins/watcher/models/watch/threshold_watch' { + export const ThresholdWatch: any; +} + +declare module 'plugins/watcher/models/watch/json_watch' { + export const JsonWatch: any; +} + +declare module 'plugins/watcher/models/execute_details/execute_details' { + export const ExecuteDetails: any; +} + +declare module 'plugins/watcher/models/watch_history_item' { + export const WatchHistoryItem: any; +} + +declare module 'plugins/watcher/models/watch_status' { + export const WatchStatus: any; +} + +declare module 'plugins/watcher/models/settings' { + export const Settings: any; +} +declare module 'plugins/watcher/models/action' { + export const Action: any; +} +declare module 'ui/time_buckets' { + export const TimeBuckets: any; +} diff --git a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/agg_types.js b/x-pack/legacy/plugins/watcher/public/models/watch/agg_types.ts similarity index 53% rename from x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/agg_types.js rename to x-pack/legacy/plugins/watcher/public/models/watch/agg_types.ts index e26f6f57c42ff..65ab537889ea4 100644 --- a/x-pack/legacy/plugins/watcher/public/components/threshold_watch_expression/lib/agg_types.js +++ b/x-pack/legacy/plugins/watcher/public/models/watch/agg_types.ts @@ -4,36 +4,43 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AGG_TYPES } from 'plugins/watcher/../common/constants'; +import { AGG_TYPES } from '../../../common/constants'; -export const aggTypes = { +export interface AggType { + text: string; + fieldRequired: boolean; + value: string; + validNormalizedTypes: string[]; +} +export const aggTypes: { [key: string]: AggType } = { count: { - label: 'count()', + text: 'count()', fieldRequired: false, - value: AGG_TYPES.COUNT + value: AGG_TYPES.COUNT, + validNormalizedTypes: [], }, - average: { - label: 'average()', + avg: { + text: 'average()', fieldRequired: true, validNormalizedTypes: ['number'], - value: AGG_TYPES.AVERAGE + value: AGG_TYPES.AVERAGE, }, sum: { - label: 'sum()', + text: 'sum()', fieldRequired: true, validNormalizedTypes: ['number'], - value: AGG_TYPES.SUM + value: AGG_TYPES.SUM, }, min: { - label: 'min()', + text: 'min()', fieldRequired: true, validNormalizedTypes: ['number', 'date'], - value: AGG_TYPES.MIN + value: AGG_TYPES.MIN, }, max: { - label: 'max()', + text: 'max()', fieldRequired: true, validNormalizedTypes: ['number', 'date'], - value: AGG_TYPES.MAX - } + value: AGG_TYPES.MAX, + }, }; diff --git a/x-pack/legacy/plugins/watcher/public/models/watch/base_watch.js b/x-pack/legacy/plugins/watcher/public/models/watch/base_watch.js index 8363ebaf05cc3..cf68776081c18 100644 --- a/x-pack/legacy/plugins/watcher/public/models/watch/base_watch.js +++ b/x-pack/legacy/plugins/watcher/public/models/watch/base_watch.js @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getSearchValue } from 'plugins/watcher/lib/get_search_value'; -import { get, isEqual, remove, map, merge } from 'lodash'; +import { getSearchValue } from '../../lib/get_search_value'; +import { get, isEqual, remove, map } from 'lodash'; import { Action } from '../action'; import { WatchStatus } from '../watch_status'; import { WatchErrors } from '../watch_errors'; @@ -28,8 +28,7 @@ export class BaseWatch { this.id = get(props, 'id'); this.type = get(props, 'type'); this.isNew = get(props, 'isNew', true); - - this.name = get(props, 'name', ''); + this.name = get(props, 'name'); this.isSystemWatch = Boolean(get(props, 'isSystemWatch')); this.watchStatus = WatchStatus.fromUpstreamJson(get(props, 'watchStatus')); this.watchErrors = WatchErrors.fromUpstreamJson(get(props, 'watchErrors')); @@ -56,11 +55,7 @@ export class BaseWatch { } const id = createActionId(this.actions, type); - const props = merge( - {}, - defaults, - { id, type } - ); + const props = { id, type, ...defaults }; const action = new ActionType(props); this.addAction(action); @@ -119,6 +114,7 @@ export class BaseWatch { id: this.id, name: this.name, type: this.type, + isNew: this.isNew, actions: map(this.actions, action => action.upstreamJson) }; } @@ -136,44 +132,6 @@ export class BaseWatch { return isEqual(cleanWatch, cleanOtherWatch); } - /** - * Client validation of the Watch. - * Currently we are *only* validating the Watch "Actions" - */ - validate() { - - // Get the errors from each watch action - const actionsErrors = this.actions.reduce((actionsErrors, action) => { - if (action.validate) { - const { errors } = action.validate(); - if (!errors) { - return actionsErrors; - } - return [...actionsErrors, ...errors]; - } - return actionsErrors; - }, []); - - if (!actionsErrors.length) { - return { warning: null }; - } - - // Concatenate their message - const warningMessage = actionsErrors.reduce((message, error) => ( - !!message - ? `${message}, ${error.message}` - : error.message - ), ''); - - // We are not doing any *blocking* validation in the client, - // so we return the errors as a _warning_ - return { - warning: { - message: warningMessage, - } - }; - } - static typeName = i18n.translate('xpack.watcher.models.baseWatch.typeName', { defaultMessage: 'Watch', }); diff --git a/x-pack/legacy/plugins/watcher/public/models/watch/comparators.ts b/x-pack/legacy/plugins/watcher/public/models/watch/comparators.ts new file mode 100644 index 0000000000000..b636cdaf14c18 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/models/watch/comparators.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { COMPARATORS } from '../../../common/constants'; + +export interface Comparator { + text: string; + value: string; + requiredValues: number; +} +export const comparators: { [key: string]: Comparator } = { + [COMPARATORS.GREATER_THAN]: { + text: i18n.translate('xpack.watcher.thresholdWatchExpression.comparators.isAboveLabel', { + defaultMessage: 'Is above', + }), + value: COMPARATORS.GREATER_THAN, + requiredValues: 1, + }, + [COMPARATORS.GREATER_THAN_OR_EQUALS]: { + text: i18n.translate( + 'xpack.watcher.thresholdWatchExpression.comparators.isAboveOrEqualsLabel', + { + defaultMessage: 'Is above or equals', + } + ), + value: COMPARATORS.GREATER_THAN_OR_EQUALS, + requiredValues: 1, + }, + [COMPARATORS.LESS_THAN]: { + text: i18n.translate('xpack.watcher.thresholdWatchExpression.comparators.isBelowLabel', { + defaultMessage: 'Is below', + }), + value: COMPARATORS.LESS_THAN, + requiredValues: 1, + }, + [COMPARATORS.LESS_THAN_OR_EQUALS]: { + text: i18n.translate( + 'xpack.watcher.thresholdWatchExpression.comparators.isBelowOrEqualsLabel', + { + defaultMessage: 'Is below or equals', + } + ), + value: COMPARATORS.LESS_THAN_OR_EQUALS, + requiredValues: 1, + }, + [COMPARATORS.BETWEEN]: { + text: i18n.translate('xpack.watcher.thresholdWatchExpression.comparators.isBetweenLabel', { + defaultMessage: 'Is between', + }), + value: COMPARATORS.BETWEEN, + requiredValues: 2, + }, +}; diff --git a/x-pack/legacy/plugins/watcher/public/models/watch/default_watch.json b/x-pack/legacy/plugins/watcher/public/models/watch/default_watch.json index 555f45ef16237..22c78660a0bb0 100644 --- a/x-pack/legacy/plugins/watcher/public/models/watch/default_watch.json +++ b/x-pack/legacy/plugins/watcher/public/models/watch/default_watch.json @@ -30,4 +30,4 @@ } } } -} \ No newline at end of file +} diff --git a/x-pack/legacy/plugins/watcher/public/models/watch/group_by_types.ts b/x-pack/legacy/plugins/watcher/public/models/watch/group_by_types.ts new file mode 100644 index 0000000000000..37dee2f9946e3 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/models/watch/group_by_types.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +export interface GroupByType { + text: string; + sizeRequired: boolean; + value: string; + validNormalizedTypes: string[]; +} +export const groupByTypes: { [key: string]: GroupByType } = { + all: { + text: i18n.translate('xpack.watcher.thresholdWatchExpression.groupByLabel.allDocumentsLabel', { + defaultMessage: 'all documents', + }), + sizeRequired: false, + value: 'all', + validNormalizedTypes: [], + }, + top: { + text: i18n.translate('xpack.watcher.thresholdWatchExpression.groupByLabel.topLabel', { + defaultMessage: 'top', + }), + sizeRequired: true, + value: 'top', + validNormalizedTypes: ['number', 'date', 'keyword'], + }, +}; diff --git a/x-pack/legacy/plugins/watcher/public/models/watch/json_watch.js b/x-pack/legacy/plugins/watcher/public/models/watch/json_watch.js index 586efc5ad7f78..3dd7af759970e 100644 --- a/x-pack/legacy/plugins/watcher/public/models/watch/json_watch.js +++ b/x-pack/legacy/plugins/watcher/public/models/watch/json_watch.js @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import uuid from 'uuid'; import { get } from 'lodash'; import { BaseWatch } from './base_watch'; -import { WATCH_TYPES } from 'plugins/watcher/../common/constants'; +import { ACTION_TYPES, WATCH_TYPES } from '../../../common/constants'; import defaultWatchJson from './default_watch.json'; import { i18n } from '@kbn/i18n'; @@ -16,9 +17,73 @@ import { i18n } from '@kbn/i18n'; export class JsonWatch extends BaseWatch { constructor(props = {}) { props.type = WATCH_TYPES.JSON; + props.id = typeof props.id === 'undefined' ? uuid.v4() : props.id; super(props); + const existingWatch = get(props, 'watch'); + this.watch = existingWatch ? existingWatch : defaultWatchJson; + this.watchString = get(props, 'watchString', JSON.stringify(existingWatch ? existingWatch : defaultWatchJson, null, 2)); + this.id = props.id; + } - this.watch = get(props, 'watch', defaultWatchJson); + validate() { + const validationResult = {}; + const idRegex = /^[A-Za-z0-9\-\_]+$/; + const errors = { + id: [], + json: [], + }; + validationResult.errors = errors; + // Watch id validation + if (!this.id) { + errors.id.push( + i18n.translate('xpack.watcher.sections.watchEdit.json.error.requiredIdText', { + defaultMessage: 'ID is required', + }) + ); + } else if (!idRegex.test(this.id)) { + errors.id.push(i18n.translate('xpack.watcher.sections.watchEdit.json.error.invalidIdText', { + defaultMessage: 'ID can only contain letters, underscores, dashes, and numbers.', + })); + } + // JSON validation + if (!this.watchString || this.watchString === '') { + errors.json.push(i18n.translate('xpack.watcher.sections.watchEdit.json.error.requiredJsonText', { + defaultMessage: 'JSON is required', + })); + } else { + try { + const parsedJson = JSON.parse(this.watchString); + if (parsedJson && typeof parsedJson === 'object') { + const { actions } = parsedJson; + if (actions) { + // Validate if the action(s) provided is one of the supported actions + const invalidActions = Object.keys(actions).find(actionKey => { + const actionKeys = Object.keys(actions[actionKey]); + let type; + Object.keys(ACTION_TYPES).forEach(actionTypeKey => { + if (actionKeys.includes(ACTION_TYPES[actionTypeKey]) && !actionKeys.includes(ACTION_TYPES.UNKNOWN)) { + type = ACTION_TYPES[actionTypeKey]; + } + }); + return !type; + }); + if (invalidActions) { + errors.json.push(i18n.translate('xpack.watcher.sections.watchEdit.json.error.invalidActionType', { + defaultMessage: 'Unknown action type provided for action "{action}".', + values: { + action: invalidActions, + }, + })); + } + } + } + } catch (e) { + errors.json.push(i18n.translate('xpack.watcher.sections.watchEdit.json.error.invalidJsonText', { + defaultMessage: 'Invalid JSON', + })); + } + } + return validationResult; } get upstreamJson() { @@ -26,7 +91,6 @@ export class JsonWatch extends BaseWatch { Object.assign(result, { watch: this.watch }); - return result; } diff --git a/x-pack/legacy/plugins/watcher/public/models/watch/monitoring_watch.js b/x-pack/legacy/plugins/watcher/public/models/watch/monitoring_watch.js index 94aec4fb54bed..a0873934e1759 100644 --- a/x-pack/legacy/plugins/watcher/public/models/watch/monitoring_watch.js +++ b/x-pack/legacy/plugins/watcher/public/models/watch/monitoring_watch.js @@ -5,7 +5,7 @@ */ import { BaseWatch } from './base_watch'; -import { WATCH_TYPES } from 'plugins/watcher/../common/constants'; +import { WATCH_TYPES } from '../../../common/constants'; /** * {@code MonitoringWatch} system defined watches created by the Monitoring plugin. diff --git a/x-pack/legacy/plugins/watcher/public/models/watch/threshold_watch.js b/x-pack/legacy/plugins/watcher/public/models/watch/threshold_watch.js index 0643a74ea35ed..5f46f1400c57a 100644 --- a/x-pack/legacy/plugins/watcher/public/models/watch/threshold_watch.js +++ b/x-pack/legacy/plugins/watcher/public/models/watch/threshold_watch.js @@ -6,10 +6,13 @@ import { BaseWatch } from './base_watch'; import uuid from 'uuid'; -import { WATCH_TYPES, SORT_ORDERS, COMPARATORS } from 'plugins/watcher/../common/constants'; -import { getTimeUnitsLabel } from 'plugins/watcher/lib/get_time_units_label'; +import { WATCH_TYPES, SORT_ORDERS, COMPARATORS } from '../../../common/constants'; +import { getTimeUnitLabel } from '../../lib/get_time_unit_label'; import { i18n } from '@kbn/i18n'; - +import { aggTypes } from './agg_types'; +import { groupByTypes } from './group_by_types'; +import { comparators } from './comparators'; +const { BETWEEN } = COMPARATORS; const DEFAULT_VALUES = { AGG_TYPE: 'count', TERM_SIZE: 5, @@ -18,7 +21,8 @@ const DEFAULT_VALUES = { TIME_WINDOW_UNIT: 'm', TRIGGER_INTERVAL_SIZE: 1, TRIGGER_INTERVAL_UNIT: 'm', - THRESHOLD: 1000 + THRESHOLD: [1000, 5000], + GROUP_BY: 'all', }; /** @@ -32,20 +36,29 @@ export class ThresholdWatch extends BaseWatch { this.index = props.index; this.timeField = props.timeField; - this.triggerIntervalSize = props.triggerIntervalSize || DEFAULT_VALUES.TRIGGER_INTERVAL_SIZE; + this.timeFields = props.timeFields || undefined; + this.triggerIntervalSize = + props.triggerIntervalSize == null + ? DEFAULT_VALUES.TRIGGER_INTERVAL_SIZE + : props.triggerIntervalSize; this.triggerIntervalUnit = props.triggerIntervalUnit || DEFAULT_VALUES.TRIGGER_INTERVAL_UNIT; this.aggType = props.aggType || DEFAULT_VALUES.AGG_TYPE; this.aggField = props.aggField; - this.termSize = props.termSize || DEFAULT_VALUES.TERM_SIZE; + this.termSize = props.termSize == null ? DEFAULT_VALUES.TERM_SIZE : props.termSize; this.termField = props.termField; this.thresholdComparator = props.thresholdComparator || DEFAULT_VALUES.THRESHOLD_COMPARATOR; - this.timeWindowSize = props.timeWindowSize || DEFAULT_VALUES.TIME_WINDOW_SIZE; + this.timeWindowSize = + props.timeWindowSize == null ? DEFAULT_VALUES.TIME_WINDOW_SIZE : props.timeWindowSize; this.timeWindowUnit = props.timeWindowUnit || DEFAULT_VALUES.TIME_WINDOW_UNIT; + this.groupBy = props.groupBy || DEFAULT_VALUES.GROUP_BY; + if (this.termField != null) { + this.groupBy = 'top'; + } //NOTE: The threshold must be of the appropriate type, i.e.,number/date. //Conversion from string must occur by consumer when assigning a //value to this property. - this.threshold = props.threshold || DEFAULT_VALUES.THRESHOLD; + this.threshold = props.threshold == null ? DEFAULT_VALUES.THRESHOLD : props.threshold; } get hasTermsAgg() { @@ -53,28 +66,178 @@ export class ThresholdWatch extends BaseWatch { } get termOrder() { - return this.thresholdComparator === COMPARATORS.GREATER_THAN ? SORT_ORDERS.DESCENDING : SORT_ORDERS.ASCENDING; + return this.thresholdComparator === COMPARATORS.GREATER_THAN + ? SORT_ORDERS.DESCENDING + : SORT_ORDERS.ASCENDING; } get titleDescription() { - const staticPart = i18n.translate('xpack.watcher.models.thresholdWatch.sendAlertOnSpecificConditionTitleDescription', { - defaultMessage: 'Send an alert when a specific condition is met.' - }); + const staticPart = i18n.translate( + 'xpack.watcher.models.thresholdWatch.sendAlertOnSpecificConditionTitleDescription', + { + defaultMessage: 'Send an alert when your specified condition is met.', + } + ); if (isNaN(this.triggerIntervalSize)) { return staticPart; } - const timeUnitLabel = getTimeUnitsLabel(this.triggerIntervalUnit, this.triggerIntervalSize); - const dynamicPartText = i18n.translate('xpack.watcher.models.thresholdWatch.thresholdWatchIntervalTitleDescription', { - defaultMessage: 'This will run every {triggerIntervalSize} {timeUnitLabel}.', - values: { - triggerIntervalSize: this.triggerIntervalSize, - timeUnitLabel + const timeUnitLabel = getTimeUnitLabel(this.triggerIntervalUnit, this.triggerIntervalSize); + const dynamicPartText = i18n.translate( + 'xpack.watcher.models.thresholdWatch.thresholdWatchIntervalTitleDescription', + { + defaultMessage: 'Your watch will run every {triggerIntervalSize} {timeUnitLabel}.', + values: { + triggerIntervalSize: this.triggerIntervalSize, + timeUnitLabel, + }, } - }); + ); return `${staticPart} ${dynamicPartText}`; } + validate() { + const validationResult = {}; + const errors = { + name: [], + index: [], + timeField: [], + triggerIntervalSize: [], + aggField: [], + termSize: [], + termField: [], + timeWindowSize: [], + }; + validationResult.errors = errors; + if (!this.name) { + errors.name.push( + i18n.translate('xpack.watcher.sections.watchEdit.threshold.error.requiredNameText', { + defaultMessage: 'Name is required.', + }) + ); + } + if (this.index !== undefined && this.index.length < 1) { + errors.index.push( + i18n.translate( + 'xpack.watcher.sections.watchEdit.threshold.enterOneOrMoreIndicesValidationMessage', + { + defaultMessage: 'Enter one or more indices.', + } + ) + ); + } + if (this.index !== undefined + && this.index.length > 0 + && this.timeFields !== undefined + && this.timeFields.length === 1 // first option will have empty value + ) { + errors.index.push( + i18n.translate( + 'xpack.watcher.sections.watchEdit.threshold.invalidIndexValidationMessage', + { + defaultMessage: 'Index does not have a time field.', + } + ) + ); + } + if (!this.timeField) { + errors.timeField.push( + i18n.translate( + 'xpack.watcher.sections.watchEdit.threshold.timeFieldIsRequiredValidationText', + { + defaultMessage: 'A time field is required.', + } + ) + ); + } + if (!this.triggerIntervalSize) { + errors.triggerIntervalSize.push( + i18n.translate( + 'xpack.watcher.sections.watchEdit.threshold.intervalSizeIsRequiredValidationMessage', + { + defaultMessage: 'Interval size is required.', + } + ) + ); + } + if (this.triggerIntervalSize !== undefined && this.triggerIntervalSize < 0) { + errors.triggerIntervalSize.push( + i18n.translate( + 'xpack.watcher.sections.watchEdit.threshold.intervalSizeNegativeNumberValidationMessage', + { + defaultMessage: 'Interval size cannot be a negative number.', + } + ) + ); + } + if (aggTypes[this.aggType].fieldRequired && !this.aggField) { + errors.aggField.push( + i18n.translate( + 'xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage', + { + defaultMessage: 'This field is required.', + } + ) + ); + } + // term field and term size only required if the group by type requires them + if (groupByTypes[this.groupBy].sizeRequired) { + if (!this.termSize) { + errors.termSize.push( + i18n.translate( + 'xpack.watcher.thresholdWatchExpression.aggType.xpack.watcher.thresholdWatchExpression.groupBy.requiredValueValidationMessage', + { + defaultMessage: 'A value is required.', + } + ) + ); + } + if (!this.termField) { + errors.termField.push( + i18n.translate( + 'xpack.watcher.thresholdWatchExpression.groupBy.requiredFieldValidationMessage', + { + defaultMessage: 'This field is required.', + } + ) + ); + } + } + Array.from(Array(comparators[this.thresholdComparator].requiredValues)).forEach((value, i) => { + const key = `threshold${i}`; + errors[key] = []; + if (!this.threshold[i]) { + errors[key].push(i18n.translate( + 'xpack.watcher.thresholdWatchExpression.thresholdLevel.valueIsRequiredValidationMessage', + { + defaultMessage: 'A value is required.', + } + )); + } + }); + if (this.thresholdComparator === BETWEEN && this.threshold[0] && this.threshold[1] && !(this.threshold[1] > this.threshold[0])) { + errors.threshold1.push(i18n.translate( + 'xpack.watcher.thresholdWatchExpression.thresholdLevel.secondValueMustBeGreaterMessage', + { + defaultMessage: 'Value must be greater than {lowerBound}.', + values: { + lowerBound: this.threshold[0] + } + } + )); + } + if (!this.timeWindowSize) { + errors.timeWindowSize.push( + i18n.translate( + 'xpack.watcher.thresholdWatchExpression.timeWindow.durationSizeIsRequiredValidationMessage', + { + defaultMessage: 'Window duration size is required.', + } + ) + ); + } + return validationResult; + } get upstreamJson() { const result = super.upstreamJson; Object.assign(result, { @@ -89,7 +252,7 @@ export class ThresholdWatch extends BaseWatch { thresholdComparator: this.thresholdComparator, timeWindowSize: this.timeWindowSize, timeWindowUnit: this.timeWindowUnit, - threshold: this.threshold + threshold: comparators[this.thresholdComparator].requiredValues > 1 ? this.threshold : this.threshold[0], }); return result; @@ -104,11 +267,11 @@ export class ThresholdWatch extends BaseWatch { } static typeName = i18n.translate('xpack.watcher.models.thresholdWatch.typeName', { - defaultMessage: 'Threshold Alert' + defaultMessage: 'Threshold Alert', }); static iconClass = ''; static selectMessage = i18n.translate('xpack.watcher.models.thresholdWatch.selectMessageText', { - defaultMessage: 'Send an alert on a specific condition' + defaultMessage: 'Send an alert on a specific condition', }); static isCreatable = true; static selectSortOrder = 1; diff --git a/x-pack/legacy/plugins/watcher/public/models/watch/watch.js b/x-pack/legacy/plugins/watcher/public/models/watch/watch.js index 864ba0a61c819..d58a7799c6516 100644 --- a/x-pack/legacy/plugins/watcher/public/models/watch/watch.js +++ b/x-pack/legacy/plugins/watcher/public/models/watch/watch.js @@ -5,7 +5,7 @@ */ import { get, set } from 'lodash'; -import { WATCH_TYPES } from 'plugins/watcher/../common/constants'; +import { WATCH_TYPES } from '../../../common/constants'; import { JsonWatch } from './json_watch'; import { ThresholdWatch } from './threshold_watch'; import { MonitoringWatch } from './monitoring_watch'; @@ -21,7 +21,7 @@ export class Watch { return WatchTypes; } - static fromUpstreamJson(upstreamWatch) { + static fromUpstreamJson(upstreamWatch = {}) { const type = get(upstreamWatch, 'type'); const WatchType = WatchTypes[type]; diff --git a/x-pack/legacy/plugins/watcher/public/models/watch_history_item/watch_history_item.js b/x-pack/legacy/plugins/watcher/public/models/watch_history_item/watch_history_item.js index f6229eb5b4238..a5918cec2764b 100644 --- a/x-pack/legacy/plugins/watcher/public/models/watch_history_item/watch_history_item.js +++ b/x-pack/legacy/plugins/watcher/public/models/watch_history_item/watch_history_item.js @@ -6,7 +6,7 @@ import 'moment-duration-format'; import { get } from 'lodash'; -import { getMoment } from 'plugins/watcher/../common/lib/get_moment'; +import { getMoment } from '../../../common/lib/get_moment'; import { WatchStatus } from '../watch_status'; export class WatchHistoryItem { diff --git a/x-pack/legacy/plugins/watcher/public/models/watch_status/watch_status.js b/x-pack/legacy/plugins/watcher/public/models/watch_status/watch_status.js index 59034ab9624a8..f213032a93c27 100644 --- a/x-pack/legacy/plugins/watcher/public/models/watch_status/watch_status.js +++ b/x-pack/legacy/plugins/watcher/public/models/watch_status/watch_status.js @@ -5,7 +5,7 @@ */ import { get } from 'lodash'; -import { getMoment } from 'plugins/watcher/../common/lib/get_moment'; +import { getMoment } from '../../../common/lib/get_moment'; import { ActionStatus } from '../action_status'; export class WatchStatus { @@ -16,7 +16,7 @@ export class WatchStatus { this.isActive = get(props, 'isActive'); this.lastFired = getMoment(get(props, 'lastFired')); this.lastChecked = getMoment(get(props, 'lastChecked')); - this.lastMetCondition = getMoment(this.lastMetCondition); + this.lastMetCondition = getMoment(get(props, 'lastMetCondition')); if (this.lastFired) { this.lastFiredHumanized = this.lastFired.fromNow(); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/register_management_section.js b/x-pack/legacy/plugins/watcher/public/register_management_sections.js similarity index 73% rename from x-pack/legacy/plugins/watcher/public/sections/watch_list/register_management_section.js rename to x-pack/legacy/plugins/watcher/public/register_management_sections.js index c7b6937485705..886ac7d28db64 100644 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/register_management_section.js +++ b/x-pack/legacy/plugins/watcher/public/register_management_sections.js @@ -4,54 +4,57 @@ * you may not use this file except in compliance with the Elastic License. */ + import { management } from 'ui/management'; import { i18n } from '@kbn/i18n'; -import 'plugins/watcher/services/license'; management.getSection('elasticsearch').register('watcher', { display: i18n.translate('xpack.watcher.sections.watchList.managementSection.watcherDisplayName', { - defaultMessage: 'Watcher' } - ), + defaultMessage: 'Watcher', + }), order: 6, - url: '#/management/elasticsearch/watcher/' + url: '#/management/elasticsearch/watcher/', }); management.getSection('elasticsearch/watcher').register('watches', { display: i18n.translate('xpack.watcher.sections.watchList.managementSection.watchesDisplayName', { - defaultMessage: 'Watches' } - ), - order: 1 + defaultMessage: 'Watches', + }), + order: 1, }); management.getSection('elasticsearch/watcher').register('watch', { - visible: false + visible: false, }); management.getSection('elasticsearch/watcher/watch').register('status', { display: i18n.translate('xpack.watcher.sections.watchList.managementSection.statusDisplayName', { - defaultMessage: 'Status' } - ), + defaultMessage: 'Status', + }), order: 1, - visible: false + visible: false, }); management.getSection('elasticsearch/watcher/watch').register('edit', { display: i18n.translate('xpack.watcher.sections.watchList.managementSection.editDisplayName', { - defaultMessage: 'Edit' } - ), + defaultMessage: 'Edit', + }), order: 2, - visible: false + visible: false, }); management.getSection('elasticsearch/watcher/watch').register('new', { - display: i18n.translate('xpack.watcher.sections.watchList.managementSection.newWatchDisplayName', { - defaultMessage: 'New Watch' } + display: i18n.translate( + 'xpack.watcher.sections.watchList.managementSection.newWatchDisplayName', + { + defaultMessage: 'New Watch', + } ), order: 1, - visible: false + visible: false, }); management.getSection('elasticsearch/watcher/watch').register('history-item', { order: 1, - visible: false + visible: false, }); diff --git a/x-pack/legacy/plugins/watcher/public/register_route.js b/x-pack/legacy/plugins/watcher/public/register_route.js new file mode 100644 index 0000000000000..c58be17bc6e75 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/register_route.js @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { SavedObjectsClientProvider } from 'ui/saved_objects'; +import routes from 'ui/routes'; +import { management } from 'ui/management'; +import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; +import template from './app.html'; +import { App } from './app'; +import { setHttpClient, setSavedObjectsClient } from './lib/api'; +import { I18nContext } from 'ui/i18n'; +import { manageAngularLifecycle } from './lib/manage_angular_lifecycle'; +import { PLUGIN } from '../common/constants'; +import { LICENSE_STATUS_UNAVAILABLE, LICENSE_STATUS_INVALID } from '../../../common/constants'; + +let elem; +const renderReact = async (elem, licenseStatus) => { + render( + + + , + elem + ); +}; +routes.when('/management/elasticsearch/watcher/:param1?/:param2?/:param3?/:param4?', { + template, + controller: class WatcherController { + constructor($injector, $scope, $http, Private) { + const $route = $injector.get('$route'); + const licenseStatus = xpackInfo.get(`features.${PLUGIN.ID}`); + + // clean up previously rendered React app if one exists + // this happens because of React Router redirects + elem && unmountComponentAtNode(elem); + setSavedObjectsClient(Private(SavedObjectsClientProvider)); + // NOTE: We depend upon Angular's $http service because it's decorated with interceptors, + // e.g. to check license status per request. + setHttpClient($http); + $scope.$$postDigest(() => { + elem = document.getElementById('watchReactRoot'); + renderReact(elem, licenseStatus); + manageAngularLifecycle($scope, $route, elem); + }); + } + }, + controllerAs: 'watchRoute', +}); + +routes.defaults(/\/management/, { + resolve: { + watcherManagementSection: () => { + const watchesSection = management.getSection('elasticsearch/watcher'); + const licenseStatus = xpackInfo.get(`features.${PLUGIN.ID}`); + const { status } = licenseStatus; + + if (status === LICENSE_STATUS_INVALID || status === LICENSE_STATUS_UNAVAILABLE) { + return watchesSection.hide(); + } + + watchesSection.show(); + + }, + }, +}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/action_status_table.html b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/action_status_table.html deleted file mode 100644 index 3cf5b7c5e6304..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/action_status_table.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - -
- - {{ 'xpack.watcher.sections.watchDetail.actionStatusTable.actionColumnLabel' | i18n: { defaultMessage: 'Action' } }} - - - - {{ 'xpack.watcher.sections.watchDetail.actionStatusTable.stateColumnLabel' | i18n: { defaultMessage: 'State' } }} - - - Errors - -
-
- {{ actionStatus.id }} -
-
-
- - {{ actionStatus.state }} -
-
- - -
-
- -
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/action_status_table.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/action_status_table.js deleted file mode 100644 index aed49c8c3b043..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/action_status_table.js +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { i18n } from '@kbn/i18n'; -import template from './action_status_table.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('actionStatusTable', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - actionStatuses: '=', - actionErrors: '=', - sortField: '=', - sortReverse: '=', - onSortChange: '=', - onActionAcknowledge: '=', - showErrors: '=' - }, - bindToController: true, - controllerAs: 'actionStatusTable', - controller: class ActionStatusTableController { - getLabelErrors(actionId) { - const errors = this.actionErrors[actionId]; - const total = errors.length; - - const label = i18n.translate('xpack.watcher.sections.watchDetail.actionStatusTotalErrors', { - defaultMessage: '{total, number} {total, plural, one {error} other {errors}}', - values: { - total, - } - }); - - return label; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/index.js deleted file mode 100644 index 93e1cd9ad3ade..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/action_status_table/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './action_status_table'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/index.js deleted file mode 100644 index 0a4b1629c610a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_detail'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/watch_detail.html b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/watch_detail.html deleted file mode 100644 index fa46a0b12097a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/watch_detail.html +++ /dev/null @@ -1,89 +0,0 @@ - -
- -
-
- - -
-

-
- -
-
- - - -
-
-
-
- - - -
- - - - {{ 'xpack.watcher.sections.watchDetail.noActionsFoundText' | i18n: { defaultMessage: 'No actions found.' } }} - -
- - -
-

-
- -
- -
- - -
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/watch_detail.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/watch_detail.js deleted file mode 100644 index f806a51eed2bd..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_detail/watch_detail.js +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { i18n } from '@kbn/i18n'; -import { InitAfterBindingsWorkaround } from 'ui/compat'; -import moment from 'moment-timezone'; -import { toastNotifications } from 'ui/notify'; -import 'ui/config'; -import 'ui/url'; -import 'ui/table_info'; -import 'plugins/watcher/components/tool_bar_selected_count'; -import 'plugins/watcher/services/watch'; -import 'plugins/watcher/services/license'; -import 'plugins/watcher/components/errors_display_modal'; - -import template from './watch_detail.html'; -import errorsDisplayTemplate from 'plugins/watcher/components/errors_display_modal/errors_display_modal.html'; -import '../watch_history'; -import '../action_status_table'; -import { REFRESH_INTERVALS } from 'plugins/watcher/../common/constants'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchDetail', function ($injector) { - const watchService = $injector.get('xpackWatcherWatchService'); - const licenseService = $injector.get('xpackWatcherLicenseService'); - - const config = $injector.get('config'); - const kbnUrl = $injector.get('kbnUrl'); - const confirmModal = $injector.get('confirmModal'); - const $interval = $injector.get('$interval'); - - const $filter = $injector.get('$filter'); - const orderBy = $filter('orderBy'); - const $modal = $injector.get('$modal'); - - moment.tz.setDefault(config.get('dateFormat:tz')); - - return { - restrict: 'E', - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - initialHistoryRange: '=', - watchHistoryItems: '=' - }, - bindToController: true, - controllerAs: 'watchDetail', - controller: class WatchDetailController extends InitAfterBindingsWorkaround { - initAfterBindings($scope) { - // history settings - this.isHistoryLoading = false; - this.historyRange = this.initialHistoryRange; - - this.actionStatusTableSortField = 'id'; - this.actionStatusTableSortReverse = false; - this.actionErrors = (this.watch.watchErrors && this.watch.watchErrors.actionErrors) || null; - - this.omitBreadcrumbPages = ['watch', this.watch.id]; - this.breadcrumb = this.watch.displayName; - - // Reload watch history periodically - const refreshInterval = $interval( - () => this.loadWatchHistory(), - REFRESH_INTERVALS.WATCH_HISTORY - ); - $scope.$on('$destroy', () => $interval.cancel(refreshInterval)); - - // react to data and UI changes - $scope.$watchMulti( - ['watchDetail.actionStatusTableSortField', 'watchDetail.actionStatusTableSortReverse'], - this.applySortToActionStatusTable - ); - } - - loadWatchHistory = () => { - return watchService - .loadWatchHistory(this.watch.id, this.historyRange) - .then(watchHistoryItems => { - this.isHistoryLoading = false; - this.watchHistoryItems = watchHistoryItems; - }) - .catch(err => { - return licenseService.checkValidity().then(() => toastNotifications.addDanger(err)); - }); - }; - - // update the watch history items when the time range changes - onHistoryRangeChange = range => { - this.historyRange = range; - this.isHistoryLoading = true; - return this.loadWatchHistory(); - }; - - /** - * Action status table methods - */ - - get hasActionStatusTableActions() { - return this.sortedActionStatuses.length > 0; - } - - onActionSortChange = (field, reverse) => { - this.actionStatusTableSortField = field; - this.actionStatusTableSortReverse = reverse; - }; - - applySortToActionStatusTable = () => { - this.sortedActionStatuses = orderBy( - this.watch.watchStatus.actionStatuses, - this.actionStatusTableSortField, - this.actionStatusTableSortReverse - ); - }; - - onActionAcknowledge = (actionStatus) => { - return watchService.acknowledgeWatchAction(this.watch.id, actionStatus.id) - .then(watchStatus => { - this.watch.updateWatchStatus(watchStatus); - this.applySortToActionStatusTable(); - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => toastNotifications.addDanger(err)); - }); - } - - showErrors = (actionId, errors) => { - const errorsModal = $modal.open({ - template: errorsDisplayTemplate, - controller: 'WatcherErrorsDisplayController', - controllerAs: 'vm', - backdrop: 'static', - keyboard: true, - ariaLabelledBy: 'watcher__error-display-modal-title', - resolve: { - params: function () { - return { - title: i18n.translate('xpack.watcher.sections.watchDetail.errorDisplayModalTitleText', { - defaultMessage: 'Errors in the "{actionId}" action', - values: { actionId } } - ), - errors, - }; - } - } - }); - - errorsModal.result.catch(() => { - // We need to add this empty Promise catch to avoid - // a console error "Possibly unhandled rejection" - }); - } - - /** - * Event handler methods - */ - - onWatchDeactivate = () => { - return watchService.deactivateWatch(this.watch.id) - .then(watchStatus => { - this.watch.updateWatchStatus(watchStatus); - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => toastNotifications.addDanger(err)); - }); - }; - - onWatchActivate = () => { - return watchService.activateWatch(this.watch.id) - .then(watchStatus => { - this.watch.updateWatchStatus(watchStatus); - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => toastNotifications.addDanger(err)); - }); - } - - onWatchDelete = () => { - const confirmModalOptions = { - confirmButtonText: i18n.translate('xpack.watcher.sections.watchDetail.deleteConfirmModal.deleteWatchButtonLabel', { - defaultMessage: 'Delete Watch' } - ), - onConfirm: this.deleteWatch - }; - - return confirmModal( - i18n.translate('xpack.watcher.sections.watchDetail.deleteConfirmModal.description', { - defaultMessage: 'This will permanently delete the watch. Are you sure?' } - ), - confirmModalOptions); - } - - deleteWatch = () => { - return watchService.deleteWatch(this.watch.id) - .then(() => { - toastNotifications.addSuccess( - i18n.translate('xpack.watcher.sections.watchDetail.deleteWatchSuccessNotificationText', { - defaultMessage: 'Deleted {watchName}', - values: { watchName: this.watch.displayName } } - ), - ); - this.close(); - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => toastNotifications.addDanger(err)); - }); - } - - close = () => { - kbnUrl.change('/management/elasticsearch/watcher/watches', {}); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/index.js deleted file mode 100644 index 422fe636f34e4..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_history'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/watch_history.html b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/watch_history.html deleted file mode 100644 index 79238eb4838bd..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/watch_history.html +++ /dev/null @@ -1,60 +0,0 @@ -
- -
-
- - -
- - -
- - -
-
- - - - {{ 'xpack.watcher.sections.watchDetail.watchHistory.watchHistoryLoadingText' | i18n: { defaultMessage: 'Loading watch history' } }}… - - - {{ 'xpack.watcher.sections.watchDetail.watchHistory.watchHistoryNotFoundText' | i18n: { defaultMessage: 'No watch history found.' } }} - - - - -
-
- - -
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/watch_history.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/watch_history.js deleted file mode 100644 index 381ee9483bb44..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history/watch_history.js +++ /dev/null @@ -1,85 +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 '../../../../directives/moment'; -import { uiModules } from 'ui/modules'; -import 'ui/pager_control'; -import 'ui/pager'; -import 'ui/table_info'; -import { InitAfterBindingsWorkaround } from 'ui/compat'; - -import { PAGINATION } from 'plugins/watcher/../common/constants'; - -import template from './watch_history.html'; -import '../watch_history_table'; -import '../watch_history_range_filter'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchHistory', function ($injector) { - const pagerFactory = $injector.get('pagerFactory'); - - const $filter = $injector.get('$filter'); - const orderBy = $filter('orderBy'); - const limitTo = $filter('limitTo'); - - return { - restrict: 'E', - replace: true, - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - historyItems: '=', - initialRange: '=', - onRangeChange: '=', - isLoading: '=', - }, - bindToController: true, - controllerAs: 'watchHistory', - controller: class WatchHistoryController extends InitAfterBindingsWorkaround { - initAfterBindings($scope) { - this.isLoading = false; - this.sortField = 'startTime'; - this.sortReverse = true; - this.pageOfHistoryItems = []; - - this.historyRange = this.initialRange; - this.pager = pagerFactory.create(this.historyItems.length, PAGINATION.PAGE_SIZE, 1); - - // react to data and UI changes - $scope.$watchMulti([ - 'watchHistory.historyItems', - 'watchHistory.sortField', - 'watchHistory.sortReverse', - 'watchHistory.pager.currentPage', - ], this.applyFilters); - } - - get hasPageOfHistoryItems() { - return this.pageOfHistoryItems.length > 0; - } - - onPageNext = () => { - this.pager.nextPage(); - }; - - onPagePrevious = () => { - this.pager.previousPage(); - }; - - onSortChange = (field, reverse) => { - this.sortField = field; - this.sortReverse = reverse; - }; - - applyFilters = () => { - const orderedHistoryItems = orderBy(this.historyItems, this.sortField, this.sortReverse); - this.pageOfHistoryItems = limitTo(orderedHistoryItems, this.pager.pageSize, this.pager.startIndex); - this.pager.setTotalItems(orderedHistoryItems.length); - }; - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/index.js deleted file mode 100644 index dd189310a75a3..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_history_range_filter'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/watch_history_range_filter.html b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/watch_history_range_filter.html deleted file mode 100644 index 4b0f4fa2dad01..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/watch_history_range_filter.html +++ /dev/null @@ -1,41 +0,0 @@ - diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/watch_history_range_filter.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/watch_history_range_filter.js deleted file mode 100644 index c9b27a0f189df..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_range_filter/watch_history_range_filter.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { InitAfterBindingsWorkaround } from 'ui/compat'; -import template from './watch_history_range_filter.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchHistoryRangeFilter', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - historyRange: '=', - onRangeChange: '=', - }, - controllerAs: 'watchHistoryRangeFilter', - bindToController: true, - controller: class WatchHistoryRangeFilterController extends InitAfterBindingsWorkaround { - initAfterBindings() { - // avoid parent state mutation, since we have no one-way binding - this.range = this.historyRange; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/index.js deleted file mode 100644 index ddbfdfd90b3ae..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_history_table'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/watch_history_table.html b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/watch_history_table.html deleted file mode 100644 index e87c1e57f11be..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/watch_history_table.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - -
- - {{ 'xpack.watcher.sections.watchDetail.watchHistoryTable.triggerTimeColumnLabel' | i18n: { defaultMessage: 'Trigger Time' } }} - - - - {{ 'xpack.watcher.sections.watchDetail.watchHistoryTable.stateColumnLabel' | i18n: { defaultMessage: 'State' } }} - - - - {{ 'xpack.watcher.sections.watchDetail.watchHistoryTable.commentColumnLabel' | i18n: { defaultMessage: 'Comment' } }} - -
- - -
- - - {{ item.historyItem.watchStatus.state }} - -
-
-
- - {{item.historyItem.watchStatus.comment}} - -
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/watch_history_table.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/watch_history_table.js deleted file mode 100644 index 53851902e7292..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/components/watch_history_table/watch_history_table.js +++ /dev/null @@ -1,53 +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 '../../../../directives/moment'; -import { uiModules } from 'ui/modules'; -import template from './watch_history_table.html'; -import 'plugins/watcher/components/watch_state_icon'; -import { find, isEmpty } from 'lodash'; -import { LISTS } from 'plugins/watcher/../common/constants'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchHistoryTable', function ($injector) { - const $timeout = $injector.get('$timeout'); - - return { - restrict: 'E', - replace: true, - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - historyItems: '=', - sortField: '=', - sortReverse: '=', - onSortChange: '=' - }, - bindToController: true, - controllerAs: 'watchHistoryTable', - controller: class WatchHistoryTableController { - constructor($scope) { - $scope.$watch('watchHistoryTable.historyItems', historyItems => { - const previousItems = this.items; - const isInitialLoad = isEmpty(previousItems); - - this.items = historyItems.map(historyItem => { - const matchedItem = find(previousItems, previousItem => previousItem.historyItem.id === historyItem.id); - const isNew = !isInitialLoad && Boolean(!matchedItem); - return { historyItem, isNew }; - }); - - $timeout(() => { - this.items.forEach(item => { - item.isNew = false; - }); - }, LISTS.NEW_ITEMS_HIGHLIGHT_DURATION); - }); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/index.js deleted file mode 100644 index ec0a7820ded81..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_detail_route'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/watch_detail_route.html b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/watch_detail_route.html deleted file mode 100644 index 0e467e412874c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/watch_detail_route.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/watch_detail_route.js b/x-pack/legacy/plugins/watcher/public/sections/watch_detail/watch_detail_route.js deleted file mode 100644 index 28830f7eb31fe..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_detail/watch_detail_route.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import routes from 'ui/routes'; -import 'ui/url'; -import { toastNotifications } from 'ui/notify'; -import template from './watch_detail_route.html'; -import 'plugins/watcher/services/watch'; -import './components/watch_detail'; -import { WATCH_HISTORY } from '../../../common/constants'; -import { updateWatchSections } from 'plugins/watcher/lib/update_management_sections'; -import 'plugins/watcher/services/license'; -import { getWatchDetailBreadcrumbs } from '../../lib/breadcrumbs'; - -routes - .when('/management/elasticsearch/watcher/watches/watch/:id', { - redirectTo: '/management/elasticsearch/watcher/watches/watch/:id/status' - }); - -routes - .when('/management/elasticsearch/watcher/watches/watch/:id/status', { - template: template, - k7Breadcrumbs: getWatchDetailBreadcrumbs, - resolve: { - watchTabs: ($injector) => { - const $route = $injector.get('$route'); - const watchId = $route.current.params.id; - updateWatchSections(watchId); - }, - initialHistoryRange: function () { - return WATCH_HISTORY.INITIAL_RANGE; - }, - watch: function ($injector) { - const $route = $injector.get('$route'); - const watchService = $injector.get('xpackWatcherWatchService'); - const kbnUrl = $injector.get('kbnUrl'); - - const watchId = $route.current.params.id; - - return watchService.loadWatch(watchId) - .catch(err => { - if (err.status !== 403) { - toastNotifications.addDanger(err.data.message); - } - - kbnUrl.redirect('/management/elasticsearch/watcher/watches'); - return Promise.reject(); - }); - }, - watchHistoryItems: function ($injector) { - const $route = $injector.get('$route'); - const watchService = $injector.get('xpackWatcherWatchService'); - const kbnUrl = $injector.get('kbnUrl'); - - const watchId = $route.current.params.id; - - return watchService.loadWatchHistory(watchId, WATCH_HISTORY.INITIAL_RANGE) - .catch(err => { - if (err.status !== 403) { - toastNotifications.addDanger(err.data.message); - } - - kbnUrl.redirect('/management/elasticsearch/watcher/watches'); - return Promise.reject(); - }); - }, - checkLicense: ($injector) => { - const licenseService = $injector.get('xpackWatcherLicenseService'); - return licenseService.checkValidity(); - } - }, - controllerAs: 'watchDetailRoute', - controller: class WatchDetailRouteController { - constructor($injector) { - const $route = $injector.get('$route'); - this.initialHistoryRange = $route.current.locals.initialHistoryRange; - this.watch = $route.current.locals.watch; - this.watchHistoryItems = $route.current.locals.watchHistoryItems; - } - } - }); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/index.js deleted file mode 100644 index 4b1b16792dfb9..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './json_watch_edit'; diff --git a/x-pack/legacy/plugins/watcher/common/constants/pagination.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/index.ts similarity index 82% rename from x-pack/legacy/plugins/watcher/common/constants/pagination.js rename to x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/index.ts index fd44ffdecfc3b..21716f9e544e7 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/pagination.js +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/index.ts @@ -4,6 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const PAGINATION = { - PAGE_SIZE: 20 -}; +export { JsonWatchEdit } from './json_watch_edit'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.html b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.html deleted file mode 100644 index 824117b3d415c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.html +++ /dev/null @@ -1,121 +0,0 @@ - -
-
-
- -
- -
- - - {{ 'xpack.watcher.sections.watchEdit.json.editTabLabel' | i18n: { defaultMessage: 'Edit' } }} - - - {{ 'xpack.watcher.sections.watchEdit.json.simulateTabLabel' | i18n: { defaultMessage: 'Simulate' } }} - - - {{ 'xpack.watcher.sections.watchEdit.json.simulateResultsTabLabel' | i18n: { defaultMessage: 'Simulation Results' } }} - - -
- -
- -
- -
-
- -
- -
- -
- -
- -
-
- -
-
- -
- -
- -
- -
- - -
-
- -
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.js deleted file mode 100644 index 6ded712512360..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.js +++ /dev/null @@ -1,289 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { i18n } from '@kbn/i18n'; -import { InitAfterBindingsWorkaround } from 'ui/compat'; -import { toastNotifications } from 'ui/notify'; -import template from './json_watch_edit.html'; -import 'plugins/watcher/components/kbn_tabs'; -import 'plugins/watcher/components/watch_history_item_detail'; -import '../watch_edit_detail'; -import '../watch_edit_title_bar'; -import '../watch_edit_execute_info_panel'; -import '../watch_edit_execute_detail'; -import '../watch_edit_actions_execute_summary'; -import '../watch_edit_watch_execute_summary'; -import 'plugins/watcher/services/license'; -import { ACTION_TYPES } from '../../../../../common/constants'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('jsonWatchEdit', function ($injector) { - const watchService = $injector.get('xpackWatcherWatchService'); - const licenseService = $injector.get('xpackWatcherLicenseService'); - const kbnUrl = $injector.get('kbnUrl'); - const confirmModal = $injector.get('confirmModal'); - // const dirtyPrompt = $injector.get('dirtyPrompt'); - - return { - restrict: 'E', - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - }, - bindToController: true, - controllerAs: 'jsonWatchEdit', - controller: class JsonWatchEditController extends InitAfterBindingsWorkaround { - initAfterBindings() { - this.selectedTabId = 'edit-watch'; - this.simulateResults = null; - this.originalWatch = { - ...this.watch - }; - - this.omitBreadcrumbPages = [ - 'new-watch', - this.watch.id - ]; - this.breadcrumb = this.watch.displayName; - - // dirtyPrompt.register(() => !this.watch.isEqualTo(this.originalWatch)); - // $scope.$on('$destroy', dirtyPrompt.deregister); - - this.onExecuteDetailsValid(); - } - - onTabSelect = (tabId) => { - this.selectedTabId = tabId; - } - - isTabSelected = (tabId) => { - return this.selectedTabId === tabId; - } - - onWatchChange = (watch) => { - this.watch = watch; - } - - onValid = () => { - this.isValid = true; - } - - onInvalid = () => { - this.isValid = false; - } - - executeDetailsChange = (executeDetails) => { - this.executeDetails = executeDetails; - } - - onExecuteDetailsValid = () => { - this.isExecuteValid = true; - } - - onExecuteDetailsInvalid = () => { - this.isExecuteValid = false; - } - - onWatchExecute = () => { - return watchService.executeWatch(this.executeDetails, this.watch) - .then((watchHistoryItem) => { - this.simulateResults = watchHistoryItem; - this.onTabSelect('simulate-results'); - }) - .catch(e => { - toastNotifications.addDanger(e); - }); - } - - onWatchSave = () => { - this.createActionsForWatch(this.watch); - - if (!this.watch.isNew) { - return this.validateAndSaveWatch(); - } - - return this.isExistingWatch() - .then(existingWatch => { - if (!existingWatch) { - return this.validateAndSaveWatch(); - } - - const confirmModalOptions = { - onConfirm: this.saveWatch, - confirmButtonText: i18n.translate('xpack.watcher.sections.watchEdit.json.saveConfirmModal.overwriteWatchButtonLabel', { - defaultMessage: 'Overwrite Watch', - }), - }; - - const message = i18n.translate('xpack.watcher.sections.watchEdit.json.saveConfirmModal.description', { - defaultMessage: 'Watch with ID "{watchId}" {watchNameMessageFragment} already exists. Do you want to overwrite it?', - values: { - watchId: this.watch.id, - watchNameMessageFragment: existingWatch.name - ? i18n.translate('xpack.watcher.sections.watchEdit.json.saveConfirmModal.descriptionFragmentText', { - defaultMessage: '(name: "{existingWatchName}")', - values: { - existingWatchName: existingWatch.name - } - }) - : '' - } - }); - return confirmModal(message, confirmModalOptions); - }) - .catch(err => toastNotifications.addDanger(err)); - } - - isExistingWatch = () => { - return watchService.loadWatch(this.watch.id) - .then(existingWatch => { - return existingWatch; - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => { - if (err.status === 404) { - return false; - } - throw err; - }); - }); - } - - validateAndSaveWatch = () => { - const { warning } = this.watch.validate(); - - if (warning) { - const confirmModalOptions = { - onConfirm: this.saveWatch, - confirmButtonText: i18n.translate('xpack.watcher.sections.watchEdit.json.watchErrorsWarning.confirmSaveWatch', { - defaultMessage: 'Save watch', - }), - }; - - return confirmModal(warning.message, confirmModalOptions); - } - - return this.saveWatch(); - } - - saveWatch = () => { - return watchService.saveWatch(this.watch) - .then(() => { - this.watch.isNew = false; // without this, the message displays 'New Watch' - toastNotifications.addSuccess( - i18n.translate('xpack.watcher.sections.watchEdit.json.saveSuccessNotificationText', { - defaultMessage: 'Saved \'{watchDisplayName}\'', - values: { - watchDisplayName: this.watch.displayName - } - }), - ); - this.onClose(); - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => toastNotifications.addDanger(err)); - }); - } - - onWatchDelete = () => { - const confirmModalOptions = { - onConfirm: this.deleteWatch, - confirmButtonText: i18n.translate('xpack.watcher.sections.watchEdit.json.deleteConfirmModal.overwriteWatchButtonLabel', { - defaultMessage: 'Delete Watch', - }), - }; - - return confirmModal( - i18n.translate('xpack.watcher.sections.watchEdit.json.deleteConfirmModal.description', { - defaultMessage: 'This will permanently delete the watch. Are you sure?', - }), - confirmModalOptions - ); - } - - deleteWatch = () => { - return watchService.deleteWatch(this.watch.id) - .then(() => { - toastNotifications.addSuccess( - i18n.translate('xpack.watcher.sections.watchEdit.json.deleteSuccessNotificationText', { - defaultMessage: 'Deleted \'{watchDisplayName}\'', - values: { - watchDisplayName: this.watch.displayName - } - }), - ); - this.onClose(); - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => toastNotifications.addDanger(err)); - }); - } - - onClose = () => { - // dirtyPrompt.deregister(); - kbnUrl.change('/management/elasticsearch/watcher/watches', {}); - } - - /** - * Actions instances are not automatically added to the Watch _actions_ Array - * when we add them in the Json editor. This method takes takes care of it. - * - * @param watchModel Watch instance - * @return Watch instance - */ - createActionsForWatch(watchInstance) { - watchInstance.resetActions(); - - let action; - let type; - let actionProps; - - Object.keys(watchInstance.watch.actions).forEach((k) => { - action = watchInstance.watch.actions[k]; - type = this.getTypeFromAction(action); - actionProps = this.getPropsFromAction(type, action); - - watchInstance.createAction(type, actionProps); - }); - - return watchInstance; - } - - /** - * Get the type from an action where a key defines its type. - * eg: { email: { ... } } | { slack: { ... } } - * - * @param action A raw action object - * @return {string} The action type - */ - getTypeFromAction(action) { - const actionKeys = Object.keys(action); - let type; - - Object.keys(ACTION_TYPES).forEach((k) => { - if (actionKeys.includes(ACTION_TYPES[k])) { - type = ACTION_TYPES[k]; - } - }); - - return type ? type : ACTION_TYPES.UNKNOWN; - } - - getPropsFromAction(type, action) { - if (type === ACTION_TYPES.SLACK) { - // Slack action has its props inside the "message" object - return action[type].message; - } - return action[type]; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.tsx new file mode 100644 index 0000000000000..9c4b16e301b38 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit.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 React, { useContext, useState } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiPageContent, + EuiSpacer, + EuiTab, + EuiTabs, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ExecuteDetails } from 'plugins/watcher/models/execute_details/execute_details'; +import { getActionType } from '../../../../../common/lib/get_action_type'; +import { BaseWatch, ExecutedWatchDetails } from '../../../../../common/types/watch_types'; +import { ACTION_MODES, TIME_UNITS } from '../../../../../common/constants'; +import { JsonWatchEditForm } from './json_watch_edit_form'; +import { JsonWatchEditSimulate } from './json_watch_edit_simulate'; +import { WatchContext } from '../../watch_context'; + +interface WatchAction { + actionId: string; + actionMode: string; + type: string; +} + +interface WatchTab { + id: string; + name: string; +} + +const WATCH_EDIT_TAB = 'watchEditTab'; +const WATCH_SIMULATE_TAB = 'watchSimulateTab'; + +const WATCH_TABS: WatchTab[] = [ + { + id: WATCH_EDIT_TAB, + name: i18n.translate('xpack.watcher.sections.watchEdit.json.editTabLabel', { + defaultMessage: 'Edit', + }), + }, + { + id: WATCH_SIMULATE_TAB, + name: i18n.translate('xpack.watcher.sections.watchEdit.json.simulateTabLabel', { + defaultMessage: 'Simulate', + }), + }, +]; + +const EXECUTE_DETAILS_INITIAL_STATE = { + triggeredTimeValue: 0, + triggeredTimeUnit: TIME_UNITS.SECOND, + scheduledTimeValue: 0, + scheduledTimeUnit: TIME_UNITS.SECOND, + ignoreCondition: false, +}; + +function getActions(watch: BaseWatch) { + const actions = (watch.watch && watch.watch.actions) || {}; + return Object.keys(actions).map(actionKey => ({ + actionId: actionKey, + type: getActionType(actions[actionKey]), + actionMode: ACTION_MODES.SIMULATE, + })); +} + +function getActionModes(items: WatchAction[]) { + const result = items.reduce((itemsAccum: any, item) => { + if (item.actionId) { + itemsAccum[item && item.actionId] = item.actionMode; + } + return itemsAccum; + }, {}); + return result; +} + +export const JsonWatchEdit = ({ pageTitle }: { pageTitle: string }) => { + const { watch } = useContext(WatchContext); + const watchActions = getActions(watch); + // hooks + const [selectedTab, setSelectedTab] = useState(WATCH_EDIT_TAB); + const [executeDetails, setExecuteDetails] = useState( + new ExecuteDetails({ + ...EXECUTE_DETAILS_INITIAL_STATE, + actionModes: getActionModes(watchActions), + }) + ); + const executeWatchErrors = executeDetails.validate(); + const hasExecuteWatchErrors = !!Object.keys(executeWatchErrors).find( + errorKey => executeWatchErrors[errorKey].length >= 1 + ); + return ( + + + + +

{pageTitle}

+
+
+
+ + {WATCH_TABS.map((tab, index) => ( + { + setSelectedTab(tab.id); + setExecuteDetails( + new ExecuteDetails({ + ...executeDetails, + actionModes: getActionModes(watchActions), + }) + ); + }} + isSelected={tab.id === selectedTab} + key={index} + data-test-subj="tab" + > + {tab.name} + + ))} + + + {selectedTab === WATCH_SIMULATE_TAB && ( + setExecuteDetails(details)} + executeWatchErrors={executeWatchErrors} + hasExecuteWatchErrors={hasExecuteWatchErrors} + watchActions={watchActions} + /> + )} + {selectedTab === WATCH_EDIT_TAB && } +
+ ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_form.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_form.tsx new file mode 100644 index 0000000000000..80d3751ae3632 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_form.tsx @@ -0,0 +1,222 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment, useContext, useState } from 'react'; + +import { + EuiButton, + EuiButtonEmpty, + EuiCodeEditor, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiLink, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ErrableFormRow, SectionError } from '../../../../components'; +import { putWatchApiUrl } from '../../../../lib/documentation_links'; +import { onWatchSave } from '../../watch_edit_actions'; +import { WatchContext } from '../../watch_context'; +import { goToWatchList } from '../../../../lib/navigation'; + +export const JsonWatchEditForm = () => { + const { watch, setWatchProperty } = useContext(WatchContext); + + const { errors } = watch.validate(); + const hasErrors = !!Object.keys(errors).find(errorKey => errors[errorKey].length >= 1); + + const [validationError, setValidationError] = useState(null); + + const [serverError, setServerError] = useState<{ + data: { nessage: string; error: string }; + } | null>(null); + + const [isSaving, setIsSaving] = useState(false); + + const hasActionErrors = !!validationError; + + const invalidActionMessage = i18n.translate( + 'xpack.watcher.sections.watchEdit.json.form.actionValidationErrorMessage', + { + defaultMessage: 'Invalid watch actions', + } + ); + + const jsonErrors = { + ...errors, + json: hasActionErrors ? [...errors.json, invalidActionMessage] : [...errors.json], + }; + + if (errors.json.length === 0) { + setWatchProperty('watch', JSON.parse(watch.watchString)); + } + + return ( + + + {serverError && ( + + + } + error={serverError} + data-test-subj="sectionError" + /> + + + )} + + ) => { + const watchName = e.target.value; + setWatchProperty('name', watchName); + }} + onBlur={() => { + if (!watch.name) { + setWatchProperty('name', ''); + } + }} + /> + + + ) => { + setWatchProperty('id', e.target.value); + }} + onBlur={() => { + if (!watch.id) { + setWatchProperty('id', ''); + } + }} + /> + + + + {i18n.translate('xpack.watcher.sections.watchEdit.json.form.watchJsonLabel', { + defaultMessage: 'Watch JSON', + })}{' '} + ( + + {i18n.translate('xpack.watcher.sections.watchEdit.json.form.watchJsonDocLink', { + defaultMessage: 'API syntax', + })} + + ) + + } + errorKey="json" + isShowingErrors={hasErrors || hasActionErrors} + fullWidth + errors={jsonErrors} + > + { + if (validationError) { + setValidationError(null); + } + setWatchProperty('watchString', json); + }} + /> + + + + { + setIsSaving(true); + const savedWatch = await onWatchSave(watch); + if (savedWatch && savedWatch.error) { + const { data } = savedWatch.error; + + setIsSaving(false); + + if (data && data.error === 'validation') { + return setValidationError(data.message); + } + + return setServerError(savedWatch.error); + } + }} + > + {watch.isNew ? ( + + ) : ( + + )} + + + + goToWatchList()}> + {i18n.translate('xpack.watcher.sections.watchEdit.json.cancelButtonLabel', { + defaultMessage: 'Cancel', + })} + + + + + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate.tsx new file mode 100644 index 0000000000000..5d16ac3aa18f0 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate.tsx @@ -0,0 +1,416 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment, useContext, useState } from 'react'; + +import { + EuiBasicTable, + EuiButton, + EuiCodeEditor, + EuiDescribedFormGroup, + EuiFieldNumber, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiLink, + EuiSelect, + EuiSpacer, + EuiSwitch, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ExecuteDetails } from 'plugins/watcher/models/execute_details/execute_details'; +import { WatchHistoryItem } from 'plugins/watcher/models/watch_history_item'; +import { ACTION_MODES, TIME_UNITS } from '../../../../../common/constants'; +import { + ExecutedWatchDetails, + ExecutedWatchResults, +} from '../../../../../common/types/watch_types'; +import { ErrableFormRow } from '../../../../components/form_errors'; +import { executeWatch } from '../../../../lib/api'; +import { executeWatchApiUrl } from '../../../../lib/documentation_links'; +import { WatchContext } from '../../watch_context'; +import { JsonWatchEditSimulateResults } from './json_watch_edit_simulate_results'; +import { getTimeUnitLabel } from '../../../../lib/get_time_unit_label'; + +const actionModeOptions = Object.keys(ACTION_MODES).map(mode => ({ + text: ACTION_MODES[mode], + value: ACTION_MODES[mode], +})); + +const getScheduleTimeOptions = (unitSize = '0') => + Object.entries(TIME_UNITS) + .filter(([key]) => key !== TIME_UNITS.DAY) + .map(([_key, value]) => { + return { + text: getTimeUnitLabel(value, unitSize), + value, + }; + }); + +export const JsonWatchEditSimulate = ({ + executeWatchErrors, + hasExecuteWatchErrors, + executeDetails, + setExecuteDetails, + watchActions, +}: { + executeWatchErrors: { [key: string]: string[] }; + hasExecuteWatchErrors: boolean; + executeDetails: ExecutedWatchDetails; + setExecuteDetails: (details: ExecutedWatchDetails) => void; + watchActions: Array<{ + actionId: string; + actionMode: string; + type: string; + }>; +}) => { + const { watch } = useContext(WatchContext); + + // hooks + const [executeResults, setExecuteResults] = useState(null); + const [isExecuting, setIsExecuting] = useState(false); + const [executeResultsError, setExecuteResultsError] = useState(null); + + const { errors: watchErrors } = watch.validate(); + const hasWatchJsonError = watchErrors.json.length >= 1; + + const { + actionModes, + scheduledTimeValue, + scheduledTimeUnit, + triggeredTimeValue, + alternativeInput, + ignoreCondition, + } = executeDetails; + + const columns = [ + { + field: 'actionId', + name: i18n.translate('xpack.watcher.sections.watchEdit.simulate.table.idColumnLabel', { + defaultMessage: 'ID', + }), + sortable: true, + truncateText: true, + }, + { + field: 'type', + name: i18n.translate('xpack.watcher.sections.watchEdit.simulate.table.typeColumnLabel', { + defaultMessage: 'Type', + }), + truncateText: true, + }, + { + field: 'actionMode', + name: i18n.translate('xpack.watcher.sections.watchEdit.simulate.table.modeColumnLabel', { + defaultMessage: 'Mode', + }), + render: ({}, row: { actionId: string }) => ( + { + setExecuteDetails( + new ExecuteDetails({ + ...executeDetails, + actionModes: { ...actionModes, [row.actionId]: e.target.value }, + }) + ); + }} + aria-label={i18n.translate( + 'xpack.watcher.sections.watchEdit.simulate.table.modeSelectLabel', + { + defaultMessage: 'Action modes', + } + )} + /> + ), + }, + ]; + + return ( + + { + setExecuteResults(null); + setExecuteResultsError(null); + }} + /> + +

+ {i18n.translate('xpack.watcher.sections.watchEdit.simulate.pageDescription', { + defaultMessage: + 'Use the simulator to override the watch schedule, condition, actions, and input results.', + })} +

+
+ + + + {i18n.translate( + 'xpack.watcher.sections.watchEdit.simulate.form.triggerOverridesTitle', + { defaultMessage: 'Trigger' } + )} + + } + description={i18n.translate( + 'xpack.watcher.sections.watchEdit.simulate.form.triggerOverridesDescription', + { + defaultMessage: 'Set the time and date for starting the watch.', + } + )} + > + + + + { + const value = e.target.value; + setExecuteDetails( + new ExecuteDetails({ + ...executeDetails, + scheduledTimeValue: value === '' ? value : parseInt(value, 10), + }) + ); + }} + /> + + + { + setExecuteDetails( + new ExecuteDetails({ + ...executeDetails, + scheduledTimeUnit: e.target.value, + }) + ); + }} + /> + + + + + + {getTimeUnitLabel(TIME_UNITS.SECOND, triggeredTimeValue)} + + } + onChange={e => { + const value = e.target.value; + setExecuteDetails( + new ExecuteDetails({ + ...executeDetails, + triggeredTimeValue: value === '' ? value : parseInt(value, 10), + triggeredTimeUnit: TIME_UNITS.SECOND, + }) + ); + }} + /> + + + + + {i18n.translate( + 'xpack.watcher.sections.watchEdit.simulate.form.conditionOverridesTitle', + { defaultMessage: 'Condition' } + )} + + } + description={i18n.translate( + 'xpack.watcher.sections.watchEdit.simulate.form.conditionOverridesDescription', + { + defaultMessage: + 'Execute the watch when the condition is met. Otherwise, ignore the condition and run the watch on a fixed schedule.', + } + )} + > + { + setExecuteDetails( + new ExecuteDetails({ ...executeDetails, ignoreCondition: e.target.checked }) + ); + }} + /> + + + + {i18n.translate( + 'xpack.watcher.sections.watchEdit.simulate.form.actionOverridesTitle', + { defaultMessage: 'Actions' } + )} + + } + description={ + + {i18n.translate( + 'xpack.watcher.sections.watchEdit.simulate.form.actionOverridesDescription.linkLabel', + { + defaultMessage: 'Learn about actions.', + } + )} + + ), + }} + /> + } + > + + + + + + + {i18n.translate( + 'xpack.watcher.sections.watchEdit.simulate.form.inputOverridesTitle', + { defaultMessage: 'Input' } + )} + + } + description={i18n.translate( + 'xpack.watcher.sections.watchEdit.simulate.form.inputOverridesDescription', + { + defaultMessage: + 'Enter JSON data to override the watch payload that comes from running the input.', + } + )} + > + + { + setExecuteDetails( + new ExecuteDetails({ + ...executeDetails, + alternativeInput: json, + }) + ); + }} + /> + + + { + setIsExecuting(true); + + const { data, error } = await executeWatch(executeDetails, watch); + + setIsExecuting(false); + + if (error) { + return setExecuteResultsError(error); + } + + const formattedResults = WatchHistoryItem.fromUpstreamJson(data.watchHistoryItem); + setExecuteResults(formattedResults); + }} + > + {i18n.translate('xpack.watcher.sections.watchEdit.simulate.form.saveButtonLabel', { + defaultMessage: 'Simulate watch', + })} + + +
+ ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate_results.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate_results.tsx new file mode 100644 index 0000000000000..1b2b4ab935e8c --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate_results.tsx @@ -0,0 +1,207 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment, useContext } from 'react'; + +import { + EuiBasicTable, + EuiCodeBlock, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + ExecutedWatchDetails, + ExecutedWatchResults, +} from '../../../../../common/types/watch_types'; +import { getTypeFromAction } from '../../watch_edit_actions'; +import { WatchContext } from '../../watch_context'; +import { WatchStatus, SectionError } from '../../../../components'; + +export const JsonWatchEditSimulateResults = ({ + executeResults, + executeDetails, + onCloseFlyout, + error, +}: { + executeResults: ExecutedWatchResults | null; + executeDetails: ExecutedWatchDetails; + onCloseFlyout: () => void; + error: any; +}) => { + const { watch } = useContext(WatchContext); + + const { actionModes } = executeDetails; + + const getTableData = () => { + const actions = watch.watch && watch.watch.actions; + if (executeResults && actions) { + const actionStatuses = + executeResults.watchStatus && executeResults.watchStatus.actionStatuses; + return Object.keys(actions).map(actionKey => { + const actionStatus = actionStatuses.find(status => status.id === actionKey); + return { + actionId: actionKey, + actionType: getTypeFromAction(actions[actionKey]), + actionMode: actionModes[actionKey], + actionState: actionStatus && actionStatus.state, + actionReason: actionStatus && actionStatus.lastExecutionReason, + }; + }); + } + return []; + }; + + const actionsTableData = getTableData(); + + const columns = [ + { + field: 'actionId', + name: i18n.translate( + 'xpack.watcher.sections.watchEdit.simulateResults.table.actionColumnLabel', + { + defaultMessage: 'ID', + } + ), + sortable: true, + truncateText: true, + }, + { + field: 'actionType', + name: i18n.translate( + 'xpack.watcher.sections.watchEdit.simulateResults.table.typeColumnLabel', + { + defaultMessage: 'Type', + } + ), + truncateText: true, + }, + { + field: 'actionMode', + name: i18n.translate( + 'xpack.watcher.sections.watchEdit.simulateResults.table.modeColumnLabel', + { + defaultMessage: 'Mode', + } + ), + }, + { + field: 'actionState', + name: i18n.translate( + 'xpack.watcher.sections.watchEdit.simulateResults.table.stateColumnLabel', + { + defaultMessage: 'State', + } + ), + dataType: 'string', + render: (actionState: string) => , + }, + { + field: 'actionReason', + name: i18n.translate( + 'xpack.watcher.sections.watchEdit.simulateResults.table.reasonColumnLabel', + { + defaultMessage: 'Reason', + } + ), + }, + ]; + + const flyoutTitle = ( + +

+ {i18n.translate('xpack.watcher.sections.watchEdit.simulateResults.title', { + defaultMessage: 'Simulation results', + })} +

+
+ ); + + if (error) { + return ( + { + onCloseFlyout(); + }} + aria-labelledby="simulateResultsFlyOutTitle" + > + {flyoutTitle} + + + } + error={error} + /> + + + ); + } + + if (!executeResults) { + return null; + } + + const { + watchStatus: { state }, + details, + } = executeResults; + + return ( + { + onCloseFlyout(); + }} + data-test-subj="simulateResultsFlyout" + aria-labelledby="simulateResultsFlyOutTitle" + > + + {flyoutTitle} + + + + + {actionsTableData && actionsTableData.length > 0 && ( + + +
+ {i18n.translate( + 'xpack.watcher.sections.watchEdit.simulateResults.actionsSectionTitle', + { + defaultMessage: 'Actions', + } + )} +
+
+ + + +
+ )} + +
+ {i18n.translate( + 'xpack.watcher.sections.watchEdit.simulateResults.simulationOutputSectionTitle', + { + defaultMessage: 'Simulation output', + } + )} +
+
+ + {JSON.stringify(details, null, 2)} +
+
+ ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/monitoring_watch_edit/index.ts b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/monitoring_watch_edit/index.ts new file mode 100644 index 0000000000000..e984da0d2cd11 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/monitoring_watch_edit/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 { MonitoringWatchEdit } from './monitoring_watch_edit'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx new file mode 100644 index 0000000000000..babc7d0053a51 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/monitoring_watch_edit/monitoring_watch_edit.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useContext } from 'react'; + +import { + EuiFlexGroup, + EuiFlexItem, + EuiPageContent, + EuiSpacer, + EuiTitle, + EuiCallOut, + EuiText, + EuiLink, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { WatchContext } from '../../watch_context'; + +export const MonitoringWatchEdit = ({ pageTitle }: { pageTitle: string }) => { + const { watch } = useContext(WatchContext); + + const systemWatchTitle = ( + + ); + + const systemWatchMessage = ( + + + + ), + }} + /> + ); + + return ( + + + + +

{pageTitle}

+
+
+
+ + + +

{systemWatchMessage}

+
+
+
+ ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/_index.scss b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/_index.scss deleted file mode 100644 index d601bc6b0e9da..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'threshold_watch_edit'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/_threshold_watch_edit.scss b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/_threshold_watch_edit.scss deleted file mode 100644 index b8a39a65c847f..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/_threshold_watch_edit.scss +++ /dev/null @@ -1,31 +0,0 @@ -threshold-watch-edit { - .thresholdWatchEditButtons { - flex-direction:row-reverse; - } - - .thresholdWatchEditSectionPanel { - background-color: $euiColorEmptyShade; - padding: $euiSizeL; - } - - .kuiVerticalRhythm + .kuiVerticalRhythm { - margin-top: $euiSizeL; - } - - .kuiWatchVisualization { - width: 800px; - height: 250px; - border: 1px solid $euiColorLightShade; - padding: $euiSizeM; - border-radius: $euiBorderRadius; - background-color: $euiColorLightestShade; - } - - .kuiWatchVisualizationMulti { - margin: $euiSizeL; - } - - .kuiSubText { - margin-top: $euiSizeS; - } -} diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/email_action_fields.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/email_action_fields.tsx new file mode 100644 index 0000000000000..c8f03cb9f8c63 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/email_action_fields.tsx @@ -0,0 +1,120 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { Fragment } from 'react'; + +import { EuiComboBox, EuiFieldText, EuiFormRow, EuiTextArea } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ErrableFormRow } from '../../../../../components/form_errors'; +import { EmailAction } from '../../../../../../common/types/action_types'; + +interface Props { + action: EmailAction; + editAction: (changedProperty: { key: string; value: any }) => void; + errors: { [key: string]: string[] }; + hasErrors: boolean; +} + +export const EmailActionFields: React.FunctionComponent = ({ + action, + editAction, + errors, + hasErrors, +}) => { + const { to, subject, body } = action; + const toOptions = to ? to.map(label => ({ label })) : []; + + return ( + + + { + const newOptions = [...toOptions, { label: searchValue }]; + editAction({ key: 'to', value: newOptions.map(newOption => newOption.label) }); + }} + onChange={(selectedOptions: Array<{ label: string }>) => { + editAction({ + key: 'to', + value: selectedOptions.map(selectedOption => selectedOption.label), + }); + }} + onBlur={() => { + if (!to) { + editAction({ + key: 'to', + value: [], + }); + } + }} + /> + + + + { + editAction({ key: 'subject', value: e.target.value }); + }} + /> + + + + { + editAction({ key: 'body', value: e.target.value }); + }} + onBlur={() => { + if (!body) { + editAction({ key: 'body', value: '' }); + } + }} + /> + + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/index.ts b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/index.ts new file mode 100644 index 0000000000000..b425c0b5e073d --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { EmailActionFields } from './email_action_fields'; +export { IndexActionFields } from './index_action_fields'; +export { JiraActionFields } from './jira_action_fields'; +export { LoggingActionFields } from './logging_action_fields'; +export { PagerDutyActionFields } from './pagerduty_action_fields'; +export { SlackActionFields } from './slack_action_fields'; +export { WebhookActionFields } from './webhook_action_fields'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/index_action_fields.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/index_action_fields.tsx new file mode 100644 index 0000000000000..1cafb08ca4060 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/index_action_fields.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { EuiFieldText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ErrableFormRow } from '../../../../../components/form_errors'; +import { IndexAction } from '../../../../../../common/types/action_types'; + +interface Props { + action: IndexAction; + editAction: (changedProperty: { key: string; value: string }) => void; + errors: { [key: string]: string[] }; + hasErrors: boolean; +} + +export const IndexActionFields: React.FunctionComponent = ({ + action, + editAction, + errors, + hasErrors, +}) => { + const { index } = action; + return ( + + ) => { + editAction({ key: 'index', value: e.target.value }); + }} + onBlur={() => { + if (!index) { + editAction({ key: 'index', value: '' }); + } + }} + /> + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/jira_action_fields.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/jira_action_fields.tsx new file mode 100644 index 0000000000000..b8bdeaff90821 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/jira_action_fields.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, { Fragment } from 'react'; + +import { EuiFieldText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ErrableFormRow } from '../../../../../components/form_errors'; +import { JiraAction } from '../../../../../../common/types/action_types'; + +interface Props { + action: JiraAction; + editAction: (changedProperty: { key: string; value: any }) => void; + errors: { [key: string]: string[] }; + hasErrors: boolean; + children: React.ReactNode; +} + +export const JiraActionFields: React.FunctionComponent = ({ + action, + editAction, + errors, + hasErrors, + children, +}) => { + const { projectKey, issueType, summary } = action; + + return ( + + {children} + + + { + editAction({ key: 'projectKey', value: e.target.value }); + }} + onBlur={() => { + if (!projectKey) { + editAction({ key: 'projectKey', value: '' }); + } + }} + /> + + + + { + editAction({ key: 'issueType', value: e.target.value }); + }} + onBlur={() => { + if (!issueType) { + editAction({ key: 'issueType', value: '' }); + } + }} + /> + + + + { + editAction({ key: 'summary', value: e.target.value }); + }} + onBlur={() => { + if (!summary) { + editAction({ key: 'summary', value: '' }); + } + }} + /> + + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/logging_action_fields.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/logging_action_fields.tsx new file mode 100644 index 0000000000000..b70e504519ae5 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/logging_action_fields.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { EuiFieldText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ErrableFormRow } from '../../../../../components/form_errors'; +import { LoggingAction } from '../../../../../../common/types/action_types'; + +interface Props { + action: LoggingAction; + editAction: (changedProperty: { key: string; value: string }) => void; + errors: { [key: string]: string[] }; + hasErrors: boolean; +} + +export const LoggingActionFields: React.FunctionComponent = ({ + action, + editAction, + errors, + hasErrors, +}) => { + const { text } = action; + return ( + + ) => { + editAction({ key: 'text', value: e.target.value }); + }} + onBlur={() => { + if (!text) { + editAction({ key: 'text', value: '' }); + } + }} + /> + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/pagerduty_action_fields.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/pagerduty_action_fields.tsx new file mode 100644 index 0000000000000..b2b670bf6b91f --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/pagerduty_action_fields.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { Fragment } from 'react'; +import { EuiFieldText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ErrableFormRow } from '../../../../../components/form_errors'; +import { PagerDutyAction } from '../../../../../../common/types/action_types'; + +interface Props { + action: PagerDutyAction; + editAction: (changedProperty: { key: string; value: string }) => void; + errors: { [key: string]: string[] }; + hasErrors: boolean; + children: React.ReactNode; +} + +export const PagerDutyActionFields: React.FunctionComponent = ({ + errors, + hasErrors, + action, + editAction, + children, +}) => { + const { description } = action; + return ( + + {children} + + ) => { + editAction({ key: 'description', value: e.target.value }); + }} + onBlur={() => { + if (!description) { + editAction({ key: 'description', value: '' }); + } + }} + /> + + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/slack_action_fields.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/slack_action_fields.tsx new file mode 100644 index 0000000000000..7b5a598c97eb7 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/slack_action_fields.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { Fragment } from 'react'; +import { EuiComboBox, EuiTextArea, EuiFormRow } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { SlackAction } from '../../../../../../common/types/action_types'; + +interface Props { + action: SlackAction; + editAction: (changedProperty: { key: string; value: any }) => void; + children: React.ReactNode; +} + +export const SlackActionFields: React.FunctionComponent = ({ + action, + editAction, + children, +}) => { + const { text, to } = action; + const toOptions = to ? to.map(label => ({ label })) : []; + + return ( + + {children} + + { + const newOptions = [...toOptions, { label: searchValue }]; + editAction({ key: 'to', value: newOptions.map(newOption => newOption.label) }); + }} + onChange={(selectedOptions: Array<{ label: string }>) => { + editAction({ + key: 'to', + value: selectedOptions.map(selectedOption => selectedOption.label), + }); + }} + /> + + + + { + editAction({ key: 'text', value: e.target.value }); + }} + /> + + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx new file mode 100644 index 0000000000000..d877ec1dbcbde --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx @@ -0,0 +1,250 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { Fragment, useEffect } from 'react'; + +import { + EuiCodeEditor, + EuiFieldNumber, + EuiFieldPassword, + EuiFieldText, + EuiFormRow, + EuiSelect, + EuiFlexGroup, + EuiFlexItem, + EuiText, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ErrableFormRow } from '../../../../../components/form_errors'; +import { WebhookAction } from '../../../../../../common/types/action_types'; + +interface Props { + action: WebhookAction; + editAction: (changedProperty: { key: string; value: any }) => void; + errors: { [key: string]: string[] }; + hasErrors: boolean; +} + +const HTTP_VERBS = ['head', 'get', 'post', 'put', 'delete']; + +export const WebhookActionFields: React.FunctionComponent = ({ + action, + editAction, + errors, + hasErrors, +}) => { + const { method, host, port, path, body, username, password } = action; + + useEffect(() => { + editAction({ key: 'contentType', value: 'application/json' }); // set content-type for threshold watch to json by default + }, []); + + return ( + + + + + ({ text: verb.toUpperCase(), value: verb }))} + onChange={e => { + editAction({ key: 'method', value: e.target.value }); + }} + /> + + + + + + { + editAction({ key: 'host', value: e.target.value }); + }} + onBlur={() => { + if (!host) { + editAction({ key: 'host', value: '' }); + } + }} + /> + + + + + + + : + + } + fullWidth + name="port" + value={port || ''} + data-test-subj="webhookPortInput" + onChange={e => { + editAction({ key: 'port', value: parseInt(e.target.value, 10) }); + }} + onBlur={() => { + if (!port) { + editAction({ key: 'port', value: '' }); + } + }} + /> + + + + + + + / + + } + fullWidth + name="path" + value={path || ''} + data-test-subj="webhookPathInput" + onChange={e => { + editAction({ key: 'path', value: e.target.value }); + }} + /> + + + + + + + + { + editAction({ key: 'username', value: e.target.value }); + }} + /> + + + + + + { + editAction({ key: 'password', value: e.target.value }); + }} + /> + + + + + + + + { + editAction({ key: 'body', value: json }); + }} + /> + + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/index.js deleted file mode 100644 index c00b7979a3f3b..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './threshold_watch_edit'; diff --git a/x-pack/legacy/plugins/watcher/common/constants/plugin.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/index.ts similarity index 79% rename from x-pack/legacy/plugins/watcher/common/constants/plugin.js rename to x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/index.ts index bcff927863019..5db1969477011 100644 --- a/x-pack/legacy/plugins/watcher/common/constants/plugin.js +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/index.ts @@ -4,6 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const PLUGIN = { - ID: 'watcher' -}; +export { ThresholdWatchEdit } from './threshold_watch_edit'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_accordion.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_accordion.tsx new file mode 100644 index 0000000000000..8b72eb7f19456 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_accordion.tsx @@ -0,0 +1,254 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { Fragment, useContext, useState } from 'react'; + +import { + EuiAccordion, + EuiButton, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiTitle, + EuiForm, + EuiCallOut, + EuiLink, + EuiText, + EuiSpacer, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ExecuteDetails } from 'plugins/watcher/models/execute_details/execute_details'; +import { Action } from 'plugins/watcher/models/action'; +import { toastNotifications } from 'ui/notify'; +import { WatchHistoryItem } from 'plugins/watcher/models/watch_history_item'; +import { ThresholdWatch } from 'plugins/watcher/models/watch/threshold_watch'; +import { ActionType } from '../../../../../common/types/action_types'; +import { ACTION_TYPES, ACTION_MODES } from '../../../../../common/constants'; +import { WatchContext } from '../../watch_context'; +import { + WebhookActionFields, + LoggingActionFields, + IndexActionFields, + SlackActionFields, + EmailActionFields, + PagerDutyActionFields, + JiraActionFields, +} from './action_fields'; +import { executeWatch } from '../../../../lib/api'; +import { watchActionsConfigurationMap } from '../../../../lib/documentation_links'; +import { SectionError } from '../../../../components'; + +const actionFieldsComponentMap = { + [ACTION_TYPES.LOGGING]: LoggingActionFields, + [ACTION_TYPES.SLACK]: SlackActionFields, + [ACTION_TYPES.EMAIL]: EmailActionFields, + [ACTION_TYPES.INDEX]: IndexActionFields, + [ACTION_TYPES.WEBHOOK]: WebhookActionFields, + [ACTION_TYPES.PAGERDUTY]: PagerDutyActionFields, + [ACTION_TYPES.JIRA]: JiraActionFields, +}; + +interface Props { + settings: { + actionTypes: { + [key: string]: { + enabled: boolean; + }; + }; + } | null; + actionErrors: { + [key: string]: { + [key: string]: string[]; + }; + }; +} + +export const WatchActionsAccordion: React.FunctionComponent = ({ + settings, + actionErrors, +}) => { + const { watch, setWatchProperty } = useContext(WatchContext); + const { actions } = watch; + + const [isExecuting, setIsExecuting] = useState<{ [key: string]: boolean }>({}); + const [executeResultsError, setExecuteResultsError] = useState(null); + + if (actions && actions.length >= 1) { + return actions.map((action: any) => { + const FieldsComponent = actionFieldsComponentMap[action.type]; + const errors = actionErrors[action.id]; + const hasErrors = !!Object.keys(errors).find(errorKey => errors[errorKey].length >= 1); + + return ( + + + + + + +
{action.typeName}
+
+
+ + } + extraAction={ + { + const updatedActions = actions.filter( + (actionItem: ActionType) => actionItem.id !== action.id + ); + setWatchProperty('actions', updatedActions); + }} + /> + } + paddingSize="l" + > + {executeResultsError && executeResultsError[action.id] && ( + + + } + error={executeResultsError[action.id]} + /> + + + )} + + { + const updatedActions = actions.map((actionItem: ActionType) => { + if (actionItem.id === action.id) { + const ActionTypes = Action.getActionTypes(); + const ActionTypeModel = ActionTypes[action.type]; + const { key, value } = changedProperty; + return new ActionTypeModel({ ...action, [key]: value }); + } + return actionItem; + }); + setWatchProperty('actions', updatedActions); + }} + > + {settings && settings.actionTypes[action.type].enabled === false ? ( + + + +

+ + + + ), + }} + /> +

+
+
+ +
+ ) : null} +
+ + { + const selectedWatchAction = watch.actions.filter( + (watchAction: any) => watchAction.id === action.id + ); + + const executeDetails = new ExecuteDetails({ + ignoreCondition: true, + recordExecution: false, + actionModes: { + [action.id]: ACTION_MODES.FORCE_EXECUTE, + }, + }); + + const newExecuteWatch = new ThresholdWatch({ + ...watch, + actions: selectedWatchAction, + }); + + setIsExecuting({ [action.id]: true }); + setExecuteResultsError(null); + + const { data, error } = await executeWatch(executeDetails, newExecuteWatch); + + setIsExecuting({ [action.id]: false }); + + if (error) { + return setExecuteResultsError({ [action.id]: error }); + } + + const formattedResults = WatchHistoryItem.fromUpstreamJson(data.watchHistoryItem); + const actionStatuses = formattedResults.watchStatus.actionStatuses; + const actionStatus = actionStatuses.find( + (actionItem: ActionType) => actionItem.id === action.id + ); + + if (actionStatus && actionStatus.lastExecutionSuccessful === false) { + const message = actionStatus.lastExecutionReason || action.simulateFailMessage; + return toastNotifications.addDanger(message); + } + return toastNotifications.addSuccess(action.simulateMessage); + }} + > + {action.simulatePrompt} + +
+
+ ); + }); + } + return null; +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_dropdown.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_dropdown.tsx new file mode 100644 index 0000000000000..82f3352b4e023 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_dropdown.tsx @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { + EuiSpacer, + EuiText, + EuiFlexItem, + EuiIcon, + EuiFlexGroup, + EuiButton, + EuiPopover, + EuiContextMenuPanel, + EuiContextMenuItem, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useContext, useState } from 'react'; +import { Action } from 'plugins/watcher/models/action'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ACTION_TYPES } from '../../../../../common/constants'; +import { WatchContext } from '../../watch_context'; + +const disabledMessage = i18n.translate( + 'xpack.watcher.sections.watchEdit.actions.disabledOptionLabel', + { + defaultMessage: 'Disabled. Configure your elasticsearch.yml.', + } +); + +interface Props { + settings: { + actionTypes: { + [key: string]: { + enabled: boolean; + }; + }; + } | null; + isLoading: boolean; +} + +export const WatchActionsDropdown: React.FunctionComponent = ({ settings, isLoading }) => { + const { addAction } = useContext(WatchContext); + + const [isPopoverOpen, setIsPopOverOpen] = useState(false); + + const allActionTypes = Action.getActionTypes() as Record; + + const actions = Object.entries(allActionTypes).map( + ([type, { typeName, iconClass, selectMessage }]) => { + const isEnabled = + settings && + settings.actionTypes && + settings.actionTypes[type] && + typeof settings.actionTypes[type].enabled !== 'undefined' + ? settings.actionTypes[type].enabled + : true; + return { + type, + typeName, + iconClass, + selectMessage, + isEnabled, + }; + } + ); + + const button = ( + setIsPopOverOpen(!isPopoverOpen)} + > + + + ); + + return ( + setIsPopOverOpen(false)} + panelPaddingSize="none" + anchorPosition="downLeft" + > + { + const isActionDisabled = action.type === ACTION_TYPES.EMAIL && !action.isEnabled; // Currently can only fully verify email action + const description = isActionDisabled ? disabledMessage : action.selectMessage; + return ( + { + addAction({ type: action.type, defaults: { isNew: true } }); + setIsPopOverOpen(false); + }} + > + + + + + + {action.typeName} + + +

{description}

+
+
+
+
+ ); + })} + /> +
+ ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_panel.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_panel.tsx new file mode 100644 index 0000000000000..504c5e10ed665 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_action_panel.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSpacer } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useContext } from 'react'; +import { loadSettings } from '../../../../lib/api'; +import { WatchActionsDropdown } from './threshold_watch_action_dropdown'; +import { WatchActionsAccordion } from './threshold_watch_action_accordion'; +import { WatchContext } from '../../watch_context'; + +interface Props { + actionErrors: { + [key: string]: { + [key: string]: string[]; + }; + }; +} + +export const WatchActionsPanel: React.FunctionComponent = ({ actionErrors }) => { + const { watch } = useContext(WatchContext); + + const { data: settings, isLoading } = loadSettings(); + + return ( +
+ + + +

+ {i18n.translate('xpack.watcher.sections.watchEdit.actions.title', { + defaultMessage: + 'Perform {watchActionsCount, plural, one{# action} other {# actions}} when condition is met', + values: { + watchActionsCount: watch.actions.length, + }, + })} +

+
+
+ + + +
+ + +
+ ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.html b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.html deleted file mode 100644 index 9283d2e9f43c2..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.html +++ /dev/null @@ -1,105 +0,0 @@ - -
- -
- -
-
-
-

-
-
- -
- -
- - -
-
-
-
- {{ thresholdWatchEdit.visualizeDataTitle }} -
-
-
- -
-
- -
-
- -
-
- -
- -
- -
- - - -
- -
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.js deleted file mode 100644 index 340c1e4eb4610..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.js +++ /dev/null @@ -1,325 +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 { debounce, isEqual } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { uiModules } from 'ui/modules'; -import { InitAfterBindingsWorkaround } from 'ui/compat'; -import template from './threshold_watch_edit.html'; -import '../watch_edit_title_panel'; -import 'plugins/watcher/components/threshold_watch_expression'; -import 'plugins/watcher/components/threshold_preview_chart'; -import 'plugins/watcher/components/watch_actions'; -import 'plugins/watcher/components/panel_pager'; - -import 'plugins/watcher/services/fields'; -import 'plugins/watcher/services/license'; -import 'plugins/watcher/services/timezone'; -import 'plugins/watcher/services/watch'; -import 'plugins/watcher/services/interval'; -import 'plugins/watcher/services/action_defaults'; - -import dateMath from '@elastic/datemath'; -import { toastNotifications } from 'ui/notify'; -import { VisualizeOptions } from 'plugins/watcher/models/visualize_options'; -import { REFRESH_INTERVALS } from 'plugins/watcher/../common/constants'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('thresholdWatchEdit', function ($injector) { - const watchService = $injector.get('xpackWatcherWatchService'); - const fieldsService = $injector.get('xpackWatcherFieldsService'); - const timezoneService = $injector.get('xpackWatcherTimezoneService'); - const licenseService = $injector.get('xpackWatcherLicenseService'); - const intervalService = $injector.get('xpackWatcherIntervalService'); - const actionDefaultsService = $injector.get('xpackWatcherActionDefaultsService'); - const kbnUrl = $injector.get('kbnUrl'); - const confirmModal = $injector.get('confirmModal'); - // const dirtyPrompt = $injector.get('dirtyPrompt'); - const $interval = $injector.get('$interval'); - - return { - restrict: 'E', - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - }, - bindToController: true, - controllerAs: 'thresholdWatchEdit', - controller: class ThresholdWatchEditController extends InitAfterBindingsWorkaround { - initAfterBindings($scope) { - this.index = undefined; - this.originalWatch = { - ...this.watch - }; - - this.omitBreadcrumbPages = [ - 'new-watch', - this.watch.id - ]; - this.breadcrumb = this.watch.displayName; - - // dirtyPrompt.register(() => !this.watch.isEqualTo(this.originalWatch)); - $scope.$on('$destroy', () => { - // dirtyPrompt.deregister(); - this.stopRefreshWatchVisualizationTimer(); - }); - - this.loadFields(); - } - - loadFields = () => { - if (!isEqual(this.watch.index, this.index)) { - this.index = this.watch.index; - - if (!this.index.length) { - this.fields = []; - return; - } - - fieldsService.getFields(this.index) - .then((fields) => { - this.fields = fields; - }); - } - } - - onChange = (watch) => { - this.loadFields(); - this.watch = watch; - this.updateVisualizeTimeWindow(); - this.visualizeWatch(); - } - - onActionChange = () => {} - - onTitlePanelValid = () => { - this.titlePanelValid = true; - this.visualizeWatch(); - } - - onTitlePanelInvalid = () => { - this.titlePanelValid = false; - } - - onConditionPanelValid = () => { - this.conditionPanelValid = true; - this.visualizeWatch(); - } - - onConditionPanelInvalid = () => { - this.conditionPanelValid = false; - } - - onActionsValid = () => { - this.actionsPanelValid = true; - } - - onActionsInvalid = () => { - this.actionsPanelValid = false; - } - - onActionAdd = (actionType) => { - const defaults = actionDefaultsService.getDefaults(this.watch.type, actionType); - this.watch.createAction(actionType, defaults); - } - - onActionDelete = (action) => { - this.watch.deleteAction(action); - } - - onActionSimulate = (action) => { - watchService.simulateWatchAction(this.watch, action) - .then((watchHistoryItem) => { - const actionStatuses = watchHistoryItem.watchStatus.actionStatuses; - const actionStatus = actionStatuses.find(a => a.id === action.id); - - if (actionStatus.lastExecutionSuccessful === false) { - const message = actionStatus.lastExecutionReason || action.simulateFailMessage; - toastNotifications.addDanger(message); - } else { - toastNotifications.addSuccess(action.simulateMessage); - } - }) - .catch(err => { - toastNotifications.addDanger(err); - }); - } - - onClose = () => { - // dirtyPrompt.deregister(); - kbnUrl.change('/management/elasticsearch/watcher/watches', {}); - } - - get saveDisabled() { - return !(this.titlePanelValid && this.conditionPanelValid && this.actionsPanelValid); - } - - updateVisualizeTimeWindow = () => { - const VISUALIZE_TIME_WINDOW_MULTIPLIER = 5; - - const fromExpression = `now-${this.watch.timeWindowSize * VISUALIZE_TIME_WINDOW_MULTIPLIER}${this.watch.timeWindowUnit}`; - const toExpression = `now`; - - const fromMoment = dateMath.parse(fromExpression); - const toMoment = dateMath.parse(toExpression); - this.visualizeTimeWindowFrom = fromMoment ? fromMoment.valueOf() : undefined; - this.visualizeTimeWindowTo = toMoment ? toMoment.valueOf() : undefined; - } - - visualizeWatch = debounce(() => { - if (!this.titlePanelValid || !this.conditionPanelValid) { - return; - } - - const interval = intervalService.getInterval({ - min: this.visualizeTimeWindowFrom, - max: this.visualizeTimeWindowTo - }).expression; - - const visualizeOptions = new VisualizeOptions({ - rangeFrom: this.visualizeTimeWindowFrom, - rangeTo: this.visualizeTimeWindowTo, - interval, - timezone: timezoneService.getTimezone() - }); - - return watchService.visualizeWatch(this.watch, visualizeOptions) - .then(({ visualizeData }) => { - this.visualizeData = visualizeData; - this.visualizeDataPageCount = Object.keys(visualizeData).length; - this.setVisualizationPageByKey(this.visualizeDataKey); - this.restartRefreshWatchVisualizationTimer(); - }) - .catch(e => { - toastNotifications.addDanger(e); - this.stopRefreshWatchVisualizationTimer(); - }); - }, 500); - - setVisualizationPageByKey = (key) => { - const newIndex = Object.keys(this.visualizeData).indexOf(key); - if (newIndex === -1) { - this.setVisualizationPage(0); - } else { - this.setVisualizationPage(newIndex); - } - } - - setVisualizationPage = (index) => { - this.visualizeDataKey = Object.keys(this.visualizeData)[index]; - - this.visualizeDataPageIndex = index; - this.visualizeDataTitle = `${this.watch.termField} (${index + 1} of ${this.visualizeDataPageCount}): ${this.visualizeDataKey}`; - this.chartSeries = this.visualizeData[this.visualizeDataKey]; - } - - onVisualizationNextPage = () => { - const newIndex = this.visualizeDataPageIndex + 1; - - if (newIndex < this.visualizeDataPageCount) { - this.setVisualizationPage(newIndex); - } else { - this.setVisualizationPage(0); - } - } - - onVisualizationPreviousPage = () => { - const newIndex = this.visualizeDataPageIndex - 1; - - if (newIndex >= 0) { - this.setVisualizationPage(newIndex); - } else { - this.setVisualizationPage(this.visualizeDataPageCount - 1); - } - } - - restartRefreshWatchVisualizationTimer = () => { - this.stopRefreshWatchVisualizationTimer(); - this.refreshVisualizationWatchInterval = $interval(this.visualizeWatch, REFRESH_INTERVALS.WATCH_VISUALIZATION); - } - - stopRefreshWatchVisualizationTimer = () => { - if (Boolean(this.refreshVisualizationWatchInterval)) { - $interval.cancel(this.refreshVisualizationWatchInterval); - } - } - - onWatchSave = () => { - if (!this.watch.isNew) { - return this.saveWatch(); - } - - return this.isExistingWatch() - .then(existingWatch => { - if (!existingWatch) { - return this.saveWatch(); - } - - const confirmModalOptions = { - onConfirm: this.saveWatch, - confirmButtonText: i18n.translate('xpack.watcher.sections.watchEdit.threshold.saveConfirmModal.overwriteWatchButtonLabel', { - defaultMessage: 'Overwrite Watch', - }), - }; - - const message = i18n.translate('xpack.watcher.sections.watchEdit.threshold.saveConfirmModal.description', { - defaultMessage: 'Watch with ID "{watchId}" {watchNameMessageFragment} already exists. Do you want to overwrite it?', - values: { - watchId: this.watch.id, - watchNameMessageFragment: existingWatch.name - ? i18n.translate('xpack.watcher.sections.watchEdit.threshold.saveConfirmModal.descriptionFragmentText', { - defaultMessage: '(name: "{existingWatchName}")', - values: { - existingWatchName: existingWatch.name - } - }) - : '' - } - }); - return confirmModal(message, confirmModalOptions); - }) - .catch(err => toastNotifications.addDanger(err)); - } - - isExistingWatch = () => { - return watchService.loadWatch(this.watch.id) - .then(existingWatch => { - return existingWatch; - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => { - if (err.status === 404) { - return false; - } - throw err; - }); - }); - } - - saveWatch = () => { - return watchService.saveWatch(this.watch) - .then(() => { - this.watch.isNew = false; // without this, the message displays 'New Watch' - toastNotifications.addSuccess( - i18n.translate('xpack.watcher.sections.watchEdit.threshold.saveSuccessNotificationText', { - defaultMessage: 'Saved \'{watchDisplayName}\'', - values: { - watchDisplayName: this.watch.displayName - } - }), - ); - this.onClose(); - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => toastNotifications.addDanger(err)); - }); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx new file mode 100644 index 0000000000000..4d092895b4149 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/threshold_watch_edit.tsx @@ -0,0 +1,913 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment, useContext, useEffect, useState } from 'react'; +import { + EuiButton, + EuiButtonEmpty, + EuiComboBox, + EuiComboBoxOptionProps, + EuiExpression, + EuiFieldNumber, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiPageContent, + EuiPopover, + EuiPopoverTitle, + EuiSelect, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { TIME_UNITS } from '../../../../../common/constants'; +import { ErrableFormRow, SectionError } from '../../../../components'; +import { fetchFields, getMatchingIndices, loadIndexPatterns } from '../../../../lib/api'; +import { aggTypes } from '../../../../models/watch/agg_types'; +import { groupByTypes } from '../../../../models/watch/group_by_types'; +import { comparators } from '../../../../models/watch/comparators'; +import { onWatchSave } from '../../watch_edit_actions'; +import { WatchContext } from '../../watch_context'; +import { WatchVisualization } from './watch_visualization'; +import { WatchActionsPanel } from './threshold_watch_action_panel'; +import { getTimeUnitLabel } from '../../../../lib/get_time_unit_label'; +import { goToWatchList } from '../../../../lib/navigation'; + +const expressionFieldsWithValidation = [ + 'aggField', + 'termSize', + 'termField', + 'threshold0', + 'threshold1', + 'timeWindowSize', +]; + +const expressionFields = [ + 'aggType', + 'aggField', + 'termSize', + 'termField', + 'thresholdComparator', + 'timeWindowSize', + 'timeWindowUnit', + 'triggerIntervalSize', + 'triggerIntervalUnit', + 'threshold', + 'groupBy', +]; + +const expressionErrorMessage = i18n.translate( + 'xpack.watcher.thresholdWatchExpression.fixErrorInExpressionBelowValidationMessage', + { + defaultMessage: 'Expression contains errors.', + } +); + +const firstFieldOption = { + text: i18n.translate('xpack.watcher.sections.watchEdit.titlePanel.timeFieldOptionLabel', { + defaultMessage: 'Select a field', + }), + value: '', +}; + +const getTimeOptions = (unitSize: string) => + Object.entries(TIME_UNITS).map(([_key, value]) => { + return { + text: getTimeUnitLabel(value, unitSize), + value, + }; + }); + +const getFields = async (indices: string[]) => { + return await fetchFields(indices); +}; +const getTimeFieldOptions = (fields: any) => { + const options = [firstFieldOption]; + + fields.forEach((field: any) => { + if (field.type === 'date') { + options.push({ + text: field.name, + value: field.name, + }); + } + }); + return options; +}; +interface IOption { + label: string; + options: Array<{ value: string; label: string }>; +} + +const getIndexOptions = async (patternString: string, indexPatterns: string[]) => { + const options: IOption[] = []; + + if (!patternString) { + return options; + } + + const matchingIndices = (await getMatchingIndices(patternString)) as string[]; + const matchingIndexPatterns = indexPatterns.filter(anIndexPattern => { + return anIndexPattern.includes(patternString); + }) as string[]; + + if (matchingIndices.length || matchingIndexPatterns.length) { + const matchingOptions = _.uniq([...matchingIndices, ...matchingIndexPatterns]); + + options.push({ + label: i18n.translate( + 'xpack.watcher.sections.watchEdit.titlePanel.indicesAndIndexPatternsLabel', + { + defaultMessage: 'Based on your indices and index patterns', + } + ), + options: matchingOptions.map(match => { + return { + label: match, + value: match, + }; + }), + }); + } + + options.push({ + label: i18n.translate('xpack.watcher.sections.watchEdit.titlePanel.chooseLabel', { + defaultMessage: 'Choose…', + }), + options: [ + { + value: patternString, + label: patternString, + }, + ], + }); + + return options; +}; + +export const ThresholdWatchEdit = ({ pageTitle }: { pageTitle: string }) => { + // hooks + const [indexPatterns, setIndexPatterns] = useState([]); + const [esFields, setEsFields] = useState([]); + const [indexOptions, setIndexOptions] = useState([]); + const [timeFieldOptions, setTimeFieldOptions] = useState([firstFieldOption]); + + const [aggFieldPopoverOpen, setAggFieldPopoverOpen] = useState(false); + const [groupByPopoverOpen, setGroupByPopoverOpen] = useState(false); + const [watchThresholdPopoverOpen, setWatchThresholdPopoverOpen] = useState(false); + const [watchDurationPopoverOpen, setWatchDurationPopoverOpen] = useState(false); + const [aggTypePopoverOpen, setAggTypePopoverOpen] = useState(false); + const [serverError, setServerError] = useState<{ + data: { nessage: string; error: string }; + } | null>(null); + const [isSaving, setIsSaving] = useState(false); + const [isIndiciesLoading, setIsIndiciesLoading] = useState(false); + + const { watch, setWatchProperty } = useContext(WatchContext); + + const getIndexPatterns = async () => { + const indexPatternObjects = await loadIndexPatterns(); + const titles = indexPatternObjects.map((indexPattern: any) => indexPattern.attributes.title); + setIndexPatterns(titles); + }; + + const loadData = async () => { + if (watch.index && watch.index.length > 0) { + const allEsFields = await getFields(watch.index); + const timeFields = getTimeFieldOptions(allEsFields); + setEsFields(allEsFields); + setTimeFieldOptions(timeFields); + setWatchProperty('timeFields', timeFields); + } + getIndexPatterns(); + }; + + useEffect(() => { + loadData(); + }, []); + + const { errors } = watch.validate(); + + const hasErrors = !!Object.keys(errors).find(errorKey => errors[errorKey].length >= 1); + + const actionErrors = watch.actions.reduce((acc: any, action: any) => { + const actionValidationErrors = action.validate(); + acc[action.id] = actionValidationErrors; + return acc; + }, {}); + + const hasActionErrors = !!Object.keys(actionErrors).find(actionError => { + return !!Object.keys(actionErrors[actionError]).find((actionErrorKey: string) => { + return actionErrors[actionError][actionErrorKey].length >= 1; + }); + }); + + const hasExpressionErrors = !!Object.keys(errors).find( + errorKey => expressionFieldsWithValidation.includes(errorKey) && errors[errorKey].length >= 1 + ); + + const shouldShowThresholdExpression = watch.index && watch.index.length > 0 && watch.timeField; + + const andThresholdText = i18n.translate('xpack.watcher.sections.watchEdit.threshold.andLabel', { + defaultMessage: 'AND', + }); + + return ( + + + + +

{pageTitle}

+
+ + + {watch.titleDescription} + +
+
+ + + {serverError && ( + + + } + error={serverError} + /> + + + )} + + } + errorKey="name" + isShowingErrors={hasErrors && watch.name !== undefined} + errors={errors} + > + { + setWatchProperty('name', e.target.value); + }} + onBlur={() => { + if (!watch.name) { + setWatchProperty('name', ''); + } + }} + /> + + + + + } + errorKey="index" + isShowingErrors={hasErrors && watch.index !== undefined} + errors={errors} + helpText={ + + } + > + { + return { + label: anIndex, + value: anIndex, + }; + })} + onChange={async (selected: EuiComboBoxOptionProps[]) => { + setWatchProperty('index', selected.map(aSelected => aSelected.value)); + const indices = selected.map(s => s.value as string); + + // reset time field and expression fields if indices are deleted + if (indices.length === 0) { + setTimeFieldOptions(getTimeFieldOptions([])); + setWatchProperty('timeFields', []); + + expressionFields.forEach(expressionField => { + setWatchProperty(expressionField, null); + }); + return; + } + const currentEsFields = await getFields(indices); + const timeFields = getTimeFieldOptions(currentEsFields); + + setEsFields(currentEsFields); + setWatchProperty('timeFields', timeFields); + setTimeFieldOptions(timeFields); + }} + onSearchChange={async search => { + setIsIndiciesLoading(true); + setIndexOptions(await getIndexOptions(search, indexPatterns)); + setIsIndiciesLoading(false); + }} + onBlur={() => { + if (!watch.index) { + setWatchProperty('index', []); + } + }} + /> + + + + + } + errorKey="timeField" + isShowingErrors={hasErrors && watch.timeField !== undefined} + errors={errors} + > + { + setWatchProperty('timeField', e.target.value); + }} + onBlur={() => { + if (watch.timeField === undefined) { + setWatchProperty('timeField', ''); + } + }} + /> + + + + + } + errorKey="triggerIntervalSize" + isShowingErrors={hasErrors && watch.triggerIntervalSize !== undefined} + errors={errors} + > + + + { + const { value } = e.target; + const triggerIntervalSize = value !== '' ? parseInt(value, 10) : value; + setWatchProperty('triggerIntervalSize', triggerIntervalSize); + }} + onBlur={e => { + if (watch.triggerIntervalSize === undefined) { + setWatchProperty('triggerIntervalSize', ''); + } + }} + /> + + + { + setWatchProperty('triggerIntervalUnit', e.target.value); + }} + options={getTimeOptions(watch.triggerIntervalSize)} + /> + + + + + + + {shouldShowThresholdExpression ? ( + + +

+ +

+
+ + + + { + setAggTypePopoverOpen(true); + }} + /> + } + isOpen={aggTypePopoverOpen} + closePopover={() => { + setAggTypePopoverOpen(false); + }} + ownFocus + withTitle + anchorPosition="downLeft" + > +
+ + {i18n.translate( + 'xpack.watcher.sections.watchEdit.threshold.whenButtonLabel', + { + defaultMessage: 'when', + } + )} + + { + setWatchProperty('aggType', e.target.value); + setAggTypePopoverOpen(false); + }} + options={Object.values(aggTypes).map(({ text, value }) => { + return { + text, + value, + }; + })} + /> +
+
+
+ {watch.aggType && aggTypes[watch.aggType].fieldRequired ? ( + + { + setAggFieldPopoverOpen(true); + }} + color={watch.aggField ? 'secondary' : 'danger'} + /> + } + isOpen={aggFieldPopoverOpen} + closePopover={() => { + setAggFieldPopoverOpen(false); + }} + anchorPosition="downLeft" + > +
+ + {i18n.translate( + 'xpack.watcher.sections.watchEdit.threshold.ofButtonLabel', + { + defaultMessage: 'of', + } + )} + + + + + { + if ( + aggTypes[watch.aggType].validNormalizedTypes.includes( + field.normalizedType + ) + ) { + esFieldOptions.push({ + label: field.name, + }); + } + return esFieldOptions; + }, [])} + selectedOptions={watch.aggField ? [{ label: watch.aggField }] : []} + onChange={selectedOptions => { + setWatchProperty( + 'aggField', + selectedOptions.length === 1 + ? selectedOptions[0].label + : undefined + ); + setAggFieldPopoverOpen(false); + }} + /> + + + +
+
+
+ ) : null} + + { + setGroupByPopoverOpen(true); + }} + color={ + watch.groupBy === 'all' || (watch.termSize && watch.termField) + ? 'secondary' + : 'danger' + } + /> + } + isOpen={groupByPopoverOpen} + closePopover={() => { + setGroupByPopoverOpen(false); + }} + ownFocus + withTitle + anchorPosition="downLeft" + > +
+ + {i18n.translate( + 'xpack.watcher.sections.watchEdit.threshold.overButtonLabel', + { + defaultMessage: 'over', + } + )} + + + + { + setWatchProperty('termSize', null); + setWatchProperty('termField', null); + setWatchProperty('groupBy', e.target.value); + }} + options={Object.values(groupByTypes).map(({ text, value }) => { + return { + text, + value, + }; + })} + /> + + + {groupByTypes[watch.groupBy].sizeRequired ? ( + + + + { + setWatchProperty('termSize', e.target.value); + }} + min={1} + /> + + + + + { + setWatchProperty('termField', e.target.value); + }} + options={esFields.reduce( + (options, field: any) => { + if ( + groupByTypes[watch.groupBy].validNormalizedTypes.includes( + field.normalizedType + ) + ) { + options.push({ + text: field.name, + value: field.name, + }); + } + return options; + }, + [firstFieldOption] + )} + /> + + + + ) : null} + +
+
+
+ + { + setWatchThresholdPopoverOpen(true); + }} + color={ + errors.threshold0.length || (errors.threshold1 && errors.threshold1.length) + ? 'danger' + : 'secondary' + } + /> + } + isOpen={watchThresholdPopoverOpen} + closePopover={() => { + setWatchThresholdPopoverOpen(false); + }} + ownFocus + withTitle + anchorPosition="downLeft" + > +
+ {comparators[watch.thresholdComparator].text} + + + { + setWatchProperty('thresholdComparator', e.target.value); + }} + options={Object.values(comparators).map(({ text, value }) => { + return { text, value }; + })} + /> + + {Array.from(Array(comparators[watch.thresholdComparator].requiredValues)).map( + (_notUsed, i) => { + return ( + + {i > 0 ? ( + + {andThresholdText} + {hasErrors && } + + ) : null} + + + { + const { value } = e.target; + const threshold = value !== '' ? parseFloat(value) : value; + const newThreshold = [...watch.threshold]; + newThreshold[i] = threshold; + setWatchProperty('threshold', newThreshold); + }} + /> + + + + ); + } + )} + +
+
+
+ + { + setWatchDurationPopoverOpen(true); + }} + color={watch.timeWindowSize ? 'secondary' : 'danger'} + /> + } + isOpen={watchDurationPopoverOpen} + closePopover={() => { + setWatchDurationPopoverOpen(false); + }} + ownFocus + withTitle + anchorPosition="downLeft" + > +
+ + + + + + + { + const { value } = e.target; + const timeWindowSize = value !== '' ? parseInt(value, 10) : value; + setWatchProperty('timeWindowSize', timeWindowSize); + }} + /> + + + + { + setWatchProperty('timeWindowUnit', e.target.value); + }} + options={getTimeOptions(watch.timeWindowSize)} + /> + + +
+
+
+
+ {hasExpressionErrors ? ( + + + + {expressionErrorMessage} + + + + ) : null} + {hasErrors ? null : ( + + + + + )} + +
+ ) : null} + + + { + setIsSaving(true); + const savedWatch = await onWatchSave(watch); + if (savedWatch && savedWatch.error) { + setIsSaving(false); + return setServerError(savedWatch.error); + } + }} + > + {watch.isNew ? ( + + ) : ( + + )} + + + + goToWatchList()}> + {i18n.translate('xpack.watcher.sections.watchEdit.threshold.cancelButtonLabel', { + defaultMessage: 'Cancel', + })} + + + +
+
+ ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx new file mode 100644 index 0000000000000..d9254c3692ee0 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/threshold_watch_edit/watch_visualization.tsx @@ -0,0 +1,290 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment, useContext, useEffect, useState } from 'react'; +import { + AnnotationDomainTypes, + Axis, + DARK_THEME, + getAnnotationId, + getAxisId, + getSpecId, + Chart, + LIGHT_THEME, + LineAnnotation, + LineSeries, + Position, + ScaleType, + Settings, +} from '@elastic/charts'; +import { TimeBuckets } from 'ui/time_buckets'; +import dateMath from '@elastic/datemath'; +import chrome from 'ui/chrome'; +import moment from 'moment-timezone'; +import { EuiCallOut, EuiLoadingChart, EuiSpacer, EuiEmptyPrompt, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { VisualizeOptions } from 'plugins/watcher/models/visualize_options'; +import { ThresholdWatch } from 'plugins/watcher/models/watch/threshold_watch'; +import { getWatchVisualizationData } from '../../../../lib/api'; +import { WatchContext } from '../../watch_context'; +import { aggTypes } from '../../../../models/watch/agg_types'; +import { comparators } from '../../../../models/watch/comparators'; +import { SectionError } from '../../../../components'; + +const getChartTheme = () => { + const isDarkTheme = chrome.getUiSettingsClient().get('theme:darkMode'); + const baseTheme = isDarkTheme ? DARK_THEME : LIGHT_THEME; + + return { + ...baseTheme, + lineSeriesStyle: { + ...baseTheme.lineSeriesStyle, + line: { + ...baseTheme.lineSeriesStyle.line, + strokeWidth: 3, + }, + point: { + ...baseTheme.lineSeriesStyle.point, + visible: false, + }, + }, + }; +}; + +const getTimezone = () => { + const config = chrome.getUiSettingsClient(); + const DATE_FORMAT_CONFIG_KEY = 'dateFormat:tz'; + const isCustomTimezone = !config.isDefault(DATE_FORMAT_CONFIG_KEY); + if (isCustomTimezone) { + return config.get(DATE_FORMAT_CONFIG_KEY); + } + + const detectedTimezone = moment.tz.guess(); + if (detectedTimezone) { + return detectedTimezone; + } + // default to UTC if we can't figure out the timezone + const tzOffset = moment().format('Z'); + return tzOffset; +}; + +const getDomain = (watch: any) => { + const VISUALIZE_TIME_WINDOW_MULTIPLIER = 5; + const fromExpression = `now-${watch.timeWindowSize * VISUALIZE_TIME_WINDOW_MULTIPLIER}${ + watch.timeWindowUnit + }`; + const toExpression = 'now'; + const fromMoment = dateMath.parse(fromExpression); + const toMoment = dateMath.parse(toExpression); + const visualizeTimeWindowFrom = fromMoment ? fromMoment.valueOf() : 0; + const visualizeTimeWindowTo = toMoment ? toMoment.valueOf() : 0; + return { + min: visualizeTimeWindowFrom, + max: visualizeTimeWindowTo, + }; +}; + +const getThreshold = (watch: any) => { + return watch.threshold.slice(0, comparators[watch.thresholdComparator].requiredValues); +}; + +const getTimeBuckets = (watch: any) => { + const domain = getDomain(watch); + const timeBuckets = new TimeBuckets(); + timeBuckets.setBounds(domain); + return timeBuckets; +}; + +export const WatchVisualization = () => { + const { watch } = useContext(WatchContext); + const { + index, + timeField, + triggerIntervalSize, + triggerIntervalUnit, + aggType, + aggField, + termSize, + termField, + thresholdComparator, + timeWindowSize, + timeWindowUnit, + groupBy, + threshold, + } = watch; + + const domain = getDomain(watch); + const timeBuckets = new TimeBuckets(); + timeBuckets.setBounds(domain); + const interval = timeBuckets.getInterval().expression; + const visualizeOptions = new VisualizeOptions({ + rangeFrom: domain.min, + rangeTo: domain.max, + interval, + timezone: getTimezone(), + }); + + // Fetching visualization data is independent of watch actions + const watchWithoutActions = new ThresholdWatch({ ...watch, actions: [] }); + + const [isInitialRequest, setIsInitialRequest] = useState(true); + + const { + isLoading, + data: watchVisualizationData, + error, + createRequest: reload, + } = getWatchVisualizationData(watchWithoutActions, visualizeOptions); + + useEffect( + () => { + // Prevents refetch on initial render + if (isInitialRequest) { + return setIsInitialRequest(false); + } + reload(false); + }, + [ + index, + timeField, + triggerIntervalSize, + triggerIntervalUnit, + aggType, + aggField, + termSize, + termField, + thresholdComparator, + timeWindowSize, + timeWindowUnit, + groupBy, + threshold, + ] + ); + + if (isLoading) { + return ( + } + body={ + + + + } + /> + ); + } + + if (error) { + return ( + + + + } + error={error} + /> + + + ); + } + + if (watchVisualizationData) { + const watchVisualizationDataKeys = Object.keys(watchVisualizationData); + const timezone = getTimezone(); + const actualThreshold = getThreshold(watch); + let maxY = actualThreshold[actualThreshold.length - 1]; + + (Object.values(watchVisualizationData) as number[][][]).forEach(data => { + data.forEach(([, y]) => { + if (y > maxY) { + maxY = y; + } + }); + }); + const dateFormatter = (d: number) => { + return moment(d) + .tz(timezone) + .format(getTimeBuckets(watch).getScaledDateFormat()); + }; + const aggLabel = aggTypes[watch.aggType].text; + return ( +
+ + {watchVisualizationDataKeys.length ? ( + + + + + {watchVisualizationDataKeys.map((key: string) => { + return ( + + ); + })} + {actualThreshold.map((_value: any, i: number) => { + const specId = i === 0 ? 'threshold' : `threshold${i}`; + return ( + + ); + })} + + ) : ( + + } + color="warning" + > + + + )} + +
+ ); + } + return null; +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/index.js deleted file mode 100644 index 090d7bb5092ad..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_action_mode_table'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/watch_action_mode_table.html b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/watch_action_mode_table.html deleted file mode 100644 index efa392a7f18b9..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/watch_action_mode_table.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - -
-
- {{actionOption.actionId}} -
-
-
- {{actionOption.type}} -
-
-
- -
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/watch_action_mode_table.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/watch_action_mode_table.js deleted file mode 100644 index 642daaceee83a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_action_mode_table/watch_action_mode_table.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { map, get, values, set } from 'lodash'; -import { uiModules } from 'ui/modules'; -import template from './watch_action_mode_table.html'; -import { getActionType } from 'plugins/watcher/../common/lib/get_action_type'; -import { ACTION_MODES } from 'plugins/watcher/../common/constants'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchActionModeTable', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - onChange: '=' - }, - bindToController: true, - controllerAs: 'watchActionModeTable', - controller: class WatchActionModeTableController { - constructor($scope) { - $scope.$watch('watchActionModeTable.watch.watch', () => { - this.populateActionCollection(); - this.onActionModeChange(); - }); - - this.actionModes = values(ACTION_MODES); - } - - populateActionCollection() { - const actions = get(this.watch, 'watch.actions'); - - this.actionOptions = map(actions, (action, actionId) => { - const type = getActionType(action); - return { - actionId, - type, - actionMode: ACTION_MODES.SIMULATE - }; - }); - } - - onActionModeChange() { - const result = {}; - this.actionOptions.forEach(actionOption => { - set(result, actionOption.actionId, actionOption.actionMode); - }); - this.onChange(result); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit.tsx new file mode 100644 index 0000000000000..b6946c8cc54f6 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit.tsx @@ -0,0 +1,198 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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, useReducer } from 'react'; +import { isEqual } from 'lodash'; + +import { EuiPageContent } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import chrome from 'ui/chrome'; +import { MANAGEMENT_BREADCRUMB } from 'ui/management'; + +import { Watch } from 'plugins/watcher/models/watch'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { WATCH_TYPES } from '../../../../common/constants'; +import { BaseWatch } from '../../../../common/types/watch_types'; +import { getPageErrorCode, PageError, SectionLoading, SectionError } from '../../../components'; +import { loadWatch } from '../../../lib/api'; +import { listBreadcrumb, editBreadcrumb, createBreadcrumb } from '../../../lib/breadcrumbs'; +import { JsonWatchEdit } from './json_watch_edit'; +import { ThresholdWatchEdit } from './threshold_watch_edit'; +import { MonitoringWatchEdit } from './monitoring_watch_edit'; +import { WatchContext } from '../watch_context'; + +const getTitle = (watch: BaseWatch) => { + if (watch.isNew) { + const typeName = watch.typeName.toLowerCase(); + return i18n.translate( + 'xpack.watcher.sections.watchEdit.json.titlePanel.createNewTypeOfWatchTitle', + { + defaultMessage: 'Create {typeName}', + values: { typeName }, + } + ); + } else { + return i18n.translate('xpack.watcher.sections.watchEdit.json.titlePanel.editWatchTitle', { + defaultMessage: 'Edit {watchName}', + values: { watchName: watch.name ? watch.name : watch.id }, + }); + } +}; + +const watchReducer = (state: any, action: any) => { + const { command, payload } = action; + const { watch } = state; + + switch (command) { + case 'setWatch': + return { + ...state, + watch: payload, + }; + + case 'setProperty': + const { property, value } = payload; + if (isEqual(watch[property], value)) { + return state; + } else { + return { + ...state, + watch: new (Watch.getWatchTypes())[watch.type]({ + ...watch, + [property]: value, + }), + }; + } + + case 'addAction': + const { type, defaults } = payload; + const newWatch = new (Watch.getWatchTypes())[watch.type](watch); + newWatch.createAction(type, defaults); + return { + ...state, + watch: newWatch, + }; + + case 'setError': + return { + ...state, + loadError: payload, + }; + } +}; + +export const WatchEdit = ({ + match: { + params: { id, type }, + }, +}: { + match: { + params: { + id: string | undefined; + type: string | undefined; + }; + }; +}) => { + // hooks + const [{ watch, loadError }, dispatch] = useReducer(watchReducer, { watch: null }); + + const setWatchProperty = (property: string, value: any) => { + dispatch({ command: 'setProperty', payload: { property, value } }); + }; + + const addAction = (action: any) => { + dispatch({ command: 'addAction', payload: action }); + }; + + const getWatch = async () => { + if (id) { + try { + const loadedWatch = await loadWatch(id); + dispatch({ command: 'setWatch', payload: loadedWatch }); + } catch (error) { + dispatch({ command: 'setError', payload: error }); + } + } else if (type) { + const WatchType = Watch.getWatchTypes()[type]; + if (WatchType) { + dispatch({ command: 'setWatch', payload: new WatchType() }); + } + } + }; + + useEffect(() => { + getWatch(); + }, []); + + useEffect( + () => { + chrome.breadcrumbs.set([ + MANAGEMENT_BREADCRUMB, + listBreadcrumb, + id ? editBreadcrumb : createBreadcrumb, + ]); + }, + [id] + ); + + const errorCode = getPageErrorCode(loadError); + if (errorCode) { + return ( + + + + ); + } + + if (loadError) { + return ( + + + } + error={loadError} + /> + + ); + } + + if (!watch) { + return ( + + + + ); + } + + const pageTitle = getTitle(watch); + + let EditComponent = null; + + switch (watch.type) { + case WATCH_TYPES.THRESHOLD: + EditComponent = ThresholdWatchEdit; + break; + case WATCH_TYPES.MONITORING: + EditComponent = MonitoringWatchEdit; + break; + case WATCH_TYPES.JSON: + default: + EditComponent = JsonWatchEdit; + } + + return ( + + + + ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/index.js deleted file mode 100644 index 7bae266bc8c16..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_edit_actions_execute_summary'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/watch_edit_actions_execute_summary.html b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/watch_edit_actions_execute_summary.html deleted file mode 100644 index e1b4f473c5c14..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/watch_edit_actions_execute_summary.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - -
-
- {{actionDetail.action.id}} -
-
-
- {{actionDetail.action.type}} -
-
-
- {{actionDetail.actionMode}} -
-
-
- - {{actionDetail.actionStatus.state}} -
-
-
- {{actionDetail.actionStatus.lastExecutionReason}} -
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/watch_edit_actions_execute_summary.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/watch_edit_actions_execute_summary.js deleted file mode 100644 index 2d59f623243bc..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_actions_execute_summary/watch_edit_actions_execute_summary.js +++ /dev/null @@ -1,51 +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 { find } from 'lodash'; -import { uiModules } from 'ui/modules'; -import template from './watch_edit_actions_execute_summary.html'; -import 'plugins/watcher/components/action_state_icon'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchEditActionsExecuteSummary', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - watchHistoryItem: '=', - executeDetails: '=' - }, - bindToController: true, - controllerAs: 'watchEditActionsExecuteSummary', - controller: class WatchEditActionsExecuteSummaryController { - constructor($scope) { - $scope.$watch('watchEditActionsExecuteSummary.watchHistoryItem', () => { - if (!this.watchHistoryItem) { - return; - } - - const actions = this.watch.actions; - const actionStatuses = this.watchHistoryItem.watchStatus.actionStatuses; - const actionModes = this.executeDetails.actionModes; - - this.actionDetails = actions.map(action => { - const actionMode = actionModes[action.id]; - const actionStatus = find(actionStatuses, { id: action.id }); - - return { - action, - actionMode, - actionStatus - }; - }); - }); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/_index.scss b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/_index.scss deleted file mode 100644 index 046363fdb37e7..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'watch_edit_detail'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/_watch_edit_detail.scss b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/_watch_edit_detail.scss deleted file mode 100644 index eac8309bc8f42..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/_watch_edit_detail.scss +++ /dev/null @@ -1,5 +0,0 @@ -watch-edit-detail { - .json-editor { - height: 400px; - } -} diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/index.js deleted file mode 100644 index 44c63f0f985d4..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_edit_detail'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/watch_edit_detail.html b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/watch_edit_detail.html deleted file mode 100644 index 9e114ef004ba4..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/watch_edit_detail.html +++ /dev/null @@ -1,81 +0,0 @@ -
-
- - -
-
-
- -
- - -
-
- -
- - -
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/watch_edit_detail.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/watch_edit_detail.js deleted file mode 100644 index 50875982ef485..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_detail/watch_edit_detail.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './watch_edit_detail.html'; -import 'plugins/watcher/components/json_editor'; -import { documentationLinks } from 'plugins/watcher/lib/documentation_links'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchEditDetail', function () { - return { - restrict: 'E', - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - onWatchChange: '=', - onInvalid: '=', - onValid: '=' - }, - bindToController: true, - controllerAs: 'watchEditDetail', - controller: class WatchEditDetailController { - constructor($scope) { - $scope.$watchMulti([ - 'watchEditDetail.watch.id', - 'watchEditDetail.watch.name', - 'watchEditDetail.watch.watch' - ], () => { - this.onWatchChange(this.watch); - }); - - $scope.$watch('watchEditDetail.form.$valid', () => { - this.updateIsValid(); - }); - - this.documentationLinks = documentationLinks; - } - - updateIsValid = () => { - const isValid = !(this.form.$invalid || !this.isJsonValid); - - if (isValid) { - this.onValid(); - } else { - this.onInvalid(); - } - } - - onJsonValid = () => { - this.isJsonValid = true; - this.updateIsValid(); - } - - onJsonInvalid = () => { - this.isJsonValid = false; - this.updateIsValid(); - } - - onJsonChange = (json) => { - this.watch.watch = json; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/_index.scss b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/_index.scss deleted file mode 100644 index 4f308b2526b8c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'watch_edit_execute_detail'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/_watch_edit_execute_detail.scss b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/_watch_edit_execute_detail.scss deleted file mode 100644 index 3d1dcf6cc3197..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/_watch_edit_execute_detail.scss +++ /dev/null @@ -1,19 +0,0 @@ -watch-edit-execute-detail { - .json-editor { - height: 300px; - } - - .copyFromPrevious { - float: right; - font-weight: normal; - } - - .kuiFormSection { - padding-left: $euiSize; - } - - label.collapserLabel { - font-weight: normal; - color: $euiColorDarkShade; - } -} diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/index.js deleted file mode 100644 index 452b73308d0e8..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_edit_execute_detail'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/watch_edit_execute_detail.html b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/watch_edit_execute_detail.html deleted file mode 100644 index 69e7a282f5ec9..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/watch_edit_execute_detail.html +++ /dev/null @@ -1,125 +0,0 @@ -
- -
- - -
-
- - -
-
-
- - -
- - - -
-
-
- -
- -
- -
-
-
- -
- -
- - -
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/watch_edit_execute_detail.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/watch_edit_execute_detail.js deleted file mode 100644 index e190cf247ddb0..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_detail/watch_edit_execute_detail.js +++ /dev/null @@ -1,109 +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 { isEmpty, get } from 'lodash'; -import { uiModules } from 'ui/modules'; -import template from './watch_edit_execute_detail.html'; -import '../watch_action_mode_table'; -import 'ace'; -import 'plugins/watcher/directives/toggle_panel'; -import { ExecuteDetails } from 'plugins/watcher/models/execute_details'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchEditExecuteDetail', function () { - return { - restrict: 'E', - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - previousWatchHistoryItem: '=', - onChange: '=', - onInvalid: '=', - onValid: '=' - }, - bindToController: true, - controllerAs: 'watchEditExecuteDetail', - controller: class WatchEditExecuteDetailController { - constructor($scope) { - this.executeDetails = new ExecuteDetails(); - - this.isCollapsed = { - trigger: true, - input: true, - condition: true, - action: true - }; - - $scope.aceLoaded = (editor) => { - this.editor = editor; - editor.$blockScrolling = Infinity; - }; - - $scope.$watchMulti([ - 'watchEditExecuteDetail.executeDetails.triggeredTime', - 'watchEditExecuteDetail.executeDetails.scheduledTime', - 'watchEditExecuteDetail.executeDetails.ignoreCondition', - 'watchEditExecuteDetail.executeDetails.recordExecution', - 'watchEditExecuteDetail.executeDetails.actionModes' - ], this.reportChanges); - } - - onSectionToggle = (sectionId) => { - this.isCollapsed[sectionId] = !this.isCollapsed[sectionId]; - } - - isSectionCollapsed = (sectionId) => { - return this.isCollapsed[sectionId]; - } - - onActionModesChanged = (actionModes) => { - this.executeDetails.actionModes = actionModes; - } - - copyFromPreviousExecution = () => { - this.executeDetails.alternativeInput = get( - this.previousWatchHistoryItem.details, - 'result.input.payload' - ); - } - - reportChanges = () => { - const isValid = this.triggerForm.$valid && - (this.isAlternativeInputValid || this.isAlternativeInputEmpty) && - this.conditionForm.$valid && - this.actionForm.$valid; - - if (isValid) { - this.onValid(); - } else { - this.onInvalid(); - } - - this.onChange(this.executeDetails); - } - - onAlternativeInputValid = () => { - this.isAlternativeInputValid = true; - this.reportChanges(); - } - - onAlternativeInputInvalid = () => { - this.isAlternativeInputValid = false; - this.reportChanges(); - } - - onAlternativeInputChange = (json) => { - this.executeDetails.alternativeInput = json; - this.reportChanges(); - } - - get isAlternativeInputEmpty() { - return isEmpty(String(this.executeDetails.alternativeInput)); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/index.js deleted file mode 100644 index cebe780523078..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_edit_execute_info_panel'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/watch_edit_execute_info_panel.html b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/watch_edit_execute_info_panel.html deleted file mode 100644 index 7b29329f0fdb5..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/watch_edit_execute_info_panel.html +++ /dev/null @@ -1,18 +0,0 @@ -
-
- - -
- -
-
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/watch_edit_execute_info_panel.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/watch_edit_execute_info_panel.js deleted file mode 100644 index 4335e790e6efd..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_execute_info_panel/watch_edit_execute_info_panel.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './watch_edit_execute_info_panel.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchEditExecuteInfoPanel', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: {}, - bindToController: true, - controllerAs: 'watchEditExecuteInfoPanel', - controller: class WatchEditExecuteInfoPanelController { - constructor() { - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/index.js deleted file mode 100644 index 40f404bf47e9b..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_edit_title_bar'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/watch_edit_title_bar.html b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/watch_edit_title_bar.html deleted file mode 100644 index 8efd0cb3672c4..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/watch_edit_title_bar.html +++ /dev/null @@ -1,37 +0,0 @@ -
-
-

-
-
-
- - - -
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/watch_edit_title_bar.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/watch_edit_title_bar.js deleted file mode 100644 index ed826392b5715..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_bar/watch_edit_title_bar.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './watch_edit_title_bar.html'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchEditTitleBar', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - isWatchValid: '=', - onWatchDelete: '=', - onWatchSave: '=', - onClose: '=' - }, - bindToController: true, - controllerAs: 'watchEditTitleBar', - controller: class WatchEditTitleBarController { - constructor() { - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/_index.scss b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/_index.scss deleted file mode 100644 index 0200b48921d3e..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'watch_edit_title_panel'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/_watch_edit_title_panel.scss b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/_watch_edit_title_panel.scss deleted file mode 100644 index 8e466b6d0e281..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/_watch_edit_title_panel.scss +++ /dev/null @@ -1,13 +0,0 @@ -.watchEditTitlePanelContent { - border-top: 1px solid $euiColorLightShade; - padding-top: $euiSize; - margin-top: $euiSize; -} - -.watchEditTitleIndexSelectContainer.ng-invalid .ui-select-container { - border-color: $euiColorDanger; -} - -.watchEditTitlePanelIntervalValidation { - width: 185px; -} diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/index.js deleted file mode 100644 index b13094c53e927..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_edit_title_panel'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/watch_edit_title_panel.html b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/watch_edit_title_panel.html deleted file mode 100644 index 20509afa1ae55..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/watch_edit_title_panel.html +++ /dev/null @@ -1,201 +0,0 @@ -
-
-

- {{watchEditTitlePanel.title}} -

-
-

{{watchEditTitlePanel.description}}

-
-
-
-
-
- -
- -
-
-
-   -
-
-
-
-
- -
- - -
-
-
-
-
- -
-
- -
- -
- -
-
-
-   -
-
- -
- - -
- -
-
-
-
-
-
-
-
-
-
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/watch_edit_title_panel.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/watch_edit_title_panel.js deleted file mode 100644 index 3be0f137b9c8f..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_title_panel/watch_edit_title_panel.js +++ /dev/null @@ -1,175 +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 { size } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { uiModules } from 'ui/modules'; -import { InitAfterBindingsWorkaround } from 'ui/compat'; -import template from './watch_edit_title_panel.html'; -import { TIME_UNITS } from 'plugins/watcher/../common/constants'; - -import 'plugins/watcher/components/index_select'; -import 'plugins/watcher/components/duration_select'; -import 'plugins/watcher/services/html_id_generator'; -import 'plugins/watcher/components/xpack_aria_describes'; - -const app = uiModules.get('xpack/watcher'); - -const VALID_NORMALIZED_TYPES = ['date']; - -app.directive('watchEditTitlePanel', function ($injector) { - const htmlIdGeneratorFactory = $injector.get('xpackWatcherHtmlIdGeneratorFactory'); - - return { - restrict: 'E', - template: template, - replace: true, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - fields: '=', - onChange: '=', - onInvalid: '=', - onValid: '=' - }, - bindToController: true, - controllerAs: 'watchEditTitlePanel', - controller: class WatchEditTitlePanelController extends InitAfterBindingsWorkaround { - initAfterBindings($scope) { - this.makeId = htmlIdGeneratorFactory.create(); - - this.triggerIntervalSize = this.watch.triggerIntervalSize; - this.triggerIntervalUnit = this.watch.triggerIntervalUnit; - - this.triggerIntervalMinimumUnit = TIME_UNITS.SECOND; - this.triggerIntervalMinimumSize = 10; - - this.timeFields = []; - this.indexSelectTouched = false; - - $scope.$watch('watchEditTitlePanel.triggerIntervalSize', (newSize) => { - this.watch.triggerIntervalSize = Number(newSize); - this.onChange(this.watch); - }); - - $scope.$watch('watchEditTitlePanel.triggerIntervalUnit', (newUnit) => { - this.watch.triggerIntervalUnit = newUnit; - this.onChange(this.watch); - }); - - $scope.$watchMulti([ - 'watchEditTitlePanel.watch.name', - 'watchEditTitlePanel.watch.timeField' - ], () => { - this.onChange(this.watch); - }); - - $scope.$watch('watchEditTitlePanel.watch.index', (newIndex) => { - if (!Boolean(newIndex) || newIndex.length === 0) { - this.watch.timeField = undefined; - this.timeFields = []; - } - }); - - $scope.$watchMulti([ - 'watchEditTitlePanel.form.$valid', - 'watchEditTitlePanel.watch.index' - ], this.updateValidity); - - $scope.$watch('watchEditTitlePanel.fields', (fields) => { - this.timeFields = Boolean(fields) - ? fields.filter( - field => VALID_NORMALIZED_TYPES.includes(field.normalizedType) - ) - : []; - this.updateValidity(); - }); - } - - get title() { - if (this.watch.isNew) { - const typeName = this.watch.typeName.toLowerCase(); - return i18n.translate('xpack.watcher.sections.watchEdit.titlePanel.createNewTypeOfWatchTitle', { - defaultMessage: 'Create a new {typeName}', - values: { typeName }, - }); - } else { - return this.watch.name; - } - } - - get description() { - return this.watch.titleDescription; - } - - onIndexSelectTouched = () => { - this.indexSelectTouched = true; - } - - onIndexSelection = (indices) => { - this.watch.index = indices; - this.indexSelectTouched = true; - this.updateValidity(); - this.onChange(this.watch); - } - - updateValidity = () => { - const isValid = this.form.$valid && this.hasAnyValidSelectedIndices(); - - if (isValid) { - this.onValid(); - } else { - this.onInvalid(); - } - } - - hasAnySelectedIndices = () => { - if (Array.isArray(this.watch.index)) { - return this.watch.index.length > 0; - } - return Boolean(this.watch.index); - } - - hasAnyValidSelectedIndices = () => { - return this.hasAnySelectedIndices() - && Boolean(this.fields) && this.fields.length > 0; - } - - isValidationMessageVisible = (fieldName, errorType, showIfOtherErrors = true) => { - if (!this.form[fieldName]) { - return false; - } - - let showMessage = (this.form[fieldName].$touched || this.form[fieldName].$dirty) && - this.form[fieldName].$error[errorType]; - - if (showMessage && !showIfOtherErrors && size(this.form[fieldName].$error) > 1) { - showMessage = false; - } - - return showMessage; - } - - showIndexSelectionValidationState = () => { - return !this.hasAnySelectedIndices() - || this.showInvalidIndexValidationMessage() - || this.showNoTimeFieldsValidationMessage(); - } - - showRequiredIndexSelectionValidationMessage = () => { - return this.indexSelectTouched && !this.hasAnySelectedIndices(); - } - - showInvalidIndexValidationMessage = () => { - return this.hasAnySelectedIndices() - && Boolean(this.fields) && this.fields.length === 0; - } - - showNoTimeFieldsValidationMessage = () => { - return this.hasAnyValidSelectedIndices() && this.timeFields.length === 0; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/index.js deleted file mode 100644 index 24e51a6e79c72..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_edit_watch_execute_summary'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/watch_edit_watch_execute_summary.html b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/watch_edit_watch_execute_summary.html deleted file mode 100644 index c4a59628729a3..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/watch_edit_watch_execute_summary.html +++ /dev/null @@ -1,8 +0,0 @@ -
- - - {{ watchEditWatchExecuteSummary.watchHistoryItem.watchStatus.state }} -
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/watch_edit_watch_execute_summary.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/watch_edit_watch_execute_summary.js deleted file mode 100644 index 2182cb20d26f3..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/components/watch_edit_watch_execute_summary/watch_edit_watch_execute_summary.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './watch_edit_watch_execute_summary.html'; -import 'plugins/watcher/components/watch_state_icon'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchEditWatchExecuteSummary', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - watchHistoryItem: '=' - }, - bindToController: true, - controllerAs: 'watchEditWatchExecuteSummary', - controller: class WatchEditWatchExecuteSummaryController { - constructor() {} - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/index.js deleted file mode 100644 index 531ad45dd6933..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_edit_route'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_context.ts b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_context.ts new file mode 100644 index 0000000000000..a786f7534e37a --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_context.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. + */ + +import React from 'react'; +export const WatchContext = React.createContext({} as any); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_actions.ts b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_actions.ts new file mode 100644 index 0000000000000..9d6a999db60e3 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_actions.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { toastNotifications } from 'ui/notify'; +import { get } from 'lodash'; +import { ACTION_TYPES, WATCH_TYPES } from '../../../common/constants'; +import { BaseWatch } from '../../../common/types/watch_types'; +import { createWatch } from '../../lib/api'; +import { goToWatchList } from '../../lib/navigation'; + +/** + * Get the type from an action where a key defines its type. + * eg: { email: { ... } } | { slack: { ... } } + */ +export function getTypeFromAction(action: { [key: string]: any }) { + const actionKeys = Object.keys(action); + let type; + Object.keys(ACTION_TYPES).forEach(k => { + if (actionKeys.includes(ACTION_TYPES[k])) { + type = ACTION_TYPES[k]; + } + }); + + return type ? type : ACTION_TYPES.UNKNOWN; +} + +function getPropsFromAction(type: string, action: { [key: string]: any }) { + if (type === ACTION_TYPES.SLACK) { + // Slack action has its props inside the "message" object + return action[type].message; + } + + if (type === ACTION_TYPES.JIRA) { + // Jira action has its required props inside the "fields" object + const jiraAction: { projectKey?: string; issueType?: string; summary?: string } = {}; + jiraAction.projectKey = get(action[type], 'fields.project.key'); + jiraAction.issueType = get(action[type], 'fields.issuetype.name'); + jiraAction.summary = get(action[type], 'fields.summary'); + return jiraAction; + } + return action[type]; +} + +/** + * Actions instances are not automatically added to the Watch _actions_ Array + * when we add them in the Json editor. This method takes takes care of it. + */ +function createActionsForWatch(watchInstance: BaseWatch) { + watchInstance.resetActions(); + + let action; + let type; + let actionProps; + + Object.keys(watchInstance.watch.actions).forEach(k => { + action = watchInstance.watch.actions[k]; + type = getTypeFromAction(action); + actionProps = { ...getPropsFromAction(type, action), ignoreDefaults: true }; + watchInstance.createAction(type, actionProps); + }); + return watchInstance; +} + +export async function saveWatch(watch: BaseWatch): Promise { + try { + await createWatch(watch); + toastNotifications.addSuccess( + i18n.translate('xpack.watcher.sections.watchEdit.json.saveSuccessNotificationText', { + defaultMessage: "Saved '{watchDisplayName}'", + values: { + watchDisplayName: watch.displayName, + }, + }) + ); + goToWatchList(); + } catch (error) { + return error.response ? { error: error.response } : { error }; + } +} + +export async function onWatchSave(watch: BaseWatch): Promise { + const watchActions = watch.watch && watch.watch.actions; + const watchData = watchActions ? createActionsForWatch(watch) : watch; + + if (watchData.type === WATCH_TYPES.JSON) { + const actionsErrors = watchData.actions.reduce((actionsErrorsAcc: any, action: any) => { + if (action.validate) { + const errors = action.validate(); + const errorKeys = Object.keys(errors); + const hasErrors = !!errorKeys.find(errorKey => errors[errorKey].length >= 1); + if (!hasErrors) { + return actionsErrorsAcc; + } + const newErrors = errorKeys.map(errorKey => errors[errorKey]); + const newErrorsFlattened = newErrors && newErrors.length ? [].concat(...newErrors) : []; + + return [...actionsErrorsAcc, ...newErrorsFlattened]; + } + return actionsErrorsAcc; + }, []); + if (actionsErrors.length > 0) { + return { + error: { + data: { + message: actionsErrors, + error: 'validation', + }, + }, + }; + } + return saveWatch(watchData); + } + return saveWatch(watchData); +} diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_route.html b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_route.html deleted file mode 100644 index b23df49ffe427..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_route.html +++ /dev/null @@ -1,8 +0,0 @@ - - diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_route.js b/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_route.js deleted file mode 100644 index dc01f31e7cbdb..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_edit/watch_edit_route.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import routes from 'ui/routes'; -import 'ui/url'; -import { toastNotifications } from 'ui/notify'; -import template from './watch_edit_route.html'; -import 'plugins/watcher/services/watch'; -import './components/json_watch_edit'; -import './components/threshold_watch_edit'; -import { WATCH_TYPES } from 'plugins/watcher/../common/constants'; -import { updateWatchSections } from 'plugins/watcher/lib/update_management_sections'; -import 'plugins/watcher/services/license'; -import { getWatchDetailBreadcrumbs } from '../../lib/breadcrumbs'; - -routes - .when('/management/elasticsearch/watcher/watches/watch/:id/edit') - .when('/management/elasticsearch/watcher/watches/new-watch/:watchType') - .defaults(/management\/elasticsearch\/watcher\/watches\/(new-watch\/:watchType|watch\/:id\/edit)/, { - template: template, - k7Breadcrumbs: getWatchDetailBreadcrumbs, - controller: class WatchEditRouteController { - constructor($injector) { - const $route = $injector.get('$route'); - this.watch = $route.current.locals.xpackWatch; - this.WATCH_TYPES = WATCH_TYPES; - } - }, - controllerAs: 'watchEditRoute', - resolve: { - watchTabs: ($injector) => { - const $route = $injector.get('$route'); - const watchId = $route.current.params.id; - updateWatchSections(watchId); - }, - xpackWatch: function ($injector) { - const $route = $injector.get('$route'); - const watchService = $injector.get('xpackWatcherWatchService'); - const licenseService = $injector.get('xpackWatcherLicenseService'); - const kbnUrl = $injector.get('kbnUrl'); - - const watchId = $route.current.params.id; - const watchType = $route.current.params.watchType; - - if (!watchId) { - return licenseService.refreshLicense() - .then(() => { - return watchService.newWatch(watchType); - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => { - if (err.status !== 403) { - toastNotifications.addDanger(err.data.message); - } - - kbnUrl.redirect('/management/elasticsearch/watcher/watches'); - return Promise.reject(); - }); - }); - } - - return watchService.loadWatch(watchId) - .catch(err => { - return licenseService.checkValidity() - .then(() => { - if (err.status !== 403) { - toastNotifications.addDanger(err.data.message); - } - - kbnUrl.redirect('/management/elasticsearch/watcher/watches'); - return Promise.reject(); - }); - }); - }, - checkLicense: ($injector) => { - const licenseService = $injector.get('xpackWatcherLicenseService'); - return licenseService.checkValidity(); - } - } - }); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/index.js deleted file mode 100644 index ddccce8fa8d9b..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_history_item'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/watch_history_item.html b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/watch_history_item.html deleted file mode 100644 index 8a16b5fdddeeb..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/watch_history_item.html +++ /dev/null @@ -1,26 +0,0 @@ - -
-
- -
-
- -
-
- - -
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/watch_history_item.js b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/watch_history_item.js deleted file mode 100644 index 540a8df90f46e..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item/watch_history_item.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { InitAfterBindingsWorkaround } from 'ui/compat'; -import template from './watch_history_item.html'; - -import 'plugins/watcher/components/watch_history_item_detail'; -import '../watch_history_item_watch_summary'; -import '../watch_history_item_actions_summary'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchHistoryItem', function () { - return { - restrict: 'E', - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - watchHistoryItem: '=', - }, - bindToController: true, - controllerAs: 'watchHistoryItem', - controller: class WatchHistoryItemController extends InitAfterBindingsWorkaround { - initAfterBindings() { - this.omitBreadcrumbPages = [ - 'watch', - 'history-item', - this.watch.id - ]; - this.breadcrumb = this.watch.displayName; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/index.js deleted file mode 100644 index f316a1e75e6f5..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_history_item_actions_summary'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/watch_history_item_actions_summary.html b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/watch_history_item_actions_summary.html deleted file mode 100644 index fce54f9faa4ad..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/watch_history_item_actions_summary.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - -
-
- {{actionDetail.action.id}} -
-
-
- {{actionDetail.action.type}} -
-
-
- - {{actionDetail.actionStatus.state}} -
-
-
- {{actionDetail.actionStatus.lastExecutionReason}} -
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/watch_history_item_actions_summary.js b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/watch_history_item_actions_summary.js deleted file mode 100644 index 8e78c12e8ad00..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_actions_summary/watch_history_item_actions_summary.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { find } from 'lodash'; -import { uiModules } from 'ui/modules'; -import { InitAfterBindingsWorkaround } from 'ui/compat'; -import template from './watch_history_item_actions_summary.html'; -import 'plugins/watcher/components/action_state_icon'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchHistoryItemActionsSummary', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - watch: '=xpackWatch', // Property names differ due to https://git.io/vSWXV - watchHistoryItem: '=', - }, - bindToController: true, - controllerAs: 'watchHistoryItemActionsSummary', - controller: class WatchHistoryItemActionsSummaryController extends InitAfterBindingsWorkaround { - initAfterBindings() { - const actions = this.watch.actions; - const actionStatuses = this.watchHistoryItem.watchStatus.actionStatuses; - - this.actionDetails = actions.map(action => { - const actionStatus = find(actionStatuses, { id: action.id }); - - return { - action, - actionStatus - }; - }); - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/index.js deleted file mode 100644 index e3ceb3c063320..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_history_item_watch_summary'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/watch_history_item_watch_summary.html b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/watch_history_item_watch_summary.html deleted file mode 100644 index 0b63aeb42562e..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/watch_history_item_watch_summary.html +++ /dev/null @@ -1,10 +0,0 @@ -
- - - {{ watchHistoryItemWatchSummary.watchHistoryItem.watchStatus.state }} -
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/watch_history_item_watch_summary.js b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/watch_history_item_watch_summary.js deleted file mode 100644 index 4aeb7b46278dd..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/components/watch_history_item_watch_summary/watch_history_item_watch_summary.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import template from './watch_history_item_watch_summary.html'; -import 'plugins/watcher/components/watch_state_icon'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchHistoryItemWatchSummary', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - watchHistoryItem: '=' - }, - bindToController: true, - controllerAs: 'watchHistoryItemWatchSummary', - controller: class WatchHistoryItemWatchSummaryController { - constructor() {} - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/index.js deleted file mode 100644 index d299a702230b0..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_history_item_route'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/watch_history_item_route.html b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/watch_history_item_route.html deleted file mode 100644 index 70aa78a0be752..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/watch_history_item_route.html +++ /dev/null @@ -1,4 +0,0 @@ - diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/watch_history_item_route.js b/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/watch_history_item_route.js deleted file mode 100644 index 491aaa2364cb7..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_history_item/watch_history_item_route.js +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import routes from 'ui/routes'; -import 'ui/url'; -import { toastNotifications } from 'ui/notify'; -import template from './watch_history_item_route.html'; -import 'plugins/watcher/services/watch'; -import 'plugins/watcher/services/watch_history'; -import './components/watch_history_item'; -import { updateHistorySection } from 'plugins/watcher/lib/update_management_sections'; -import { getWatchHistoryBreadcrumbs } from '../../lib/breadcrumbs'; - -routes - .when('/management/elasticsearch/watcher/watches/watch/:watchId/history-item/:watchHistoryItemId', { - template: template, - k7Breadcrumbs: getWatchHistoryBreadcrumbs, - resolve: { - watch: function ($injector) { - const $route = $injector.get('$route'); - const watchService = $injector.get('xpackWatcherWatchService'); - const kbnUrl = $injector.get('kbnUrl'); - - const watchId = $route.current.params.watchId; - - return watchService.loadWatch(watchId) - .catch(err => { - if (err.status !== 403) { - toastNotifications.addDanger(err.data.message); - } - - kbnUrl.redirect('/management/elasticsearch/watcher/watches'); - return Promise.reject(); - }); - }, - watchHistoryItem: function ($injector) { - const $route = $injector.get('$route'); - const $filter = $injector.get('$filter'); - const moment = $filter('moment'); - const watchHistoryService = $injector.get('xpackWatcherWatchHistoryService'); - const kbnUrl = $injector.get('kbnUrl'); - - const watchId = $route.current.params.watchId; - const watchHistoryItemId = $route.current.params.watchHistoryItemId; - - return watchHistoryService.loadWatchHistoryItem(watchHistoryItemId) - .then(historyItem => { - const display = moment(historyItem.startTime); - updateHistorySection(display); - - return historyItem; - }) - .catch(err => { - if (err.status !== 403) { - toastNotifications.addDanger(err.data.message); - } - - kbnUrl.redirect(`/management/elasticsearch/watcher/watches/watch/${watchId}/status`); - return Promise.reject(); - }); - } - }, - controllerAs: 'watchHistoryItemRoute', - controller: class WatchHistoryItemRouteController { - constructor($injector) { - const $route = $injector.get('$route'); - - this.watch = $route.current.locals.watch; - this.watchHistoryItem = $route.current.locals.watchHistoryItem; - } - } - }); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list.tsx new file mode 100644 index 0000000000000..6bcb7568eae38 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list.tsx @@ -0,0 +1,483 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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, useMemo, useEffect, Fragment } from 'react'; + +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiInMemoryTable, + EuiLink, + EuiPageContent, + EuiSpacer, + EuiText, + EuiTitle, + EuiToolTip, + EuiEmptyPrompt, + EuiButtonIcon, + EuiPopover, + EuiContextMenuPanel, + EuiContextMenuItem, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Moment } from 'moment'; +import chrome from 'ui/chrome'; +import { MANAGEMENT_BREADCRUMB } from 'ui/management'; + +import { REFRESH_INTERVALS, PAGINATION, WATCH_TYPES } from '../../../../common/constants'; +import { listBreadcrumb } from '../../../lib/breadcrumbs'; +import { + getPageErrorCode, + PageError, + DeleteWatchesModal, + WatchStatus, + SectionError, + SectionLoading, +} from '../../../components'; +import { loadWatches } from '../../../lib/api'; +import { watcherGettingStartedUrl } from '../../../lib/documentation_links'; +import { goToCreateThresholdAlert, goToCreateAdvancedWatch } from '../../../lib/navigation'; + +export const WatchList = () => { + // hooks + const [selection, setSelection] = useState([]); + const [watchesToDelete, setWatchesToDelete] = useState([]); + // Filter out deleted watches on the client, because the API will return 200 even though some watches + // may not really be deleted until after they're done firing and this could take some time. + const [deletedWatches, setDeletedWatches] = useState([]); + + useEffect(() => { + chrome.breadcrumbs.set([MANAGEMENT_BREADCRUMB, listBreadcrumb]); + }, []); + + const { isLoading: isWatchesLoading, data: watches, error } = loadWatches( + REFRESH_INTERVALS.WATCH_LIST + ); + + const [isPopoverOpen, setIsPopOverOpen] = useState(false); + + const availableWatches = useMemo( + () => + watches ? watches.filter((watch: any) => !deletedWatches.includes(watch.id)) : undefined, + [watches, deletedWatches] + ); + + const watcherDescriptionText = ( + + ); + + const createWatchContextMenu = ( + setIsPopOverOpen(!isPopoverOpen)} + > + + + } + isOpen={isPopoverOpen} + closePopover={() => setIsPopOverOpen(false)} + panelPaddingSize="none" + anchorPosition="downCenter" + > + { + return ( + { + setIsPopOverOpen(false); + const navigate = + watchType === WATCH_TYPES.THRESHOLD + ? goToCreateThresholdAlert + : goToCreateAdvancedWatch; + navigate(); + }} + > + {watchType === WATCH_TYPES.THRESHOLD ? ( + + + + + + + + + + + + + ) : ( + + + + + + + + + + + + + )} + + ); + })} + /> + + ); + + if (isWatchesLoading) { + return ( + + + + ); + } + + if (getPageErrorCode(error)) { + return ( + + + + ); + } + + if (availableWatches && availableWatches.length === 0) { + const emptyPromptBody = ( + +

+ {watcherDescriptionText}{' '} + + + +

+
+ ); + + return ( + + + + + } + body={emptyPromptBody} + actions={createWatchContextMenu} + data-test-subj="emptyPrompt" + /> + + ); + } + + let content; + + if (error) { + content = ( + + } + error={error} + /> + ); + } else if (availableWatches) { + const columns = [ + { + field: 'id', + name: i18n.translate('xpack.watcher.sections.watchList.watchTable.idHeader', { + defaultMessage: 'ID', + }), + sortable: true, + truncateText: true, + render: (id: string) => { + return ( + + {id} + + ); + }, + }, + { + field: 'name', + name: i18n.translate('xpack.watcher.sections.watchList.watchTable.nameHeader', { + defaultMessage: 'Name', + }), + render: (name: string, item: any) => { + return {name}; + }, + sortable: true, + truncateText: true, + }, + { + field: 'watchStatus.state', + name: i18n.translate('xpack.watcher.sections.watchList.watchTable.stateHeader', { + defaultMessage: 'State', + }), + sortable: true, + width: '130px', + render: (state: string) => , + }, + { + field: 'watchStatus.lastMetCondition', + name: i18n.translate('xpack.watcher.sections.watchList.watchTable.lastFiredHeader', { + defaultMessage: 'Last fired', + }), + sortable: true, + truncateText: true, + width: '130px', + render: (lastMetCondition: Moment) => { + return lastMetCondition ? lastMetCondition.fromNow() : lastMetCondition; + }, + }, + { + field: 'watchStatus.lastChecked', + name: i18n.translate('xpack.watcher.sections.watchList.watchTable.lastTriggeredHeader', { + defaultMessage: 'Last triggered', + }), + sortable: true, + truncateText: true, + width: '130px', + render: (lastChecked: Moment) => { + return lastChecked ? lastChecked.fromNow() : lastChecked; + }, + }, + { + field: 'watchStatus.comment', + name: i18n.translate('xpack.watcher.sections.watchList.watchTable.commentHeader', { + defaultMessage: 'Comment', + }), + sortable: true, + truncateText: true, + }, + { + name: i18n.translate('xpack.watcher.sections.watchList.watchTable.actionHeader', { + defaultMessage: 'Actions', + }), + width: '75px', + actions: [ + { + render: (watch: any) => { + const label = i18n.translate( + 'xpack.watcher.sections.watchList.watchTable.actionEditTooltipLabel', + { defaultMessage: 'Edit' } + ); + return ( + + + + ); + }, + }, + { + render: (watch: any) => { + const label = i18n.translate( + 'xpack.watcher.sections.watchList.watchTable.actionDeleteTooltipLabel', + { defaultMessage: 'Delete' } + ); + return ( + + { + setWatchesToDelete([watch.id]); + }} + data-test-subj="deleteWatchButton" + /> + + ); + }, + }, + ], + }, + ]; + + const selectionConfig = { + onSelectionChange: setSelection, + selectable: (watch: any) => !watch.isSystemWatch, + selectableMessage: (selectable: boolean) => + !selectable + ? i18n.translate('xpack.watcher.sections.watchList.watchTable.disabledWatchTooltipText', { + defaultMessage: 'This watch is read-only', + }) + : undefined, + }; + + const searchConfig = { + box: { + incremental: true, + }, + toolsLeft: selection.length && ( + { + setWatchesToDelete(selection.map((selected: any) => selected.id)); + }} + color="danger" + > + {selection.length > 1 ? ( + + ) : ( + + )} + + ), + toolsRight: createWatchContextMenu, + }; + + content = ( + + } + rowProps={() => ({ + 'data-test-subj': 'row', + })} + cellProps={() => ({ + 'data-test-subj': 'cell', + })} + data-test-subj="watchesTable" + /> + ); + } + + if (content) { + return ( + + { + if (deleted) { + setDeletedWatches([...deletedWatches, ...watchesToDelete]); + } + setWatchesToDelete([]); + }} + watchesToDelete={watchesToDelete} + /> + + + + +

+ +

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

{watcherDescriptionText}

+
+ + + + {content} +
+ ); + } + return null; +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/_index.scss b/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/_index.scss deleted file mode 100644 index 068a77f2e54b2..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './watch_list'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/_watch_list.scss b/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/_watch_list.scss deleted file mode 100644 index c26daaee9a08c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/_watch_list.scss +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 1. Watch list width collapses without this. - */ -.watcherWatchList { - width: 100%; /* 1 */ -} diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/index.js deleted file mode 100644 index 1097bb146ff05..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_list'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.html b/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.html deleted file mode 100644 index e0fc117fa0c5c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.html +++ /dev/null @@ -1,121 +0,0 @@ - -
- - {{ 'xpack.watcher.sections.watchList.noPermissionToManageWatchesText' | i18n: { defaultMessage: 'You do not have permission to manage watches.' } }} - - -
-

- Watcher -

-
-

-
-
-
- - - -
- -
- -
-
-
- -
- -
- -
- -
- - -
-
- - - - - - {{ 'xpack.watcher.sections.watchList.watchesNotFoundText' | i18n: { defaultMessage: 'No watches found.' } }} - - -
-
- -
- -
- - -
-
-
-
-
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.js b/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.js deleted file mode 100644 index 45b447dae1e0d..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.js +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { i18n } from '@kbn/i18n'; -import { InitAfterBindingsWorkaround } from 'ui/compat'; -import { toastNotifications } from 'ui/notify'; -import template from './watch_list.html'; -import '../watch_table'; -import { PAGINATION, REFRESH_INTERVALS, WATCH_TYPES } from 'plugins/watcher/../common/constants'; -import 'ui/pager_control'; -import 'ui/pager'; -import 'ui/react_components'; -import 'ui/table_info'; -import 'plugins/watcher/components/tool_bar_selected_count'; -import 'plugins/watcher/components/forbidden_message'; -import { watches } from 'plugins/watcher/services/watches'; -import 'plugins/watcher/services/license'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchList', function ($injector) { - const pagerFactory = $injector.get('pagerFactory'); - const licenseService = $injector.get('xpackWatcherLicenseService'); - const confirmModal = $injector.get('confirmModal'); - const $interval = $injector.get('$interval'); - const kbnUrl = $injector.get('kbnUrl'); - const Promise = $injector.get('Promise'); - - const $filter = $injector.get('$filter'); - const filter = $filter('filter'); - const orderBy = $filter('orderBy'); - const limitTo = $filter('limitTo'); - - return { - restrict: 'E', - template: template, - scope: { - watches: '=' - }, - bindToController: true, - controllerAs: 'watchList', - controller: class WatchListController extends InitAfterBindingsWorkaround { - initAfterBindings($scope) { - this.forbidden = false; - - //The initial load from watch_list_route will return null on a 403 error - if (this.watches === null) { - this.watches = []; - this.forbidden = true; - } - - this.selectedWatches = []; - this.pageOfWatches = []; - this.sortField = 'id'; - this.sortReverse = false; - - this.pager = pagerFactory.create(this.watches.length, PAGINATION.PAGE_SIZE, 1); - - // Reload watches periodically - const refreshInterval = $interval(() => this.loadWatches(), REFRESH_INTERVALS.WATCH_LIST); - $scope.$on('$destroy', () => $interval.cancel(refreshInterval)); - - // react to watch and ui changes - $scope.$watchMulti([ - 'watchList.watches', - 'watchList.sortField', - 'watchList.sortReverse', - 'watchList.query', - 'watchList.pager.currentPage' - ], this.applyFilters); - } - - get hasPageOfWatches() { - return this.pageOfWatches.length > 0; - } - - get hasSelectedWatches() { - return this.selectedWatches.length > 0; - } - - loadWatches = () => { - // wrapping promise for angular digest cycle - Promise.all([watches.getWatchList()]) - .then(([watches]) => { - this.watches = watches; - this.forbidden = false; - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => { - if (err.status === 403) { - this.forbidden = true; - } else { - toastNotifications.addDanger(err.message); - } - }); - }); - } - - onQueryChange = (query) => { - this.query = query; - }; - - onPageNext = () => { - this.pager.nextPage(); - }; - - onPagePrevious = () => { - this.pager.previousPage(); - }; - - onSortChange = (field, reverse) => { - this.sortField = field; - this.sortReverse = reverse; - }; - - onSelectedChange = (selectedWatches) => { - this.selectedWatches = selectedWatches; - }; - - onClickCreateThresholdAlert = () => { - this.goToWatchWizardForType(WATCH_TYPES.THRESHOLD); - }; - - onClickCreateAdvancedWatch = () => { - this.goToWatchWizardForType(WATCH_TYPES.JSON); - }; - - goToWatchWizardForType = (watchType) => { - const url = `management/elasticsearch/watcher/watches/new-watch/${watchType}`; - kbnUrl.change(url, {}); - }; - - onSelectedWatchesDelete = () => { - const watchesBeingDeleted = this.selectedWatches; - const numWatchesToDelete = watchesBeingDeleted.length; - - const confirmModalText = i18n.translate('xpack.watcher.sections.watchList.deleteSelectedWatchesConfirmModal.descriptionText', { - defaultMessage: 'This will permanently delete {numWatchesToDelete, plural, one {# Watch} other {# Watches}}. Are you sure?', - values: { numWatchesToDelete } - }); - const confirmButtonText = i18n.translate('xpack.watcher.sections.watchList.deleteSelectedWatchesConfirmModal.deleteButtonLabel', { - defaultMessage: 'Delete {numWatchesToDelete, plural, one {# Watch} other {# Watches}} ', - values: { numWatchesToDelete } - }); - - const confirmModalOptions = { - confirmButtonText, - onConfirm: () => this.deleteSelectedWatches(watchesBeingDeleted) - }; - - return confirmModal(confirmModalText, confirmModalOptions); - }; - - deleteSelectedWatches = (watchesBeingDeleted) => { - this.watchesBeingDeleted = watchesBeingDeleted; - - const numWatchesToDelete = this.watchesBeingDeleted.length; - - const watchIds = this.watchesBeingDeleted.map(watch => watch.id); - return watches.deleteWatches(watchIds) - .then(results => { - const numSuccesses = results.numSuccesses; - const numErrors = results.numErrors; - const numTotal = numWatchesToDelete; - - if (numSuccesses > 0) { - toastNotifications.addSuccess( - i18n.translate('xpack.watcher.sections.watchList.deleteSelectedWatchesSuccessNotification.descriptionText', { - defaultMessage: - 'Deleted {numSuccesses} out of {numTotal} selected {numWatchesToDelete, plural, one {# watch} other {# watches}}', - values: { numSuccesses, numTotal, numWatchesToDelete } - }) - ); - } - - if (numErrors > 0) { - toastNotifications.addError( - i18n.translate('xpack.watcher.sections.watchList.deleteSelectedWatchesErrorNotification.descriptionText', { - defaultMessage: - 'Couldn\'t delete {numErrors} out of {numTotal} selected {numWatchesToDelete, plural, one {# watch} other {# watches}}', - values: { numErrors, numTotal, numWatchesToDelete } - }) - ); - } - - this.loadWatches(); - }) - .catch(err => { - return licenseService.checkValidity() - .then(() => toastNotifications.addDanger(err.message)); - }); - } - - applyFilters = () => { - let filteredWatches = this.watches; - let pageOfWatches = []; - - filteredWatches = filter(filteredWatches, { searchValue: this.query }); - filteredWatches = orderBy(filteredWatches, this.sortField, this.sortReverse); - pageOfWatches = limitTo(filteredWatches, this.pager.pageSize, this.pager.startIndex); - - this.pageOfWatches = pageOfWatches; - this.pager.setTotalItems(filteredWatches.length); - }; - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/index.js deleted file mode 100644 index cb748ea048961..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_table'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.html b/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.html deleted file mode 100644 index b4c0adb60435d..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.html +++ /dev/null @@ -1,174 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - {{ 'xpack.watcher.sections.watchList.watchTable.idColumnLabel' | i18n: { defaultMessage: 'ID' } }} - - - - {{ 'xpack.watcher.sections.watchList.watchTable.nameColumnLabel' | i18n: { defaultMessage: 'Name' } }} - - - - {{ 'xpack.watcher.sections.watchList.watchTable.stateColumnLabel' | i18n: { defaultMessage: 'State' } }} - - - - {{ 'xpack.watcher.sections.watchList.watchTable.commentColumnLabel' | i18n: { defaultMessage: 'Comment' } }} - - - - {{ 'xpack.watcher.sections.watchList.watchTable.lastFiredColumnLabel' | i18n: { defaultMessage: 'Last Fired' } }} - - - - {{ 'xpack.watcher.sections.watchList.watchTable.lastTriggeredColumnLabel' | i18n: { defaultMessage: 'Last Triggered' } }} - -
-
- -
-
-
- - - {{item.watch.id}} - - - {{item.watch.id}} - - -
-
-
- - {{item.watch.name}} - -
-
-
- - - {{ item.watch.watchStatus.state }} - -
-
-
- - {{item.watch.watchStatus.comment}} - -
-
-
- - - {{item.watch.watchStatus.lastFiredHumanized}} - - -
-
-
- - - {{item.watch.watchStatus.lastCheckedHumanized}} - - -
-
-
- -
-
diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.js b/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.js deleted file mode 100644 index 9118f51af8782..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.js +++ /dev/null @@ -1,90 +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 { uiModules } from 'ui/modules'; -import 'plugins/watcher/directives/check_box'; -import 'plugins/watcher/directives/tooltip'; -import 'plugins/watcher/directives/sortable_column'; -import template from './watch_table.html'; -import 'plugins/watcher/components/watch_state_icon'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchTable', function () { - return { - restrict: 'E', - replace: true, - template: template, - scope: { - watches: '=', - watchesBeingDeleted: '=', - sortField: '=', - sortReverse: '=', - onSortChange: '=', - onSelectChange: '=' - }, - controllerAs: 'watchTable', - bindToController: true, - controller: class WatchTableController { - constructor($scope) { - this.allSelected = false; - - $scope.$watch('watchTable.watches', (watches) => { - const previousItems = this.items; - - this.items = _.map(watches, (watch) => { - const matchedItem = _.find(previousItems, previousItem => previousItem.watch.id === watch.id); - const selected = Boolean(_.get(matchedItem, 'selected')); - return { watch: watch, selected: selected }; - }); - this.editableItems = this.items.filter(item => this.isEditable(item)); - this.updateSelectedWatches(); - }); - - $scope.$watch('watchTable.watchesBeingDeleted', watches => { - this.items.forEach(item => { - const matchedItem = _.find(watches, watch => watch.id === item.watch.id); - item.selected = false; - item.isBeingDeleted = Boolean(matchedItem); - }); - this.editableItems = this.items.filter(item => this.isEditable(item)); - this.updateSelectedWatches(); - }); - } - - onAllSelectedChange = (itemId, allSelected) => { // eslint-disable-line no-unused-vars - _.forEach(this.editableItems, item => { - item.selected = allSelected; - }); - this.updateSelectedWatches(); - }; - - onWatchSelectedChange = (watchId, selected) => { - _.find(this.items, item => item.watch.id === watchId).selected = selected; - this.updateSelectedWatches(); - }; - - updateSelectedWatches = () => { - const selectedItems = _.filter(this.items, item => item.selected); - const selectedWatches = _.map(selectedItems, item => item.watch); - - const areAllEditableItemsSelected = selectedWatches.length === this.editableItems.length; - this.allSelected = areAllEditableItemsSelected && this.editableItems.length > 0; - - this.onSelectChange(selectedWatches); - }; - - isEditable = (item) => { - return !item.watch.isSystemWatch && !item.isBeingDeleted; - } - - areAnyEditable = () => { - return this.editableItems.length !== 0; - } - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/index.js b/x-pack/legacy/plugins/watcher/public/sections/watch_list/index.js deleted file mode 100644 index ec52b52d00203..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/index.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './register_management_section'; -import './watch_list_route'; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/watch_list_route.html b/x-pack/legacy/plugins/watcher/public/sections/watch_list/watch_list_route.html deleted file mode 100644 index ce7b6a2e892dd..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/watch_list_route.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_list/watch_list_route.js b/x-pack/legacy/plugins/watcher/public/sections/watch_list/watch_list_route.js deleted file mode 100644 index 9d0b91b41a559..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/sections/watch_list/watch_list_route.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import routes from 'ui/routes'; -import { management } from 'ui/management'; -import { toastNotifications } from 'ui/notify'; -import template from './watch_list_route.html'; -import './components/watch_list'; -import 'plugins/watcher/services/license'; -import { getWatchListBreadcrumbs } from '../../lib/breadcrumbs'; -import { watches } from 'plugins/watcher/services/watches'; - -routes - .when('/management/elasticsearch/watcher/', { - redirectTo: '/management/elasticsearch/watcher/watches/' - }); - -routes - .when('/management/elasticsearch/watcher/watches/', { - template: template, - controller: class WatchListRouteController { - constructor($injector) { - const $route = $injector.get('$route'); - this.watches = $route.current.locals.watches; - } - }, - controllerAs: 'watchListRoute', - k7Breadcrumbs: getWatchListBreadcrumbs, - resolve: { - watches: ($injector) => { - const licenseService = $injector.get('xpackWatcherLicenseService'); - const kbnUrl = $injector.get('kbnUrl'); - - return watches.getWatchList() - .catch(err => { - return licenseService.checkValidity() - .then(() => { - if (err.status === 403) { - return null; - } - - toastNotifications.addDanger(err.data.message); - kbnUrl.redirect('/management'); - return Promise.reject(); - }); - }); - }, - checkLicense: ($injector) => { - const licenseService = $injector.get('xpackWatcherLicenseService'); - return licenseService.checkValidity(); - } - } - }); - -routes.defaults(/\/management/, { - resolve: { - watcherManagementSection: ($injector) => { - const licenseService = $injector.get('xpackWatcherLicenseService'); - const watchesSection = management.getSection('elasticsearch/watcher'); - - if (licenseService.showLinks) { - watchesSection.show(); - } else { - watchesSection.hide(); - } - - if (licenseService.enableLinks) { - watchesSection.enable(); - watchesSection.tooltip = ''; - } else { - watchesSection.disable(); - watchesSection.tooltip = licenseService.message; - } - } - } -}); diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_detail.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_detail.tsx new file mode 100644 index 0000000000000..e871641fe0d5e --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_detail.tsx @@ -0,0 +1,237 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment, useState, useEffect, useContext } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { toastNotifications } from 'ui/notify'; + +import { + EuiInMemoryTable, + EuiTitle, + EuiButtonEmpty, + EuiToolTip, + EuiCallOut, + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, +} from '@elastic/eui'; +import { ackWatchAction } from '../../../lib/api'; +import { WatchStatus } from '../../../components'; +import { PAGINATION } from '../../../../common/constants'; +import { WatchDetailsContext } from '../watch_details_context'; + +interface ActionError { + code: string; + message: string; +} +interface ActionStatus { + id: string; + isAckable: boolean; + state: string; + errors: ActionError[]; +} + +export const WatchDetail = () => { + const { watchDetail } = useContext(WatchDetailsContext); + + const [actionStatuses, setActionStatuses] = useState([]); + const [isActionStatusLoading, setIsActionStatusLoading] = useState(false); + const [selectedErrorAction, setSelectedErrorAction] = useState(null); + + const { id: watchId, watchErrors, watchStatus, isSystemWatch } = watchDetail; + + const actionErrors = watchErrors && watchErrors.actionErrors; + const currentActionStatuses = watchStatus && watchStatus.actionStatuses; + + const hasActionErrors = actionErrors && Object.keys(actionErrors).length > 0; + + useEffect( + () => { + const actionStatusesWithErrors = + currentActionStatuses && + currentActionStatuses.map((currentActionStatus: ActionStatus) => { + const errors = actionErrors && actionErrors[currentActionStatus.id]; + return { + ...currentActionStatus, + errors: errors || [], + }; + }); + setActionStatuses(actionStatusesWithErrors); + }, + [watchDetail] + ); + + const baseColumns = [ + { + field: 'id', + name: i18n.translate('xpack.watcher.sections.watchDetail.watchTable.actionHeader', { + defaultMessage: 'Name', + }), + sortable: true, + truncateText: true, + }, + { + field: 'state', + name: i18n.translate('xpack.watcher.sections.watchDetail.watchTable.stateHeader', { + defaultMessage: 'State', + }), + sortable: true, + truncateText: true, + render: (state: string) => , + }, + ]; + + const errorColumn = { + field: 'errors', + name: i18n.translate('xpack.watcher.sections.watchDetail.watchTable.errorsHeader', { + defaultMessage: 'Errors', + }), + render: (errors: ActionError[], action: ActionStatus) => { + const { id: actionId } = action; + if (errors && errors.length > 0) { + return ( + setSelectedErrorAction(actionId)} + data-test-subj="actionErrorsButton" + > + {i18n.translate('xpack.watcher.sections.watchDetail.watchTable.errorsCellText', { + defaultMessage: '{total, number} {total, plural, one {error} other {errors}}', + values: { + total: errors.length, + }, + })} + + ); + } + return ; + }, + }; + + const actionColumn = { + actions: [ + { + available: (action: ActionStatus) => action.isAckable && !isSystemWatch, + render: (action: ActionStatus) => { + const { id: actionId } = action; + return ( + + { + setIsActionStatusLoading(true); + try { + const newWatchStatus = await ackWatchAction(watchId, actionId); + const newActionStatusesWithErrors = newWatchStatus.actionStatuses.map( + (newActionStatus: ActionStatus) => { + const errors = actionErrors && actionErrors[newActionStatus.id]; + return { + ...newActionStatus, + errors: errors || [], + }; + } + ); + setIsActionStatusLoading(false); + return setActionStatuses(newActionStatusesWithErrors); + } catch (e) { + setIsActionStatusLoading(false); + toastNotifications.addDanger( + i18n.translate( + 'xpack.watcher.sections.watchDetail.watchTable.ackActionErrorMessage', + { + defaultMessage: 'Error acknowledging action {actionId}', + values: { + actionId: action.id, + }, + } + ) + ); + } + }} + > + + + + ); + }, + }, + ], + }; + + const columns = hasActionErrors + ? [...baseColumns, errorColumn, actionColumn] + : [...baseColumns, actionColumn]; + + return ( +
+ {selectedErrorAction && ( + setSelectedErrorAction(null)} + > + + +

+ {selectedErrorAction} +

+
+
+ + + {actionErrors[selectedErrorAction].length > 1 ? ( +
    + {actionErrors[selectedErrorAction].map( + (actionError: ActionError, errorIndex: number) => ( +
  • {actionError.message}
  • + ) + )} +
+ ) : ( +

{actionErrors[selectedErrorAction][0].message}

+ )} +
+
+
+ )} + + + } + /> +
+ ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_history.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_history.tsx new file mode 100644 index 0000000000000..c65d6b12e2e65 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_history.tsx @@ -0,0 +1,311 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment, useState, useContext } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Moment } from 'moment'; + +import { + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiInMemoryTable, + EuiLink, + EuiSelect, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; + +import { PAGINATION } from '../../../../common/constants'; +import { WatchStatus, SectionError } from '../../../components'; +import { loadWatchHistory, loadWatchHistoryDetail } from '../../../lib/api'; +import { WatchDetailsContext } from '../watch_details_context'; + +const watchHistoryTimeSpanOptions = [ + { + value: 'now-1h', + text: i18n.translate('xpack.watcher.sections.watchHistory.timeSpan.1h', { + defaultMessage: 'Last one hour', + }), + }, + { + value: 'now-24h', + text: i18n.translate('xpack.watcher.sections.watchHistory.timeSpan.24h', { + defaultMessage: 'Last 24 hours', + }), + }, + { + value: 'now-7d', + text: i18n.translate('xpack.watcher.sections.watchHistory.timeSpan.7d', { + defaultMessage: 'Last 7 days', + }), + }, + { + value: 'now-30d', + text: i18n.translate('xpack.watcher.sections.watchHistory.timeSpan.30d', { + defaultMessage: 'Last 30 days', + }), + }, + { + value: 'now-6M', + text: i18n.translate('xpack.watcher.sections.watchHistory.timeSpan.6M', { + defaultMessage: 'Last 6 months', + }), + }, + { + value: 'now-1y', + text: i18n.translate('xpack.watcher.sections.watchHistory.timeSpan.1y', { + defaultMessage: 'Last 1 year', + }), + }, +]; + +export const WatchHistory = () => { + const { watchDetail: loadedWatch } = useContext(WatchDetailsContext); + + const [isActivated, setIsActivated] = useState(undefined); + const [detailWatchId, setDetailWatchId] = useState(undefined); + + const [watchHistoryTimeSpan, setWatchHistoryTimeSpan] = useState( + watchHistoryTimeSpanOptions[0].value + ); + + if (isActivated === undefined) { + // Set initial value for isActivated based on the watch we just loaded. + const isActive = (loadedWatch.watchStatus && loadedWatch.watchStatus.isActive) || false; + setIsActivated(isActive); + } + + const { error: historyError, data: history, isLoading } = loadWatchHistory( + loadedWatch.id, + watchHistoryTimeSpan + ); + + const { error: watchHistoryDetailsError, data: watchHistoryDetails } = loadWatchHistoryDetail( + detailWatchId + ); + + const executionDetail = watchHistoryDetails + ? JSON.stringify(watchHistoryDetails.details, null, 2) + : ''; + + if (historyError) { + return ( + + + + } + error={historyError} + /> + + ); + } + const columns = [ + { + field: 'startTime', + name: i18n.translate('xpack.watcher.sections.watchHistory.watchTable.startTimeHeader', { + defaultMessage: 'Trigger time', + }), + sortable: true, + truncateText: true, + render: (startTime: Moment, item: any) => { + const formattedDate = startTime.format(); + return ( + setDetailWatchId(item.id)} + > + {formattedDate} + + ); + }, + }, + { + field: 'watchStatus.state', + name: i18n.translate('xpack.watcher.sections.watchHistory.watchTable.stateHeader', { + defaultMessage: 'State', + }), + sortable: true, + truncateText: true, + render: (state: string) => , + }, + { + field: 'watchStatus.comment', + name: i18n.translate('xpack.watcher.sections.watchHistory.watchTable.commentHeader', { + defaultMessage: 'Comment', + }), + sortable: true, + truncateText: true, + }, + ]; + + const onTimespanChange = (e: React.ChangeEvent) => { + const timespan = e.target.value; + setWatchHistoryTimeSpan(timespan); + }; + + let flyout; + + if (detailWatchId !== undefined) { + if (watchHistoryDetailsError) { + flyout = ( + setDetailWatchId(undefined)} + aria-labelledby="watchHistoryErrorDetailsFlyoutTitle" + maxWidth={600} + > + + +

+ +

+
+
+ + + + } + error={watchHistoryDetailsError} + data-test-subj="errorMessage" + /> + +
+ ); + } + if (watchHistoryDetails !== undefined) { + const detailColumns = [ + { + field: 'id', + name: i18n.translate('xpack.watcher.sections.watchHistory.watchActionStatusTable.id', { + defaultMessage: 'Name', + }), + sortable: true, + truncateText: true, + }, + { + field: 'state', + name: i18n.translate('xpack.watcher.sections.watchHistory.watchActionStatusTable.state', { + defaultMessage: 'State', + }), + sortable: true, + truncateText: true, + render: (state: string) => , + }, + ]; + + flyout = ( + setDetailWatchId(undefined)} + aria-labelledby="watchHistoryDetailsFlyoutTitle" + maxWidth={600} + > + + +

+ +

+
+
+ + + +

+ +

+
+ + } + data-test-subj="watchActionsTable" + /> + + +

+ +

+
+ + {executionDetail} +
+
+ ); + } + } + + return ( +
+ + + + + + + + + + } + /> + {flyout} +
+ ); +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_status.tsx b/x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_status.tsx new file mode 100644 index 0000000000000..409cea68628f9 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_status/components/watch_status.tsx @@ -0,0 +1,258 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { + EuiPageContent, + EuiSpacer, + EuiTabs, + EuiTab, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiToolTip, + EuiBadge, + EuiButtonEmpty, +} from '@elastic/eui'; +import chrome from 'ui/chrome'; +import { MANAGEMENT_BREADCRUMB } from 'ui/management'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { toastNotifications } from 'ui/notify'; +import { WatchDetail } from './watch_detail'; +import { WatchHistory } from './watch_history'; +import { listBreadcrumb, statusBreadcrumb } from '../../../lib/breadcrumbs'; +import { loadWatchDetail, deactivateWatch, activateWatch } from '../../../lib/api'; +import { WatchDetailsContext } from '../watch_details_context'; +import { + getPageErrorCode, + PageError, + SectionLoading, + DeleteWatchesModal, +} from '../../../components'; +import { goToWatchList } from '../../../lib/navigation'; + +interface WatchStatusTab { + id: string; + name: string; +} + +const WATCH_EXECUTION_HISTORY_TAB = 'watchExecutionHistoryTab'; +const WATCH_ACTIONS_TAB = 'watchActionsTab'; + +const WATCH_STATUS_TABS: WatchStatusTab[] = [ + { + id: WATCH_EXECUTION_HISTORY_TAB, + name: i18n.translate('xpack.watcher.sections.watchStatus.executionHistoryTabLabel', { + defaultMessage: 'Execution history', + }), + }, + { + id: WATCH_ACTIONS_TAB, + name: i18n.translate('xpack.watcher.sections.watchStatus.actionsTabLabel', { + defaultMessage: 'Action statuses', + }), + }, +]; + +export const WatchStatus = ({ + match: { + params: { id }, + }, +}: { + match: { + params: { + id: string; + }; + }; +}) => { + const { + error: watchDetailError, + data: watchDetail, + isLoading: isWatchDetailLoading, + } = loadWatchDetail(id); + + const [selectedTab, setSelectedTab] = useState(WATCH_EXECUTION_HISTORY_TAB); + const [isActivated, setIsActivated] = useState(undefined); + const [watchesToDelete, setWatchesToDelete] = useState([]); + const [isTogglingActivation, setIsTogglingActivation] = useState(false); + + useEffect( + () => { + chrome.breadcrumbs.set([MANAGEMENT_BREADCRUMB, listBreadcrumb, statusBreadcrumb]); + }, + [id] + ); + + const errorCode = getPageErrorCode(watchDetailError); + + if (isWatchDetailLoading) { + return ( + + + + ); + } + + if (errorCode) { + return ( + + + + ); + } + + if (watchDetail) { + const { isSystemWatch, id: watchId, watchStatus, name: watchName } = watchDetail; + + if (isActivated === undefined) { + // Set initial value for isActivated based on the watch we just loaded. + setIsActivated(typeof watchStatus.isActive !== 'undefined' ? watchStatus.isActive : false); + } + + const activationButtonText = isActivated ? ( + + ) : ( + + ); + + const toggleWatchActivation = async () => { + const toggleActivation = isActivated ? deactivateWatch : activateWatch; + + setIsTogglingActivation(true); + + const { error } = await toggleActivation(watchId); + + setIsTogglingActivation(false); + + if (error) { + const message = isActivated + ? i18n.translate( + 'xpack.watcher.sections.watchList.toggleActivatationErrorNotification.deactivateDescriptionText', + { + defaultMessage: "Couldn't deactivate watch", + } + ) + : i18n.translate( + 'xpack.watcher.sections.watchList.toggleActivatationErrorNotification.activateDescriptionText', + { + defaultMessage: "Couldn't activate watch", + } + ); + return toastNotifications.addDanger(message); + } + + setIsActivated(!isActivated); + }; + + return ( + + + { + if (deleted) { + goToWatchList(); + } + setWatchesToDelete([]); + }} + watchesToDelete={watchesToDelete} + /> + + + +

+ +

+
+
+ {isSystemWatch ? ( + + + } + > + + + + + + ) : ( + + + + toggleWatchActivation()} + isLoading={isTogglingActivation} + > + {activationButtonText} + + + + { + setWatchesToDelete([watchId]); + }} + color="danger" + disabled={false} + > + + + + + + )} +
+ + + {WATCH_STATUS_TABS.map((tab, index) => ( + { + setSelectedTab(tab.id); + }} + isSelected={tab.id === selectedTab} + key={index} + data-test-subj="tab" + > + {tab.name} + + ))} + + + {selectedTab === WATCH_ACTIONS_TAB ? : } +
+
+ ); + } + + return null; +}; diff --git a/x-pack/legacy/plugins/watcher/public/sections/watch_status/watch_details_context.ts b/x-pack/legacy/plugins/watcher/public/sections/watch_status/watch_details_context.ts new file mode 100644 index 0000000000000..1d5001c1ad250 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/public/sections/watch_status/watch_details_context.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. + */ + +import React from 'react'; +export const WatchDetailsContext = React.createContext({} as any); diff --git a/x-pack/legacy/plugins/watcher/public/services/action_defaults/action_defaults_service.factory.js b/x-pack/legacy/plugins/watcher/public/services/action_defaults/action_defaults_service.factory.js deleted file mode 100644 index c7df96105afb6..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/action_defaults/action_defaults_service.factory.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { XpackWatcherActionDefaultsService } from './action_defaults_service'; -import { ActionDefaultsRegistryProvider } from './registry'; - -import './actions/email_action'; -import './actions/logging_action'; -import './actions/slack_action'; - -uiModules.get('xpack/watcher') - .factory('xpackWatcherActionDefaultsService', ($injector) => { - const config = $injector.get('config'); - const Private = $injector.get('Private'); - const registry = Private(ActionDefaultsRegistryProvider); - - return new XpackWatcherActionDefaultsService(config, registry); - }); diff --git a/x-pack/legacy/plugins/watcher/public/services/action_defaults/action_defaults_service.js b/x-pack/legacy/plugins/watcher/public/services/action_defaults/action_defaults_service.js deleted file mode 100644 index c261ba43db930..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/action_defaults/action_defaults_service.js +++ /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 class XpackWatcherActionDefaultsService { - constructor(config, registry) { - this.config = config; - this.registry = registry; - } - - getDefaults = (watchType, actionType) => { - const reg = this.registry; - const match = reg.find(registryEntry => registryEntry.watchType === watchType && registryEntry.actionType === actionType); - - return match ? match.getDefaults(this.config, watchType) : {}; - } -} diff --git a/x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/email_action.js b/x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/email_action.js deleted file mode 100644 index e6a7ff9ebe461..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/email_action.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { merge } from 'lodash'; -import { ACTION_TYPES, WATCH_TYPES } from 'plugins/watcher/../common/constants'; -import { ActionDefaultsRegistryProvider } from '../registry'; - -const DEFAULT_ADMIN_EMAIL_CONFIG_KEY = 'xPack:defaultAdminEmail'; -const actionType = ACTION_TYPES.EMAIL; - -function getActionDefaults(config) { - return { - to: config.get(DEFAULT_ADMIN_EMAIL_CONFIG_KEY) - }; -} - -ActionDefaultsRegistryProvider.register(() => { - return { - actionType, - watchType: WATCH_TYPES.THRESHOLD, - getDefaults: (config) => { - const actionDefaults = getActionDefaults(config); - const actionWatchComboDefaults = { - subject: 'Watch [{{ctx.metadata.name}}] has exceeded the threshold' - }; - - return merge(actionDefaults, actionWatchComboDefaults); - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/logging_action.js b/x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/logging_action.js deleted file mode 100644 index 8f1ac0bf3f580..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/logging_action.js +++ /dev/null @@ -1,30 +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 { merge } from 'lodash'; -import { ACTION_TYPES, WATCH_TYPES } from 'plugins/watcher/../common/constants'; -import { ActionDefaultsRegistryProvider } from '../registry'; - -const actionType = ACTION_TYPES.LOGGING; - -function getActionDefaults() { - return {}; -} - -ActionDefaultsRegistryProvider.register(() => { - return { - actionType, - watchType: WATCH_TYPES.THRESHOLD, - getDefaults: (config) => { - const actionDefaults = getActionDefaults(config); - const actionWatchComboDefaults = { - text: 'Watch [{{ctx.metadata.name}}] has exceeded the threshold' - }; - - return merge(actionDefaults, actionWatchComboDefaults); - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/slack_action.js b/x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/slack_action.js deleted file mode 100644 index ed2e9a5b809c8..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/action_defaults/actions/slack_action.js +++ /dev/null @@ -1,30 +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 { merge } from 'lodash'; -import { ACTION_TYPES, WATCH_TYPES } from 'plugins/watcher/../common/constants'; -import { ActionDefaultsRegistryProvider } from '../registry'; - -const actionType = ACTION_TYPES.SLACK; - -function getActionDefaults() { - return {}; -} - -ActionDefaultsRegistryProvider.register(() => { - return { - actionType, - watchType: WATCH_TYPES.THRESHOLD, - getDefaults: (config) => { - const actionDefaults = getActionDefaults(config); - const actionWatchComboDefaults = { - text: 'Watch [{{ctx.metadata.name}}] has exceeded the threshold' - }; - - return merge(actionDefaults, actionWatchComboDefaults); - } - }; -}); diff --git a/x-pack/legacy/plugins/watcher/public/services/action_defaults/index.js b/x-pack/legacy/plugins/watcher/public/services/action_defaults/index.js deleted file mode 100644 index a3ccc4d8d67b0..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/action_defaults/index.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './action_defaults_service.factory'; -import './registry'; diff --git a/x-pack/legacy/plugins/watcher/public/services/action_defaults/registry.js b/x-pack/legacy/plugins/watcher/public/services/action_defaults/registry.js deleted file mode 100644 index 475d692b58604..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/action_defaults/registry.js +++ /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. - */ - -import { uiRegistry } from 'ui/registry/_registry'; - -export const ActionDefaultsRegistryProvider = uiRegistry({ - name: 'actionDefaultsRegistry' -}); diff --git a/x-pack/legacy/plugins/watcher/public/services/fields/fields_service.factory.js b/x-pack/legacy/plugins/watcher/public/services/fields/fields_service.factory.js deleted file mode 100644 index cedb5edd9c6d1..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/fields/fields_service.factory.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { FieldsService } from './fields_service'; - -uiModules.get('xpack/watcher') - .factory('xpackWatcherFieldsService', ($injector) => { - const $http = $injector.get('$http'); - return new FieldsService($http); - }); diff --git a/x-pack/legacy/plugins/watcher/public/services/fields/fields_service.js b/x-pack/legacy/plugins/watcher/public/services/fields/fields_service.js deleted file mode 100644 index d2be37ba605fc..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/fields/fields_service.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import chrome from 'ui/chrome'; -import { ROUTES } from '../../../common/constants'; - -export class FieldsService { - constructor($http) { - this.$http = $http; - this.basePath = chrome.addBasePath(ROUTES.API_ROOT); - } - - getFields(indexes = ['*']) { - return this.$http.post(`${this.basePath}/fields`, { indexes }) - .then(response => { - return response.data.fields; - }); - } -} diff --git a/x-pack/legacy/plugins/watcher/public/services/fields/index.js b/x-pack/legacy/plugins/watcher/public/services/fields/index.js deleted file mode 100644 index cbd4b9358c355..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/fields/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './fields_service.factory'; diff --git a/x-pack/legacy/plugins/watcher/public/services/html_id_generator/html_id_generator_service.factory.js b/x-pack/legacy/plugins/watcher/public/services/html_id_generator/html_id_generator_service.factory.js deleted file mode 100644 index a10d3ce99ebde..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/html_id_generator/html_id_generator_service.factory.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { createHtmlIdGenerator } from './html_id_generator_service'; - -uiModules.get('xpack/watcher') - .factory('xpackWatcherHtmlIdGeneratorFactory', () => { - return { - create(...args) { - return createHtmlIdGenerator(...args); - } - }; - }); diff --git a/x-pack/legacy/plugins/watcher/public/services/html_id_generator/html_id_generator_service.js b/x-pack/legacy/plugins/watcher/public/services/html_id_generator/html_id_generator_service.js deleted file mode 100644 index 4e093fd3d4015..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/html_id_generator/html_id_generator_service.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { camelCase } from 'lodash'; - -export function createHtmlIdGenerator(rootPartOrParts = []) { - const rootParts = Array.isArray(rootPartOrParts) ? rootPartOrParts : [rootPartOrParts]; - const cache = {}; - - return (parts, useRootParts = true) => { - if (!cache[parts]) { - const root = useRootParts ? rootParts : []; - const combined = root.concat(parts); - const id = camelCase(combined); - - cache[parts] = id; - } - - return cache[parts]; - }; -} diff --git a/x-pack/legacy/plugins/watcher/public/services/html_id_generator/index.js b/x-pack/legacy/plugins/watcher/public/services/html_id_generator/index.js deleted file mode 100644 index e3cf7904e38a3..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/html_id_generator/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './html_id_generator_service.factory'; diff --git a/x-pack/legacy/plugins/watcher/public/services/indices/index.js b/x-pack/legacy/plugins/watcher/public/services/indices/index.js deleted file mode 100644 index e8e6ee86f84c7..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/indices/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './indices_service.factory'; diff --git a/x-pack/legacy/plugins/watcher/public/services/indices/indices_service.factory.js b/x-pack/legacy/plugins/watcher/public/services/indices/indices_service.factory.js deleted file mode 100644 index 17a66936f1243..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/indices/indices_service.factory.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { IndicesService } from './indices_service'; - -uiModules.get('xpack/watcher') - .factory('xpackWatcherIndicesService', ($injector) => { - const $http = $injector.get('$http'); - return new IndicesService($http); - }); diff --git a/x-pack/legacy/plugins/watcher/public/services/indices/indices_service.js b/x-pack/legacy/plugins/watcher/public/services/indices/indices_service.js deleted file mode 100644 index 9b185e68ba7e6..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/indices/indices_service.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import chrome from 'ui/chrome'; -import { ROUTES } from '../../../common/constants'; - -export class IndicesService { - constructor($http) { - this.$http = $http; - this.basePath = chrome.addBasePath(ROUTES.API_ROOT); - } - - getMatchingIndices(pattern) { - return this.$http.post(`${this.basePath}/indices`, { pattern }) - .then(response => { - return response.data.indices; - }); - } -} diff --git a/x-pack/legacy/plugins/watcher/public/services/interval/index.js b/x-pack/legacy/plugins/watcher/public/services/interval/index.js deleted file mode 100644 index 01477b2ec24e3..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/interval/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './xpack_watcher_interval_service.factory'; diff --git a/x-pack/legacy/plugins/watcher/public/services/interval/xpack_watcher_interval_service.factory.js b/x-pack/legacy/plugins/watcher/public/services/interval/xpack_watcher_interval_service.factory.js deleted file mode 100644 index 694639ed0069a..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/interval/xpack_watcher_interval_service.factory.js +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { XpackWatcherIntervalService } from './xpack_watcher_interval_service'; -import { TimeBuckets } from 'ui/time_buckets'; - -uiModules.get('xpack/watcher') - .factory('xpackWatcherIntervalService', () => { - const timeBuckets = new TimeBuckets(); - - return new XpackWatcherIntervalService(timeBuckets); - }); diff --git a/x-pack/legacy/plugins/watcher/public/services/interval/xpack_watcher_interval_service.js b/x-pack/legacy/plugins/watcher/public/services/interval/xpack_watcher_interval_service.js deleted file mode 100644 index c1f543d3e5db0..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/interval/xpack_watcher_interval_service.js +++ /dev/null @@ -1,16 +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 class XpackWatcherIntervalService { - constructor(timeBuckets) { - this.timeBuckets = timeBuckets; - } - - getInterval(input) { - this.timeBuckets.setBounds(input); - return this.timeBuckets.getInterval(); - } -} diff --git a/x-pack/legacy/plugins/watcher/public/services/license/index.js b/x-pack/legacy/plugins/watcher/public/services/license/index.js deleted file mode 100644 index 8be8fb5ccbc64..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/license/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './license_service.factory'; diff --git a/x-pack/legacy/plugins/watcher/public/services/license/license_service.factory.js b/x-pack/legacy/plugins/watcher/public/services/license/license_service.factory.js deleted file mode 100644 index c709fe122cea0..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/license/license_service.factory.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import 'ui/url'; -import { LicenseService } from './license_service'; - -uiModules.get('xpack/watcher') - .factory('xpackWatcherLicenseService', (kbnUrl, $timeout, $http) => - new LicenseService(kbnUrl, $timeout, $http) - ); diff --git a/x-pack/legacy/plugins/watcher/public/services/license/license_service.js b/x-pack/legacy/plugins/watcher/public/services/license/license_service.js deleted file mode 100644 index 42b6c4a3847c0..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/license/license_service.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { toastNotifications } from 'ui/notify'; -import { MarkdownSimple } from 'ui/markdown'; -import chrome from 'ui/chrome'; -import { xpackInfo } from 'plugins/xpack_main/services/xpack_info'; -import { PLUGIN, ROUTES } from '../../../common/constants'; - -export class LicenseService { - constructor(kbnUrlService, $timeout, $http) { - this.$http = $http; - this.kbnUrlService = kbnUrlService; - this.$timeout = $timeout; - this.basePath = chrome.addBasePath(ROUTES.API_ROOT); - } - - get showLinks() { - return Boolean(xpackInfo.get(`features.${PLUGIN.ID}.showLinks`)); - } - - get enableLinks() { - return Boolean(xpackInfo.get(`features.${PLUGIN.ID}.enableLinks`)); - } - - get isAvailable() { - return Boolean(xpackInfo.get(`features.${PLUGIN.ID}.isAvailable`)); - } - - get message() { - return xpackInfo.get(`features.${PLUGIN.ID}.message`); - } - - notifyAndRedirect() { - toastNotifications.addDanger({ - title: {xpackInfo.get(`features.${PLUGIN.ID}.message`)}, - }); - this.kbnUrlService.redirect('/management'); - } - - /** - * @param opts Object options - * @param opts.onValid Function To execute when license is valid. Optional; default = noop - * @param opts.onInvalid Function To execute when license is invalid. Optional; default = noop - */ - checkValidity() { - return new Promise((resolve, reject) => { - this.$timeout(() => { - if (this.isAvailable) { - return resolve(); - } - - this.notifyAndRedirect(); - return reject(); - }, 10); // To allow latest XHR call to update license info - }); - } - - refreshLicense() { - return this.$http.get(`${this.basePath}/license/refresh`) - .then(response => { - return response.data.success; - }); - } -} diff --git a/x-pack/legacy/plugins/watcher/public/services/settings/index.js b/x-pack/legacy/plugins/watcher/public/services/settings/index.js deleted file mode 100644 index 608ed2fbd6902..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/settings/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './settings_service.factory'; diff --git a/x-pack/legacy/plugins/watcher/public/services/settings/settings_service.factory.js b/x-pack/legacy/plugins/watcher/public/services/settings/settings_service.factory.js deleted file mode 100644 index 8ff8a4a99ce29..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/settings/settings_service.factory.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { SettingsService } from './settings_service'; - -uiModules.get('xpack/watcher') - .factory('xpackWatcherSettingsService', ($injector) => { - const $http = $injector.get('$http'); - return new SettingsService($http); - }); diff --git a/x-pack/legacy/plugins/watcher/public/services/settings/settings_service.js b/x-pack/legacy/plugins/watcher/public/services/settings/settings_service.js deleted file mode 100644 index f8ecd33cd662f..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/settings/settings_service.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import chrome from 'ui/chrome'; -import { ROUTES } from '../../../common/constants'; -import { Settings } from 'plugins/watcher/models/settings'; - -export class SettingsService { - constructor($http) { - this.$http = $http; - this.basePath = chrome.addBasePath(ROUTES.API_ROOT); - } - - getSettings() { - return this.$http.get(`${this.basePath}/settings`) - .then(response => { - return Settings.fromUpstreamJson(response.data); - }); - } -} diff --git a/x-pack/legacy/plugins/watcher/public/services/timezone/index.js b/x-pack/legacy/plugins/watcher/public/services/timezone/index.js deleted file mode 100644 index 00ad01e232a32..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/timezone/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './xpack_watcher_timezone_service.factory'; diff --git a/x-pack/legacy/plugins/watcher/public/services/timezone/xpack_watcher_timezone_service.factory.js b/x-pack/legacy/plugins/watcher/public/services/timezone/xpack_watcher_timezone_service.factory.js deleted file mode 100644 index 6d85e7b35bf12..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/timezone/xpack_watcher_timezone_service.factory.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { XpackWatcherTimezoneService } from './xpack_watcher_timezone_service'; - -uiModules.get('xpack/watcher') - .factory('xpackWatcherTimezoneService', ($injector) => { - const config = $injector.get('config'); - return new XpackWatcherTimezoneService(config); - }); diff --git a/x-pack/legacy/plugins/watcher/public/services/timezone/xpack_watcher_timezone_service.js b/x-pack/legacy/plugins/watcher/public/services/timezone/xpack_watcher_timezone_service.js deleted file mode 100644 index 7f93427fdcc69..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/timezone/xpack_watcher_timezone_service.js +++ /dev/null @@ -1,31 +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-timezone'; - -export class XpackWatcherTimezoneService { - constructor(config) { - this.config = config; - } - - getTimezone() { - const DATE_FORMAT_CONFIG_KEY = 'dateFormat:tz'; - const isCustomTimezone = !this.config.isDefault(DATE_FORMAT_CONFIG_KEY); - if (isCustomTimezone) { - return this.config.get(DATE_FORMAT_CONFIG_KEY); - } - - const detectedTimezone = moment.tz.guess(); - if (detectedTimezone) { - return detectedTimezone; - } - - const tzOffset = moment().format('Z'); - return tzOffset; - } -} - - diff --git a/x-pack/legacy/plugins/watcher/public/services/watch/index.js b/x-pack/legacy/plugins/watcher/public/services/watch/index.js deleted file mode 100644 index 809ff6e75abf9..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/watch/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_service.factory'; diff --git a/x-pack/legacy/plugins/watcher/public/services/watch/watch_service.factory.js b/x-pack/legacy/plugins/watcher/public/services/watch/watch_service.factory.js deleted file mode 100644 index a0ad02cbdbf4c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/watch/watch_service.factory.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { WatchService } from './watch_service'; - -uiModules.get('xpack/watcher') - .factory('xpackWatcherWatchService', ($injector) => { - const $http = $injector.get('$http'); - return new WatchService($http); - }); diff --git a/x-pack/legacy/plugins/watcher/public/services/watch/watch_service.js b/x-pack/legacy/plugins/watcher/public/services/watch/watch_service.js deleted file mode 100644 index ca97605688008..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/watch/watch_service.js +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import chrome from 'ui/chrome'; -import { reduce } from 'lodash'; -import { ROUTES, WATCH_TYPES, ACTION_MODES } from '../../../common/constants'; -import { ExecuteDetails } from 'plugins/watcher/models/execute_details'; -import { Watch } from 'plugins/watcher/models/watch'; -import { WatchStatus } from 'plugins/watcher/models/watch_status'; -import { WatchHistoryItem } from 'plugins/watcher/models/watch_history_item'; - -export class WatchService { - constructor($http) { - this.$http = $http; - this.basePath = chrome.addBasePath(ROUTES.API_ROOT); - } - - newWatch(watchType = WATCH_TYPES.JSON) { - const WatchType = Watch.getWatchTypes()[watchType]; - return new WatchType(); - } - - loadWatch(id) { - return this.$http.get(`${this.basePath}/watch/${id}`) - .then(response => { - return Watch.fromUpstreamJson(response.data.watch); - }); - } - - /** - * @param watchId string ID of watch - * @param startTime string Relative representation of start time of watch - * history, e.g. "now-1h" - * @return Promise Array of watch history items - */ - loadWatchHistory(watchId, startTime) { - let url = `${this.basePath}/watch/${watchId}/history`; - if (startTime) { - url += `?startTime=${startTime}`; - } - - return this.$http.get(url) - .then(response => response.data.watchHistoryItems) - .then(watchHistoryItems => watchHistoryItems.map(WatchHistoryItem.fromUpstreamJson)); - } - - saveWatch(watchModel) { - const url = `${this.basePath}/watch/${watchModel.id}`; - - return this.$http.put(url, watchModel.upstreamJson) - .catch(e => { - throw e.data.message; - }); - } - - /** - * @param id string ID of watch to delete - * @return Promise - */ - deleteWatch(id) { - return this.$http.delete(`${this.basePath}/watch/${id}`) - .catch(e => { - throw e.data.message; - }); - } - - /** - * @param id string ID of watch to deactivate - * @return Promise - */ - deactivateWatch(id) { - const url = `${this.basePath}/watch/${id}/deactivate`; - return this.$http.put(url) - .then(response => { - return WatchStatus.fromUpstreamJson(response.data.watchStatus); - }) - .catch(e => { - throw e.data.message; - }); - } - - /** - * @param id string ID of watch to activate - * @return Promise - */ - activateWatch(id) { - const url = `${this.basePath}/watch/${id}/activate`; - return this.$http.put(url) - .then(response => { - return WatchStatus.fromUpstreamJson(response.data.watchStatus); - }) - .catch(e => { - throw e.data.message; - }); - } - - /** - * @param watchId string ID of watch whose action is being acknowledged - * @param actionId string ID of watch action to acknowledge - * @return Promise updated WatchStatus object - */ - acknowledgeWatchAction(watchId, actionId) { - const url = `${this.basePath}/watch/${watchId}/action/${actionId}/acknowledge`; - return this.$http.put(url) - .then(response => { - return WatchStatus.fromUpstreamJson(response.data.watchStatus); - }) - .catch(e => { - throw e.data.message; - }); - } - - /** - * @param executeDetailsModel ExecuteDetailsModel instance with options on how to execute the watch - * @param watchModel Watch instance - * @return Promise which returns a populated WatchHistoryItem on success - */ - executeWatch(executeDetailsModel, watchModel) { - return this.$http.put(`${this.basePath}/watch/execute`, { - executeDetails: executeDetailsModel.upstreamJson, - watch: watchModel.upstreamJson - }) - .then(response => { - return WatchHistoryItem.fromUpstreamJson(response.data.watchHistoryItem); - }) - .catch(e => { - throw e.data.message; - }); - } - - /** - * @param watchModel Watch instance - * @param actionModel Watch instance - * @return Promise which returns a populated WatchHistoryItem on success - */ - simulateWatchAction(watchModel, actionModel) { - const actionModes = reduce(watchModel.actions, (acc, action) => { - acc[action.id] = (action === actionModel) ? - ACTION_MODES.FORCE_EXECUTE : - ACTION_MODES.SKIP; - return acc; - }, {}); - - const executeDetails = new ExecuteDetails({ - triggeredTime: 'now', - scheduledTime: 'now', - ignoreCondition: true, - actionModes, - recordExecution: false - }); - - return this.executeWatch(executeDetails, watchModel); - } - - visualizeWatch(watchModel, visualizeOptions) { - return this.$http.post(`${this.basePath}/watch/visualize`, { - watch: watchModel.upstreamJson, - options: visualizeOptions.upstreamJson - }) - .then(response => { - return response.data; - }) - .catch(e => { - throw e.data.message; - }); - } -} diff --git a/x-pack/legacy/plugins/watcher/public/services/watch_history/index.js b/x-pack/legacy/plugins/watcher/public/services/watch_history/index.js deleted file mode 100644 index d2f41a60adb77..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/watch_history/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './watch_history_service.factory'; diff --git a/x-pack/legacy/plugins/watcher/public/services/watch_history/watch_history_service.factory.js b/x-pack/legacy/plugins/watcher/public/services/watch_history/watch_history_service.factory.js deleted file mode 100644 index acb408b336d59..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/watch_history/watch_history_service.factory.js +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { WatchHistoryService } from './watch_history_service'; - -uiModules.get('xpack/watcher') - .factory('xpackWatcherWatchHistoryService', ($injector) => { - const $http = $injector.get('$http'); - return new WatchHistoryService($http); - }); diff --git a/x-pack/legacy/plugins/watcher/public/services/watch_history/watch_history_service.js b/x-pack/legacy/plugins/watcher/public/services/watch_history/watch_history_service.js deleted file mode 100644 index f684be47efccc..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/watch_history/watch_history_service.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import chrome from 'ui/chrome'; -import { ROUTES } from '../../../common/constants'; -import { WatchHistoryItem } from 'plugins/watcher/models/watch_history_item'; - -export class WatchHistoryService { - constructor($http) { - this.$http = $http; - this.basePath = chrome.addBasePath(ROUTES.API_ROOT); - } - - /** - * @param watchHistoryItemId string ID of watch history item - * @return Promise watch history item - */ - loadWatchHistoryItem(watchHistoryItemId) { - return this.$http.get(`${this.basePath}/history/${watchHistoryItemId}`) - .then(response => { - return WatchHistoryItem.fromUpstreamJson(response.data.watchHistoryItem); - }); - } -} diff --git a/x-pack/legacy/plugins/watcher/public/services/watches/index.js b/x-pack/legacy/plugins/watcher/public/services/watches/index.js deleted file mode 100644 index be8896ec5c9f6..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/watches/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { watches } from './watches'; diff --git a/x-pack/legacy/plugins/watcher/public/services/watches/watches.js b/x-pack/legacy/plugins/watcher/public/services/watches/watches.js deleted file mode 100644 index 9d05920bcee6c..0000000000000 --- a/x-pack/legacy/plugins/watcher/public/services/watches/watches.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Watch } from 'plugins/watcher/models/watch'; -import { ROUTES } from '../../../common/constants'; -import { npStart } from 'ui/new_platform'; -const { http } = npStart.core; - -export const watches = { - async getWatchList() { - const { watches } = await http.get(`${ROUTES.API_ROOT}/watches`); - return watches.map(Watch.fromUpstreamJson); - }, - - /** - * Delete a collection of watches - * - * @param watchIds Array of watch IDs - * @return Promise { numSuccesses, numErrors } - */ - async deleteWatches(watchIds) { - // $http.delete does not take the request body as the 2nd argument. Instead it expects the 2nd - // argument to be a request options object, one of which can be the request body (data). We also - // need to explicitly define the content type of the data. - const { results } = await http.post(`${ROUTES.API_ROOT}/watches/delete`, { - body: JSON.stringify({ watchIds }) - }); - return results; - } -}; diff --git a/x-pack/legacy/plugins/watcher/server/lib/check_license/__tests__/check_license.js b/x-pack/legacy/plugins/watcher/server/lib/check_license/__tests__/check_license.js deleted file mode 100644 index 3dc2eced25a8e..0000000000000 --- a/x-pack/legacy/plugins/watcher/server/lib/check_license/__tests__/check_license.js +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { set } from 'lodash'; -import { checkLicense } from '../check_license'; - -describe('check_license', function () { - - let mockLicenseInfo; - beforeEach(() => mockLicenseInfo = {}); - - describe('license information is undefined', () => { - beforeEach(() => mockLicenseInfo = undefined); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set showLinks to true', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is not available', () => { - beforeEach(() => mockLicenseInfo.isAvailable = () => false); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it('should set showLinks to true', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); - }); - - it('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('license information is available', () => { - beforeEach(() => { - mockLicenseInfo.isAvailable = () => true; - set(mockLicenseInfo, 'license.getType', () => 'basic'); - }); - - describe('& license is trial, standard, gold, platinum', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isOneOf', () => true)); - - describe('& license is active', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - - it('should set isAvailable to true', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); - }); - - it ('should set showLinks to true', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); - }); - - it ('should set enableLinks to true', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); - }); - - it('should not set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.be(undefined); - }); - }); - - describe('& license is expired', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it ('should set showLinks to true', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); - }); - - it ('should set enableLinks to false', () => { - expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); - - describe('& license is basic', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isOneOf', () => false)); - - describe('& license is active', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it ('should set showLinks to false', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - - describe('& license is expired', () => { - beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false)); - - it('should set isAvailable to false', () => { - expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); - }); - - it ('should set showLinks to false', () => { - expect(checkLicense(mockLicenseInfo).showLinks).to.be(false); - }); - - it('should set a message', () => { - expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined); - }); - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/watcher/server/lib/check_license/check_license.js b/x-pack/legacy/plugins/watcher/server/lib/check_license/check_license.js deleted file mode 100644 index 2b318c4ddd885..0000000000000 --- a/x-pack/legacy/plugins/watcher/server/lib/check_license/check_license.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; - -export function checkLicense(xpackLicenseInfo) { - // If, for some reason, we cannot get the license information - // from Elasticsearch, assume worst case and disable the Watcher UI - if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { - return { - isAvailable: false, - showLinks: true, - enableLinks: false, - message: i18n.translate('xpack.watcher.checkLicense.licenseInformationNotAvailableTextMessage', { - defaultMessage: 'You cannot use {watcher} because license information is not available at this time.', - values: { - watcher: 'Watcher' - } - }), - }; - } - - const VALID_LICENSE_MODES = [ - 'trial', - 'standard', - 'gold', - 'platinum' - ]; - - const isLicenseModeValid = xpackLicenseInfo.license.isOneOf(VALID_LICENSE_MODES); - const isLicenseActive = xpackLicenseInfo.license.isActive(); - const licenseType = xpackLicenseInfo.license.getType(); - - // License is not valid - if (!isLicenseModeValid) { - return { - isAvailable: false, - showLinks: false, - message: i18n.translate('xpack.watcher.checkLicense.licenseDoesNotSupportTextMessage', { - defaultMessage: 'Your {licenseType} license does not support {watcher}. Please upgrade your license.', - values: { - licenseType, - watcher: 'Watcher' - }, - }), - }; - } - - // License is valid but not active - if (!isLicenseActive) { - return { - isAvailable: false, - showLinks: true, - enableLinks: false, - message: i18n.translate('xpack.watcher.server.checkLicense.licenseExpiredTextMessage', { - defaultMessage: 'You cannot use {watcher} because your {licenseType} license has expired', - values: { - licenseType, - watcher: 'Watcher' - }, - }), - }; - } - - // License is valid and active - return { - isAvailable: true, - showLinks: true, - enableLinks: true - }; -} diff --git a/x-pack/legacy/plugins/watcher/server/lib/check_license/index.js b/x-pack/legacy/plugins/watcher/server/lib/check_license/index.js deleted file mode 100644 index f2c070fd44b6e..0000000000000 --- a/x-pack/legacy/plugins/watcher/server/lib/check_license/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { checkLicense } from './check_license'; diff --git a/x-pack/legacy/plugins/watcher/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js b/x-pack/legacy/plugins/watcher/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js index 24cbaae2b2b67..ed4a51a11b7cd 100644 --- a/x-pack/legacy/plugins/watcher/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js +++ b/x-pack/legacy/plugins/watcher/server/lib/license_pre_routing_factory/__tests__/license_pre_routing_factory.js @@ -6,6 +6,7 @@ import expect from '@kbn/expect'; import { licensePreRoutingFactory } from '../license_pre_routing_factory'; +import { LICENSE_STATUS_VALID, LICENSE_STATUS_EXPIRED } from '../../../../../../common/constants/license_status'; describe('license_pre_routing_factory', () => { describe('#reportingFeaturePreRoutingFactory', () => { @@ -33,10 +34,10 @@ describe('license_pre_routing_factory', () => { expect(firstInstance).to.be(secondInstance); }); - describe('isAvailable is false', () => { + describe('status is not valid', () => { beforeEach(() => { mockLicenseCheckResults = { - isAvailable: false + status: LICENSE_STATUS_EXPIRED }; }); @@ -51,10 +52,10 @@ describe('license_pre_routing_factory', () => { }); }); - describe('isAvailable is true', () => { + describe('status is valid', () => { beforeEach(() => { mockLicenseCheckResults = { - isAvailable: true + status: LICENSE_STATUS_VALID }; }); diff --git a/x-pack/legacy/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.js b/x-pack/legacy/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.js index 11e01304b6e5c..5b34108c9c1c0 100644 --- a/x-pack/legacy/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.js +++ b/x-pack/legacy/plugins/watcher/server/lib/license_pre_routing_factory/license_pre_routing_factory.js @@ -7,6 +7,7 @@ import { once } from 'lodash'; import { wrapCustomError } from '../error_wrappers'; import { PLUGIN } from '../../../common/constants'; +import { LICENSE_STATUS_VALID } from '../../../../../common/constants/license_status'; export const licensePreRoutingFactory = once((server) => { const xpackMainPlugin = server.plugins.xpack_main; @@ -14,7 +15,9 @@ export const licensePreRoutingFactory = once((server) => { // License checking and enable/disable logic function licensePreRouting() { const licenseCheckResults = xpackMainPlugin.info.feature(PLUGIN.ID).getLicenseCheckResults(); - if (!licenseCheckResults.isAvailable) { + const { status } = licenseCheckResults; + + if (status !== LICENSE_STATUS_VALID) { const error = new Error(licenseCheckResults.message); const statusCode = 403; throw wrapCustomError(error, statusCode); diff --git a/x-pack/legacy/plugins/watcher/server/lib/register_license_checker/index.js b/x-pack/legacy/plugins/watcher/server/lib/register_license_checker/index.js deleted file mode 100644 index 7b0f97c38d129..0000000000000 --- a/x-pack/legacy/plugins/watcher/server/lib/register_license_checker/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { registerLicenseChecker } from './register_license_checker'; diff --git a/x-pack/legacy/plugins/watcher/server/lib/register_license_checker/register_license_checker.js b/x-pack/legacy/plugins/watcher/server/lib/register_license_checker/register_license_checker.js deleted file mode 100644 index b05946e60b330..0000000000000 --- a/x-pack/legacy/plugins/watcher/server/lib/register_license_checker/register_license_checker.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status'; -import { checkLicense } from '../check_license'; -import { PLUGIN } from '../../../common/constants'; - -export function registerLicenseChecker(server) { - const xpackMainPlugin = server.plugins.xpack_main; - const watcherPlugin = server.plugins.watcher; - - mirrorPluginStatus(xpackMainPlugin, watcherPlugin); - xpackMainPlugin.status.once('green', () => { - // Register a function that is called whenever the xpack info changes, - // to re-compute the license check results for this plugin - xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense); - }); -} diff --git a/x-pack/legacy/plugins/watcher/server/models/action/action.js b/x-pack/legacy/plugins/watcher/server/models/action/action.js index 65c3f2b44c26c..09bc3316dcb36 100644 --- a/x-pack/legacy/plugins/watcher/server/models/action/action.js +++ b/x-pack/legacy/plugins/watcher/server/models/action/action.js @@ -11,6 +11,10 @@ import { ACTION_TYPES } from '../../../common/constants'; import { LoggingAction } from './logging_action'; import { EmailAction } from './email_action'; import { SlackAction } from './slack_action'; +import { IndexAction } from './index_action'; +import { WebhookAction } from './webhook_action'; +import { PagerDutyAction } from './pagerduty_action'; +import { JiraAction } from './jira_action'; import { UnknownAction } from './unknown_action'; import { i18n } from '@kbn/i18n'; @@ -18,6 +22,10 @@ const ActionTypes = {}; set(ActionTypes, ACTION_TYPES.LOGGING, LoggingAction); set(ActionTypes, ACTION_TYPES.EMAIL, EmailAction); set(ActionTypes, ACTION_TYPES.SLACK, SlackAction); +set(ActionTypes, ACTION_TYPES.INDEX, IndexAction); +set(ActionTypes, ACTION_TYPES.WEBHOOK, WebhookAction); +set(ActionTypes, ACTION_TYPES.PAGERDUTY, PagerDutyAction); +set(ActionTypes, ACTION_TYPES.JIRA, JiraAction); set(ActionTypes, ACTION_TYPES.UNKNOWN, UnknownAction); export class Action { @@ -30,7 +38,7 @@ export class Action { if (!json.id) { throw badRequest( i18n.translate('xpack.watcher.models.actionStatus.idPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {id} property', + defaultMessage: 'JSON argument must contain an {id} property', values: { id: 'id' } @@ -41,7 +49,7 @@ export class Action { if (!json.actionJson) { throw badRequest( i18n.translate('xpack.watcher.models.action.actionJsonPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {actionJson} property', + defaultMessage: 'JSON argument must contain an {actionJson} property', values: { actionJson: 'actionJson' } diff --git a/x-pack/legacy/plugins/watcher/server/models/action/action.test.js b/x-pack/legacy/plugins/watcher/server/models/action/action.test.js index d3d8abc73a62e..d41844017e298 100644 --- a/x-pack/legacy/plugins/watcher/server/models/action/action.test.js +++ b/x-pack/legacy/plugins/watcher/server/models/action/action.test.js @@ -39,14 +39,14 @@ describe('action', () => { delete upstreamJson.id; expect(() => { Action.fromUpstreamJson(upstreamJson); - }).toThrowError(/must contain an id property/i); + }).toThrowError('JSON argument must contain an id property'); }); it(`throws an error if no 'actionJson' property in json`, () => { delete upstreamJson.actionJson; expect(() => { Action.fromUpstreamJson(upstreamJson); - }).toThrowError(/must contain an actionJson property/i); + }).toThrowError('JSON argument must contain an actionJson property'); }); it(`throws an error if an Action is invalid`, () => { diff --git a/x-pack/legacy/plugins/watcher/server/models/action/base_action.js b/x-pack/legacy/plugins/watcher/server/models/action/base_action.js index 0500a4a451ac8..31d8b39b5b1c0 100644 --- a/x-pack/legacy/plugins/watcher/server/models/action/base_action.js +++ b/x-pack/legacy/plugins/watcher/server/models/action/base_action.js @@ -38,7 +38,7 @@ export class BaseAction { if (!json.id) { throw badRequest( i18n.translate('xpack.watcher.models.baseAction.idPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {id} property', + defaultMessage: 'JSON argument must contain an {id} property', values: { id: 'id' } diff --git a/x-pack/legacy/plugins/watcher/server/models/action/email_action.js b/x-pack/legacy/plugins/watcher/server/models/action/email_action.js index e2151b207a842..42428ddfc6908 100644 --- a/x-pack/legacy/plugins/watcher/server/models/action/email_action.js +++ b/x-pack/legacy/plugins/watcher/server/models/action/email_action.js @@ -98,7 +98,7 @@ export class EmailAction extends BaseAction { if (!json.email) { const message = i18n.translate('xpack.watcher.models.emailAction.actionJsonEmailPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {actionJsonEmail} property', + defaultMessage: 'JSON argument must contain an {actionJsonEmail} property', values: { actionJsonEmail: 'actionJson.email' } @@ -114,7 +114,7 @@ export class EmailAction extends BaseAction { if (!json.email.to) { const message = i18n.translate('xpack.watcher.models.emailAction.actionJsonEmailToPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {actionJsonEmailTo} property', + defaultMessage: 'JSON argument must contain an {actionJsonEmailTo} property', values: { actionJsonEmailTo: 'actionJson.email.to' } diff --git a/x-pack/legacy/plugins/watcher/server/models/action/index_action.js b/x-pack/legacy/plugins/watcher/server/models/action/index_action.js new file mode 100644 index 0000000000000..5bfd4335067ec --- /dev/null +++ b/x-pack/legacy/plugins/watcher/server/models/action/index_action.js @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { BaseAction } from './base_action'; +import { ACTION_TYPES, ERROR_CODES } from '../../../common/constants'; +import { i18n } from '@kbn/i18n'; + +export class IndexAction extends BaseAction { + constructor(props, errors) { + props.type = ACTION_TYPES.INDEX; + super(props, errors); + + this.index = props.index; + } + + // To Kibana + get downstreamJson() { + const result = super.downstreamJson; + Object.assign(result, { + index: this.index + }); + + return result; + } + + // From Kibana + static fromDownstreamJson(json) { + const props = super.getPropsFromDownstreamJson(json); + const { errors } = this.validateJson(json); + + Object.assign(props, { + index: json.index + }); + + const action = new IndexAction(props, errors); + return { action, errors }; + } + + // To Elasticsearch + get upstreamJson() { + const result = super.upstreamJson; + + result[this.id] = { + index: this.index, + }; + return result; + } + + // From Elasticsearch + static fromUpstreamJson(json) { + const props = super.getPropsFromUpstreamJson(json); + const { errors } = this.validateJson(json.actionJson); + + Object.assign(props, { + index: json.actionJson.index.index + }); + + const action = new IndexAction(props, errors); + return { action, errors }; + } + + static validateJson(json) { + const errors = []; + + if (!json.index) { + errors.push({ + code: ERROR_CODES.ERR_PROP_MISSING, + message: i18n.translate('xpack.watcher.models.indexAction.actionJsonIndexPropertyMissingBadRequestMessage', { + defaultMessage: 'JSON argument must contain an {actionJsonIndex} property', + values: { + actionJsonIndex: 'actionJson.index' + } + }), + }); + + json.index = {}; + } + + if (!json.index.index) { + errors.push({ + code: ERROR_CODES.ERR_PROP_MISSING, + message: i18n.translate('xpack.watcher.models.loggingAction.actionJsonIndexNamePropertyMissingBadRequestMessage', { + defaultMessage: 'JSON argument must contain an {actionJsonIndexName} property', + values: { + actionJsonIndexName: 'actionJson.index.index' + } + }), + }); + } + + return { errors: errors.length ? errors : null }; + } + +} diff --git a/x-pack/legacy/plugins/watcher/server/models/action/jira_action.js b/x-pack/legacy/plugins/watcher/server/models/action/jira_action.js new file mode 100644 index 0000000000000..0a53258fe1bb6 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/server/models/action/jira_action.js @@ -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 { BaseAction } from './base_action'; +import { ACTION_TYPES, ERROR_CODES } from '../../../common/constants'; +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; + +export class JiraAction extends BaseAction { + constructor(props, errors) { + props.type = ACTION_TYPES.JIRA; + super(props, errors); + + this.projectKey = props.projectKey; + this.issueType = props.issueType; + this.summary = props.summary; + } + + // To Kibana + get downstreamJson() { + const result = super.downstreamJson; + Object.assign(result, { + projectKey: this.projectKey, + issueType: this.issueType, + summary: this.summary, + }); + + return result; + } + + // From Kibana + static fromDownstreamJson(json) { + const props = super.getPropsFromDownstreamJson(json); + const { errors } = this.validateJson(json); + + Object.assign(props, { + projectKey: json.projectKey, + issueType: json.issueType, + summary: json.summary, + }); + + const action = new JiraAction(props, errors); + return { action, errors }; + } + + // To Elasticsearch + get upstreamJson() { + const result = super.upstreamJson; + + result[this.id] = { + jira: { + fields: { + project: { + key: this.projectKey, + }, + issuetype: { + name: this.issueType, + }, + summary: this.summary, + }, + } + }; + + return result; + } + + // From Elasticsearch + static fromUpstreamJson(json) { + const props = super.getPropsFromUpstreamJson(json); + const { errors } = this.validateJson(json.actionJson); + + Object.assign(props, { + projectKey: get(json, 'actionJson.jira.fields.project.key'), + issueType: get(json, 'actionJson.jira.fields.issuetype.name'), + summary: get(json, 'actionJson.jira.fields.summary'), + }); + + const action = new JiraAction(props, errors); + return { action, errors }; + } + + static validateJson(json) { + const errors = []; + + if (!json.jira) { + errors.push({ + code: ERROR_CODES.ERR_PROP_MISSING, + message: i18n.translate('xpack.watcher.models.jiraAction.actionJsonJiraPropertyMissingBadRequestMessage', { + defaultMessage: 'JSON argument must contain an {actionJsonJira} property', + values: { + actionJsonJira: 'actionJson.jira' + } + }), + }); + + json.jira = {}; + } + + if (!get(json, 'jira.fields.project.key')) { + errors.push({ + code: ERROR_CODES.ERR_PROP_MISSING, + message: i18n.translate('xpack.watcher.models.jiraAction.actionJsonJiraProjectKeyPropertyMissingBadRequestMessage', { + defaultMessage: 'JSON argument must contain an {actionJsonJiraProjectKey} property', + values: { + actionJsonJiraProjectKey: 'actionJson.jira.fields.project.key' + } + }), + }); + } + + if (!get(json, 'jira.fields.issuetype.name')) { + errors.push({ + code: ERROR_CODES.ERR_PROP_MISSING, + message: i18n.translate('xpack.watcher.models.jiraAction.actionJsonJiraIssueTypePropertyMissingBadRequestMessage', { + defaultMessage: 'JSON argument must contain an {actionJsonJiraIssueType} property', + values: { + actionJsonJiraIssueType: 'actionJson.jira.fields.issuetype.name' + } + }), + }); + } + + if (!json.jira.fields.summary) { + errors.push({ + code: ERROR_CODES.ERR_PROP_MISSING, + message: i18n.translate('xpack.watcher.models.jiraAction.actionJsonJiraSummaryPropertyMissingBadRequestMessage', { + defaultMessage: 'JSON argument must contain an {actionJsonJiraSummary} property', + values: { + actionJsonJiraSummary: 'actionJson.jira.fields.summary' + } + }), + }); + } + + return { errors: errors.length ? errors : null }; + } +} diff --git a/x-pack/legacy/plugins/watcher/server/models/action/logging_action.js b/x-pack/legacy/plugins/watcher/server/models/action/logging_action.js index b65288ef87f83..92ba94dca84ff 100644 --- a/x-pack/legacy/plugins/watcher/server/models/action/logging_action.js +++ b/x-pack/legacy/plugins/watcher/server/models/action/logging_action.js @@ -72,7 +72,7 @@ export class LoggingAction extends BaseAction { errors.push({ code: ERROR_CODES.ERR_PROP_MISSING, message: i18n.translate('xpack.watcher.models.loggingAction.actionJsonLoggingPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {actionJsonLogging} property', + defaultMessage: 'JSON argument must contain an {actionJsonLogging} property', values: { actionJsonLogging: 'actionJson.logging' } @@ -86,7 +86,7 @@ export class LoggingAction extends BaseAction { errors.push({ code: ERROR_CODES.ERR_PROP_MISSING, message: i18n.translate('xpack.watcher.models.loggingAction.actionJsonLoggingTextPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {actionJsonLoggingText} property', + defaultMessage: 'JSON argument must contain an {actionJsonLoggingText} property', values: { actionJsonLoggingText: 'actionJson.logging.text' } diff --git a/x-pack/legacy/plugins/watcher/server/models/action/pagerduty_action.js b/x-pack/legacy/plugins/watcher/server/models/action/pagerduty_action.js new file mode 100644 index 0000000000000..edfdb33584170 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/server/models/action/pagerduty_action.js @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { BaseAction } from './base_action'; +import { ACTION_TYPES, ERROR_CODES } from '../../../common/constants'; +import { i18n } from '@kbn/i18n'; + +export class PagerDutyAction extends BaseAction { + constructor(props, errors) { + props.type = ACTION_TYPES.PAGERDUTY; + super(props, errors); + + this.description = props.description; + } + + // To Kibana + get downstreamJson() { + const result = super.downstreamJson; + Object.assign(result, { + description: this.description + }); + + return result; + } + + // From Kibana + static fromDownstreamJson(json) { + const props = super.getPropsFromDownstreamJson(json); + const { errors } = this.validateJson(json); + + Object.assign(props, { + description: json.description + }); + + const action = new PagerDutyAction(props, errors); + return { action, errors }; + } + + // To Elasticsearch + get upstreamJson() { + const result = super.upstreamJson; + + result[this.id] = { + pagerduty: { + description: this.description + } + }; + + return result; + } + + // From Elasticsearch + static fromUpstreamJson(json) { + const props = super.getPropsFromUpstreamJson(json); + const { errors } = this.validateJson(json.actionJson); + + Object.assign(props, { + description: json.actionJson.pagerduty.description + }); + + const action = new PagerDutyAction(props, errors); + return { action, errors }; + } + + static validateJson(json) { + const errors = []; + + if (!json.pagerduty) { + errors.push({ + code: ERROR_CODES.ERR_PROP_MISSING, + message: i18n.translate('xpack.watcher.models.pagerDutyAction.actionJsonPagerDutyPropertyMissingBadRequestMessage', { + defaultMessage: 'JSON argument must contain an {actionJsonPagerDuty} property', + values: { + actionJsonPagerDuty: 'actionJson.pagerduty' + } + }), + }); + + json.pagerduty = {}; + } + + if (!json.pagerduty.description) { + errors.push({ + code: ERROR_CODES.ERR_PROP_MISSING, + message: i18n.translate('xpack.watcher.models.pagerDutyAction.actionJsonPagerDutyDescriptionPropertyMissingBadRequestMessage', { + defaultMessage: 'JSON argument must contain an {actionJsonPagerDutyText} property', + values: { + actionJsonPagerDutyText: 'actionJson.pagerduty.description' + } + }), + }); + } + + return { errors: errors.length ? errors : null }; + } +} diff --git a/x-pack/legacy/plugins/watcher/server/models/action/slack_action.js b/x-pack/legacy/plugins/watcher/server/models/action/slack_action.js index 6c76ed6ebff3d..4441a71eacae9 100644 --- a/x-pack/legacy/plugins/watcher/server/models/action/slack_action.js +++ b/x-pack/legacy/plugins/watcher/server/models/action/slack_action.js @@ -81,7 +81,7 @@ export class SlackAction extends BaseAction { errors.push({ code: ERROR_CODES.ERR_PROP_MISSING, message: i18n.translate('xpack.watcher.models.slackAction.actionJsonSlackPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {actionJsonSlack} property', + defaultMessage: 'JSON argument must contain an {actionJsonSlack} property', values: { actionJsonSlack: 'actionJson.slack' } @@ -95,7 +95,7 @@ export class SlackAction extends BaseAction { errors.push({ code: ERROR_CODES.ERR_PROP_MISSING, message: i18n.translate('xpack.watcher.models.slackAction.actionJsonSlackMessagePropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {actionJsonSlackMessage} property', + defaultMessage: 'JSON argument must contain an {actionJsonSlackMessage} property', values: { actionJsonSlackMessage: 'actionJson.slack.message' } diff --git a/x-pack/legacy/plugins/watcher/server/models/action/unknown_action.js b/x-pack/legacy/plugins/watcher/server/models/action/unknown_action.js index 858b14472a9d1..40324a9ab2882 100644 --- a/x-pack/legacy/plugins/watcher/server/models/action/unknown_action.js +++ b/x-pack/legacy/plugins/watcher/server/models/action/unknown_action.js @@ -68,7 +68,7 @@ export class UnknownAction extends BaseAction { errors.push({ code: ERROR_CODES.ERR_PROP_MISSING, message: i18n.translate('xpack.watcher.models.unknownAction.actionJsonPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {actionJson} property', + defaultMessage: 'JSON argument must contain an {actionJson} property', values: { actionJson: 'actionJson' } diff --git a/x-pack/legacy/plugins/watcher/server/models/action/webhook_action.js b/x-pack/legacy/plugins/watcher/server/models/action/webhook_action.js new file mode 100644 index 0000000000000..766ccd5371533 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/server/models/action/webhook_action.js @@ -0,0 +1,176 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { BaseAction } from './base_action'; +import { ACTION_TYPES, ERROR_CODES } from '../../../common/constants'; +import { i18n } from '@kbn/i18n'; + +export class WebhookAction extends BaseAction { + constructor(props, errors) { + props.type = ACTION_TYPES.WEBHOOK; + super(props, errors); + + this.method = props.method; + this.host = props.host; + this.port = props.port; + this.path = props.path; + this.body = props.body; + this.contentType = props.contentType; + this.username = props.username; + this.password = props.password; + } + + // To Kibana + get downstreamJson() { + const result = super.downstreamJson; + Object.assign(result, { + method: this.method, + host: this.host, + port: this.port, + path: this.path, + body: this.body, + contentType: this.contentType, + username: this.username, + }); + return result; + } + + // From Kibana + static fromDownstreamJson(json) { + const props = super.getPropsFromDownstreamJson(json); + const { errors } = this.validateJson(json); + + Object.assign(props, { + method: json.method, + host: json.host, + port: json.port, + path: json.path, + body: json.body, + contentType: json.contentType, + username: json.username, + password: json.password, + }); + + const action = new WebhookAction(props, errors); + return { action, errors }; + } + + // To Elasticsearch + get upstreamJson() { + const result = super.upstreamJson; + + const optionalFields = {}; + + if (this.path) { + optionalFields.path = this.path; + } + + if (this.method) { + optionalFields.method = this.method; + } + + if (this.body) { + optionalFields.body = this.body; + } + + if (this.contentType) { + optionalFields.headers = { + 'Content-Type': this.contentType, + }; + } + + if (this.username && this.password) { + optionalFields.auth = { + basic: { + username: this.username, + password: this.password, + }, + }; + } + + result[this.id] = { + webhook: { + host: this.host, + port: this.port, + ...optionalFields, + } + }; + + return result; + } + + // From Elasticsearch + static fromUpstreamJson(json) { + const props = super.getPropsFromUpstreamJson(json); + const webhookJson = json && json.actionJson && json.actionJson.webhook; + const { errors } = this.validateJson(json.actionJson); + + const { path, method, body, auth, headers } = webhookJson; + + const optionalFields = {}; + + if (path) { + optionalFields.path = path; + } + + if (method) { + optionalFields.method = method; + } + + if (body) { + optionalFields.body = body; + } + + if (headers['Content-Type']) { + optionalFields.contentType = headers['Content-Type']; + } + + if (auth && auth.basic) { + optionalFields.username = auth.basic.username; + } + + Object.assign(props, { + host: json.actionJson.webhook.host, + port: json.actionJson.webhook.port, + ...optionalFields, + }); + + const action = new WebhookAction(props, errors); + return { action, errors }; + } + + static validateJson(json) { + const errors = []; + + if (!json.webhook.host) { + errors.push({ + code: ERROR_CODES.ERR_PROP_MISSING, + message: i18n.translate('xpack.watcher.models.loggingAction.actionJsonWebhookHostPropertyMissingBadRequestMessage', { + defaultMessage: 'JSON argument must contain an {actionJsonWebhookHost} property', + values: { + actionJsonWebhookHost: 'actionJson.webhook.host' + } + }), + }); + json.webhook = {}; + } + + if (!json.webhook.port) { + errors.push({ + code: ERROR_CODES.ERR_PROP_MISSING, + message: i18n.translate('xpack.watcher.models.loggingAction.actionJsonWebhookPortPropertyMissingBadRequestMessage', { + defaultMessage: 'JSON argument must contain an {actionJsonWebhookPort} property', + values: { + actionJsonWebhookPort: 'actionJson.webhook.port' + } + }), + }); + } + + return { errors: errors.length ? errors : null }; + } + +} diff --git a/x-pack/legacy/plugins/watcher/server/models/action_status/__tests__/action_status.js b/x-pack/legacy/plugins/watcher/server/models/action_status/__tests__/action_status.js index 911c960efdab5..456768c8c02ec 100644 --- a/x-pack/legacy/plugins/watcher/server/models/action_status/__tests__/action_status.js +++ b/x-pack/legacy/plugins/watcher/server/models/action_status/__tests__/action_status.js @@ -43,13 +43,13 @@ describe('action_status', () => { it(`throws an error if no 'id' property in json`, () => { delete upstreamJson.id; expect(ActionStatus.fromUpstreamJson).withArgs(upstreamJson) - .to.throwError(/must contain an id property/i); + .to.throwError('JSON argument must contain an "id" property'); }); it(`throws an error if no 'actionStatusJson' property in json`, () => { delete upstreamJson.actionStatusJson; expect(ActionStatus.fromUpstreamJson).withArgs(upstreamJson) - .to.throwError(/must contain an actionStatusJson property/i); + .to.throwError('JSON argument must contain an "actionStatusJson" property'); }); it('returns correct ActionStatus instance', () => { diff --git a/x-pack/legacy/plugins/watcher/server/models/action_status/action_status.js b/x-pack/legacy/plugins/watcher/server/models/action_status/action_status.js index 89e5fff535e5e..eeedf9aefe5f6 100644 --- a/x-pack/legacy/plugins/watcher/server/models/action_status/action_status.js +++ b/x-pack/legacy/plugins/watcher/server/models/action_status/action_status.js @@ -101,25 +101,20 @@ export class ActionStatus { // generate object from elasticsearch response static fromUpstreamJson(json) { + const missingPropertyError = missingProperty => i18n.translate( + 'xpack.watcher.models.actionStatus.actionStatusJsonPropertyMissingBadRequestMessage', + { + defaultMessage: 'JSON argument must contain an "{missingProperty}" property', + values: { missingProperty } + } + ); + if (!json.id) { - throw badRequest( - i18n.translate('xpack.watcher.models.actionStatus.idPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {id} property', - values: { - id: 'id' - } - }), - ); + throw badRequest(missingPropertyError('id')); } + if (!json.actionStatusJson) { - throw badRequest( - i18n.translate('xpack.watcher.models.actionStatus.actionStatusJsonPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {actionStatusJson} property', - values: { - actionStatusJson: 'actionStatusJson' - } - }), - ); + throw badRequest(missingPropertyError('actionStatusJson')); } return new ActionStatus(json); diff --git a/x-pack/legacy/plugins/watcher/server/models/execute_details/execute_details.js b/x-pack/legacy/plugins/watcher/server/models/execute_details/execute_details.js index 2c67c4896cbcd..229bd29f07e2d 100644 --- a/x-pack/legacy/plugins/watcher/server/models/execute_details/execute_details.js +++ b/x-pack/legacy/plugins/watcher/server/models/execute_details/execute_details.js @@ -18,7 +18,7 @@ export class ExecuteDetails { get upstreamJson() { const triggerData = { triggered_time: this.triggerData.triggeredTime, - scheduled_time: this.triggerData.scheduledTime + scheduled_time: this.triggerData.scheduledTime, }; const result = { diff --git a/x-pack/legacy/plugins/watcher/server/models/fields/fields.js b/x-pack/legacy/plugins/watcher/server/models/fields/fields.js index ba3ed23711457..fef932ee630b7 100644 --- a/x-pack/legacy/plugins/watcher/server/models/fields/fields.js +++ b/x-pack/legacy/plugins/watcher/server/models/fields/fields.js @@ -55,7 +55,7 @@ export class Fields { if (!json.fields) { throw badRequest( i18n.translate('xpack.watcher.models.fields.fieldsPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain a {fields} property', + defaultMessage: 'JSON argument must contain a {fields} property', values: { fields: 'fields' } diff --git a/x-pack/legacy/plugins/watcher/server/models/settings/__tests__/settings.js b/x-pack/legacy/plugins/watcher/server/models/settings/__tests__/settings.js index 7add7a0e0564e..9757bc84bc888 100644 --- a/x-pack/legacy/plugins/watcher/server/models/settings/__tests__/settings.js +++ b/x-pack/legacy/plugins/watcher/server/models/settings/__tests__/settings.js @@ -19,8 +19,7 @@ describe('settings module', () => { expect(actionTypes.webhook.enabled).to.be(true); expect(actionTypes.index.enabled).to.be(true); expect(actionTypes.logging.enabled).to.be(true); - expect(actionTypes.hipchat.enabled).to.be(false); - expect(actionTypes.slack.enabled).to.be(true); + expect(actionTypes.slack.enabled).to.be(false); expect(actionTypes.jira.enabled).to.be(false); expect(actionTypes.pagerduty.enabled).to.be(false); }); @@ -94,8 +93,7 @@ describe('settings module', () => { expect(json.action_types.webhook.enabled).to.be(true); expect(json.action_types.index.enabled).to.be(true); expect(json.action_types.logging.enabled).to.be(true); - expect(json.action_types.hipchat.enabled).to.be(false); - expect(json.action_types.slack.enabled).to.be(true); + expect(json.action_types.slack.enabled).to.be(false); expect(json.action_types.jira.enabled).to.be(false); expect(json.action_types.pagerduty.enabled).to.be(false); }); diff --git a/x-pack/legacy/plugins/watcher/server/models/settings/settings.js b/x-pack/legacy/plugins/watcher/server/models/settings/settings.js index 91970573f795f..95a1db7533f41 100644 --- a/x-pack/legacy/plugins/watcher/server/models/settings/settings.js +++ b/x-pack/legacy/plugins/watcher/server/models/settings/settings.js @@ -12,7 +12,6 @@ function isEnabledByDefault(actionType) { case ACTION_TYPES.WEBHOOK: case ACTION_TYPES.INDEX: case ACTION_TYPES.LOGGING: - case ACTION_TYPES.SLACK: // https://github.com/elastic/x-pack-elasticsearch/issues/1573 return true; default: return false; @@ -22,8 +21,7 @@ function isEnabledByDefault(actionType) { function requiresAccountInfo(actionType) { switch (actionType) { case ACTION_TYPES.EMAIL: - case ACTION_TYPES.HIPCHAT: - // case ACTION_TYPES.SLACK: // https://github.com/elastic/x-pack-elasticsearch/issues/1573 + case ACTION_TYPES.SLACK: case ACTION_TYPES.JIRA: case ACTION_TYPES.PAGERDUTY: return true; diff --git a/x-pack/legacy/plugins/watcher/server/models/watch/__tests__/threshold_watch.js b/x-pack/legacy/plugins/watcher/server/models/watch/__tests__/threshold_watch.js index a93280d5c6aa8..cdbe311a8154a 100644 --- a/x-pack/legacy/plugins/watcher/server/models/watch/__tests__/threshold_watch.js +++ b/x-pack/legacy/plugins/watcher/server/models/watch/__tests__/threshold_watch.js @@ -387,7 +387,7 @@ describe('ThresholdWatch', () => { thresholdComparator: 'thresholdComparator', timeWindowSize: 'timeWindowSize', timeWindowUnit: 'timeWindowUnit', - threshold: 'threshold' + threshold: ['threshold'] }; expect(actual).to.eql(expected); diff --git a/x-pack/legacy/plugins/watcher/server/models/watch/base_watch.js b/x-pack/legacy/plugins/watcher/server/models/watch/base_watch.js index bfcc8737cb190..ada4cbc2dd2b2 100644 --- a/x-pack/legacy/plugins/watcher/server/models/watch/base_watch.js +++ b/x-pack/legacy/plugins/watcher/server/models/watch/base_watch.js @@ -94,7 +94,7 @@ export class BaseWatch { if (!json.id) { throw badRequest( i18n.translate('xpack.watcher.models.baseWatch.idPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {id} property', + defaultMessage: 'JSON argument must contain an {id} property', values: { id: 'id' } @@ -104,7 +104,7 @@ export class BaseWatch { if (!json.watchJson) { throw badRequest( i18n.translate('xpack.watcher.models.baseWatch.watchJsonPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain a {watchJson} property', + defaultMessage: 'JSON argument must contain a {watchJson} property', values: { watchJson: 'watchJson' } @@ -114,7 +114,7 @@ export class BaseWatch { if (!json.watchStatusJson) { throw badRequest( i18n.translate('xpack.watcher.models.baseWatch.watchStatusJsonPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain a {watchStatusJson} property', + defaultMessage: 'JSON argument must contain a {watchStatusJson} property', values: { watchStatusJson: 'watchStatusJson' } diff --git a/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/build_condition.js b/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/build_condition.js index 1c8d2ad422edb..64ee0f44c3104 100644 --- a/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/build_condition.js +++ b/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/build_condition.js @@ -5,7 +5,8 @@ */ import { singleLineScript } from '../lib/single_line_script'; - +import { COMPARATORS } from '../../../../common/constants'; +const { BETWEEN } = COMPARATORS; /* watch.condition.script.inline */ @@ -13,17 +14,39 @@ function buildInline({ aggType, thresholdComparator, hasTermsAgg }) { let script = ''; if (aggType === 'count' && !hasTermsAgg) { - script = ` + if (thresholdComparator === BETWEEN) { + script = ` + if (ctx.payload.hits.total >= params.threshold[0] && ctx.payload.hits.total <= params.threshold[1]) { + return true; + } + + return false; + `; + } else { + script = ` if (ctx.payload.hits.total ${thresholdComparator} params.threshold) { return true; } return false; `; + } } if (aggType === 'count' && hasTermsAgg) { - script = ` + if (thresholdComparator === BETWEEN) { + script = ` + ArrayList arr = ctx.payload.aggregations.bucketAgg.buckets; + for (int i = 0; i < arr.length; i++) { + if (arr[i].doc_count >= params.threshold[0] && arr[i].doc_count <= params.threshold[1]) { + return true; + } + } + + return false; + `; + } else { + script = ` ArrayList arr = ctx.payload.aggregations.bucketAgg.buckets; for (int i = 0; i < arr.length; i++) { if (arr[i].doc_count ${thresholdComparator} params.threshold) { @@ -33,20 +56,44 @@ function buildInline({ aggType, thresholdComparator, hasTermsAgg }) { return false; `; - } + } + } if (aggType !== 'count' && !hasTermsAgg) { - script = ` + if (thresholdComparator === BETWEEN) { + script = ` + if (ctx.payload.aggregations.metricAgg.value >= params.threshold[0] + && ctx.payload.aggregations.metricAgg.value <= params.threshold[1]) { + return true; + } + + return false; + `; + } else { + script = ` if (ctx.payload.aggregations.metricAgg.value ${thresholdComparator} params.threshold) { return true; } return false; `; + } } if (aggType !== 'count' && hasTermsAgg) { - script = ` + if (thresholdComparator === BETWEEN) { + script = ` + ArrayList arr = ctx.payload.aggregations.bucketAgg.buckets; + for (int i = 0; i < arr.length; i++) { + if (arr[i]['metricAgg'].value >= params.threshold[0] && arr[i]['metricAgg'].value <= params.threshold[1]) { + return true; + } + } + + return false; + `; + } else { + script = ` ArrayList arr = ctx.payload.aggregations.bucketAgg.buckets; for (int i = 0; i < arr.length; i++) { if (arr[i]['metricAgg'].value ${thresholdComparator} params.threshold) { @@ -56,6 +103,8 @@ function buildInline({ aggType, thresholdComparator, hasTermsAgg }) { return false; `; + } + } return singleLineScript(script); diff --git a/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/build_transform.js b/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/build_transform.js index d2a6116ca803f..a302dde245634 100644 --- a/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/build_transform.js +++ b/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/build_transform.js @@ -5,7 +5,8 @@ */ import { singleLineScript } from '../lib/single_line_script'; - +import { COMPARATORS } from '../../../../common/constants'; +const { BETWEEN } = COMPARATORS; /* watch.transform.script.inline */ @@ -22,7 +23,26 @@ function buildInline({ aggType, hasTermsAgg, thresholdComparator }) { } if (aggType === 'count' && hasTermsAgg) { - script = ` + if (thresholdComparator === BETWEEN) { + script = ` + HashMap result = new HashMap(); + ArrayList arr = ctx.payload.aggregations.bucketAgg.buckets; + ArrayList filteredHits = new ArrayList(); + + for (int i = 0; i < arr.length; i++) { + HashMap filteredHit = new HashMap(); + filteredHit.key = arr[i].key; + filteredHit.value = arr[i].doc_count; + if (filteredHit.value >= params.threshold[0] && filteredHit.value <= params.threshold[1]) { + filteredHits.add(filteredHit); + } + } + result.results = filteredHits; + + return result; + `; + } else { + script = ` HashMap result = new HashMap(); ArrayList arr = ctx.payload.aggregations.bucketAgg.buckets; ArrayList filteredHits = new ArrayList(); @@ -39,6 +59,8 @@ function buildInline({ aggType, hasTermsAgg, thresholdComparator }) { return result; `; + } + } if (aggType !== 'count' && !hasTermsAgg) { @@ -51,7 +73,26 @@ function buildInline({ aggType, hasTermsAgg, thresholdComparator }) { } if (aggType !== 'count' && hasTermsAgg) { - script = ` + if (thresholdComparator === BETWEEN) { + script = ` + HashMap result = new HashMap(); + ArrayList arr = ctx.payload.aggregations.bucketAgg.buckets; + ArrayList filteredHits = new ArrayList(); + + for (int i = 0; i < arr.length; i++) { + HashMap filteredHit = new HashMap(); + filteredHit.key = arr[i].key; + filteredHit.value = arr[i]['metricAgg'].value; + if (filteredHit.value >= params.threshold[0] && filteredHit.value <= params.threshold[1]) { + filteredHits.add(filteredHit); + } + } + result.results = filteredHits; + + return result; + `; + } else { + script = ` HashMap result = new HashMap(); ArrayList arr = ctx.payload.aggregations.bucketAgg.buckets; ArrayList filteredHits = new ArrayList(); @@ -68,6 +109,8 @@ function buildInline({ aggType, hasTermsAgg, thresholdComparator }) { return result; `; + } + } return singleLineScript(script); diff --git a/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/threshold_watch.js b/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/threshold_watch.js index eaf9feabf7bf9..4fbc0111f57dd 100644 --- a/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/threshold_watch.js +++ b/x-pack/legacy/plugins/watcher/server/models/watch/threshold_watch/threshold_watch.js @@ -120,7 +120,7 @@ export class ThresholdWatch extends BaseWatch { thresholdComparator: metadata.threshold_comparator, timeWindowSize: metadata.time_window_size, timeWindowUnit: metadata.time_window_unit, - threshold: metadata.threshold + threshold: Array.isArray(metadata.threshold) ? metadata.threshold : [metadata.threshold] } ); diff --git a/x-pack/legacy/plugins/watcher/server/models/watch/watch.js b/x-pack/legacy/plugins/watcher/server/models/watch/watch.js index 03e75a365f028..c75afc62c4c4b 100644 --- a/x-pack/legacy/plugins/watcher/server/models/watch/watch.js +++ b/x-pack/legacy/plugins/watcher/server/models/watch/watch.js @@ -28,7 +28,7 @@ export class Watch { if (!json.type) { throw badRequest( i18n.translate('xpack.watcher.models.watch.typePropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {type} property', + defaultMessage: 'JSON argument must contain an {type} property', values: { type: 'type' } @@ -54,7 +54,7 @@ export class Watch { if (!json.watchJson) { throw badRequest( i18n.translate('xpack.watcher.models.watch.watchJsonPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain a {watchJson} property', + defaultMessage: 'JSON argument must contain a {watchJson} property', values: { watchJson: 'watchJson' } diff --git a/x-pack/legacy/plugins/watcher/server/models/watch_history_item/watch_history_item.js b/x-pack/legacy/plugins/watcher/server/models/watch_history_item/watch_history_item.js index b3982e11bef06..617f758571742 100644 --- a/x-pack/legacy/plugins/watcher/server/models/watch_history_item/watch_history_item.js +++ b/x-pack/legacy/plugins/watcher/server/models/watch_history_item/watch_history_item.js @@ -40,7 +40,7 @@ export class WatchHistoryItem { if (!json.id) { throw badRequest( i18n.translate('xpack.watcher.models.watchHistoryItem.idPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {id} property', + defaultMessage: 'JSON argument must contain an {id} property', values: { id: 'id' } @@ -50,7 +50,7 @@ export class WatchHistoryItem { if (!json.watchId) { throw badRequest( i18n.translate('xpack.watcher.models.watchHistoryItem.watchIdPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain a {watchId} property', + defaultMessage: 'JSON argument must contain a {watchId} property', values: { watchId: 'watchId' } @@ -60,7 +60,7 @@ export class WatchHistoryItem { if (!json.watchHistoryItemJson) { throw badRequest( i18n.translate('xpack.watcher.models.watchHistoryItem.watchHistoryItemJsonPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain a {watchHistoryItemJson} property', + defaultMessage: 'JSON argument must contain a {watchHistoryItemJson} property', values: { watchHistoryItemJson: 'watchHistoryItemJson' } diff --git a/x-pack/legacy/plugins/watcher/server/models/watch_status/watch_status.js b/x-pack/legacy/plugins/watcher/server/models/watch_status/watch_status.js index 5045c3a7a311e..b7cffe16ca0bc 100644 --- a/x-pack/legacy/plugins/watcher/server/models/watch_status/watch_status.js +++ b/x-pack/legacy/plugins/watcher/server/models/watch_status/watch_status.js @@ -141,7 +141,7 @@ export class WatchStatus { if (!json.id) { throw badRequest( i18n.translate('xpack.watcher.models.watchStatus.idPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain an {id} property', + defaultMessage: 'JSON argument must contain an {id} property', values: { id: 'id' } @@ -151,7 +151,7 @@ export class WatchStatus { if (!json.watchStatusJson) { throw badRequest( i18n.translate('xpack.watcher.models.watchStatus.watchStatusJsonPropertyMissingBadRequestMessage', { - defaultMessage: 'json argument must contain a {watchStatusJson} property', + defaultMessage: 'JSON argument must contain a {watchStatusJson} property', values: { watchStatusJson: 'watchStatusJson' } diff --git a/x-pack/legacy/plugins/watcher/server/routes/api/watch/register_save_route.js b/x-pack/legacy/plugins/watcher/server/routes/api/watch/register_save_route.js index 3434a0ab559f2..ffc70e709907d 100644 --- a/x-pack/legacy/plugins/watcher/server/routes/api/watch/register_save_route.js +++ b/x-pack/legacy/plugins/watcher/server/routes/api/watch/register_save_route.js @@ -7,8 +7,15 @@ import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; import { Watch } from '../../../models/watch'; import { isEsErrorFactory } from '../../../lib/is_es_error_factory'; -import { wrapEsError, wrapUnknownError } from '../../../lib/error_wrappers'; +import { wrapEsError, wrapUnknownError, wrapCustomError } from '../../../lib/error_wrappers'; import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; +import { i18n } from '@kbn/i18n'; + +function fetchWatch(callWithRequest, watchId) { + return callWithRequest('watcher.getWatch', { + id: watchId + }); +} function saveWatch(callWithRequest, watch) { return callWithRequest('watcher.putWatch', { @@ -25,12 +32,40 @@ export function registerSaveRoute(server) { server.route({ path: '/api/watcher/watch/{id}', method: 'PUT', - handler: (request) => { + handler: async (request) => { const callWithRequest = callWithRequestFactory(server, request); + const watchPayload = request.payload; + + // For new watches, verify watch with the same ID doesn't already exist + if (watchPayload.isNew) { + const conflictError = wrapCustomError( + new Error(i18n.translate('xpack.watcher.saveRoute.duplicateWatchIdErrorMessage', { + defaultMessage: 'There is already a watch with ID \'{watchId}\'.', + values: { + watchId: watchPayload.id, + } + })), + 409 + ); + + try { + const existingWatch = await fetchWatch(callWithRequest, watchPayload.id); + + if (existingWatch.found) { + throw conflictError; + } + } catch (e) { + // Rethrow conflict error but silently swallow all others + if (e === conflictError) { + throw e; + } + } + } - const watch = Watch.fromDownstreamJson(request.payload); + const watchFromDownstream = Watch.fromDownstreamJson(watchPayload); - return saveWatch(callWithRequest, watch.upstreamJson) + // Create new watch + return saveWatch(callWithRequest, watchFromDownstream.upstreamJson) .catch(err => { // Case: Error from Elasticsearch JS client if (isEsError(err)) { diff --git a/x-pack/legacy/plugins/watcher/server/routes/api/watches/register_delete_route.js b/x-pack/legacy/plugins/watcher/server/routes/api/watches/register_delete_route.js index 29ee6f60676bc..a0bbfb954b755 100644 --- a/x-pack/legacy/plugins/watcher/server/routes/api/watches/register_delete_route.js +++ b/x-pack/legacy/plugins/watcher/server/routes/api/watches/register_delete_route.js @@ -6,37 +6,42 @@ import { callWithRequestFactory } from '../../../lib/call_with_request_factory'; import { wrapUnknownError } from '../../../lib/error_wrappers'; -import { licensePreRoutingFactory } from'../../../lib/license_pre_routing_factory'; +import { licensePreRoutingFactory } from '../../../lib/license_pre_routing_factory'; function deleteWatches(callWithRequest, watchIds) { const deletePromises = watchIds.map(watchId => { return callWithRequest('watcher.deleteWatch', { - id: watchId + id: watchId, }) .then(success => ({ success })) .catch(error => ({ error })); }); - return Promise.all(deletePromises) - .then(results => { - const successes = results.filter(result => Boolean(result.success)); - const errors = results.filter(result => Boolean(result.error)); - - return { - numSuccesses: successes.length, - numErrors: errors.length - }; + return Promise.all(deletePromises).then(results => { + const errors = []; + const successes = []; + results.forEach(({ success, error }) => { + if (success) { + successes.push(success._id); + } else if (error) { + errors.push(error._id); + } }); + + return { + successes, + errors, + }; + }); } export function registerDeleteRoute(server) { - const licensePreRouting = licensePreRoutingFactory(server); server.route({ path: '/api/watcher/watches/delete', method: 'POST', - handler: async (request) => { + handler: async request => { const callWithRequest = callWithRequestFactory(server, request); try { @@ -47,7 +52,7 @@ export function registerDeleteRoute(server) { } }, config: { - pre: [ licensePreRouting ] - } + pre: [licensePreRouting], + }, }); } diff --git a/x-pack/legacy/plugins/watcher/test/fixtures/execute_details.ts b/x-pack/legacy/plugins/watcher/test/fixtures/execute_details.ts new file mode 100644 index 0000000000000..232c44737b54c --- /dev/null +++ b/x-pack/legacy/plugins/watcher/test/fixtures/execute_details.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +interface ExecuteDetails { + triggerData?: { + triggeredTime?: string; + scheduledTime?: string; + }; + ignoreCondition: boolean; + actionModes?: { + [key: string]: string; + }; + recordExecution?: boolean; +} + +export const getExecuteDetails = ({ + triggerData = {}, + ignoreCondition = false, + actionModes = {}, + recordExecution, +}: Partial = {}): ExecuteDetails => ({ + triggerData, + ignoreCondition, + actionModes, + recordExecution, +}); diff --git a/x-pack/legacy/plugins/watcher/test/fixtures/index.ts b/x-pack/legacy/plugins/watcher/test/fixtures/index.ts new file mode 100644 index 0000000000000..f1e24a81c710a --- /dev/null +++ b/x-pack/legacy/plugins/watcher/test/fixtures/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 * from './watch'; +export * from './watch_history'; +export * from './execute_details'; diff --git a/x-pack/legacy/plugins/watcher/test/fixtures/watch.ts b/x-pack/legacy/plugins/watcher/test/fixtures/watch.ts new file mode 100644 index 0000000000000..d948fddeefd58 --- /dev/null +++ b/x-pack/legacy/plugins/watcher/test/fixtures/watch.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Moment } from 'moment'; +import { getRandomString } from '../../../../../test_utils'; + +interface Watch { + id: string; + name: string; + type: 'json' | 'threshold' | 'monitoring'; + timeField?: string; + triggerIntervalSize?: number; + triggerIntervalUnit?: string; + aggType?: string; + termSize?: number; + thresholdComparator?: string; + timeWindowSize?: number; + timeWindowUnit?: string; + threshold?: number[]; + isSystemWatch: boolean; + watchStatus: { + state: 'OK' | 'Firing' | 'Error' | 'Config error' | 'Disabled'; + comment?: string; + lastMetCondition?: Moment; + lastChecked?: Moment; + isActive: boolean; + actionStatuses?: any[]; + }; +} + +export const getWatch = ({ + id = getRandomString(), + name = getRandomString(), + type = 'json', + timeField, + triggerIntervalSize, + triggerIntervalUnit, + aggType, + termSize, + thresholdComparator, + timeWindowSize, + timeWindowUnit, + threshold, + isSystemWatch = false, + watchStatus = { + state: 'OK', + isActive: true, + }, +}: Partial = {}): Watch => ({ + id, + name, + type, + timeField, + triggerIntervalSize, + triggerIntervalUnit, + aggType, + termSize, + thresholdComparator, + timeWindowSize, + timeWindowUnit, + threshold, + isSystemWatch, + watchStatus, +}); diff --git a/x-pack/legacy/plugins/watcher/test/fixtures/watch_history.ts b/x-pack/legacy/plugins/watcher/test/fixtures/watch_history.ts new file mode 100644 index 0000000000000..70275e6e8869e --- /dev/null +++ b/x-pack/legacy/plugins/watcher/test/fixtures/watch_history.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getRandomString } from '../../../../../test_utils'; + +interface WatchHistory { + startTime: string; + id: string; + watchId: string; + watchStatus: { + state: 'OK' | 'Firing' | 'Error' | 'Config error' | 'Disabled'; + comment?: string; + actionStatuses?: Array<{ + id: string; + state: 'OK' | 'Firing' | 'Error' | 'Acked' | 'Throttled' | 'Config error'; + }>; + }; + details?: object; +} + +export const getWatchHistory = ({ + startTime = '2019-06-03T19:44:11.088Z', + id = getRandomString(), + watchId = getRandomString(), + watchStatus = { + state: 'OK', + }, + details = {}, +}: Partial = {}): WatchHistory => ({ + startTime, + id, + watchId, + watchStatus, + details, +}); diff --git a/x-pack/legacy/server/lib/check_license/check_license.js b/x-pack/legacy/server/lib/check_license/check_license.js index 5f5ecf816a4ab..12e92c74e22d6 100644 --- a/x-pack/legacy/server/lib/check_license/check_license.js +++ b/x-pack/legacy/server/lib/check_license/check_license.js @@ -59,7 +59,7 @@ export function checkLicense(pluginName, minimumLicenseRequired, xpackLicenseInf message: i18n.translate( 'xpack.server.checkLicense.errorExpiredMessage', { - defaultMessage: 'You cannot use {pluginName} because your {licenseType} license has expired', + defaultMessage: 'You cannot use {pluginName} because your {licenseType} license has expired.', values: { licenseType, pluginName }, }, ), diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 7f8da0580b658..8311604833ff9 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9604,10 +9604,6 @@ "xpack.uptime.snapshotHistogram.series.downLabel": "ダウン", "xpack.uptime.snapshotHistogram.series.upLabel": "アップ", "xpack.uptime.uptimeFeatureCatalogueTitle": "アップタイム", - "xpack.watcher.actionTypeSelect.actionDisabledTextMessage": "無効になっています。{elasticsearchYmlText} を構成します。", - "xpack.watcher.actionTypeSelect.addNewActionPlaceholder": "新規アクションを追加", - "xpack.watcher.checkLicense.licenseDoesNotSupportTextMessage": "ご使用の {licenseType} ライセンスは {watcher} をサポートしていません。ライセンスをアップグレードしてください。", - "xpack.watcher.checkLicense.licenseInformationNotAvailableTextMessage": "現在ライセンス情報が利用できないため {watcher} を使用できません。", "xpack.watcher.constants.actionStates.acknowledgedStateText": "承認済み", "xpack.watcher.constants.actionStates.configErrorStateText": "構成エラー", "xpack.watcher.constants.actionStates.errorStateText": "エラー", @@ -9624,15 +9620,7 @@ "xpack.watcher.constants.watchStates.errorStateText": "エラー", "xpack.watcher.constants.watchStates.firingStateText": "実行中", "xpack.watcher.constants.watchStates.okStateText": "OK", - "xpack.watcher.create.breadcrumb": "作成", - "xpack.watcher.durationSelect.durationAmountAriaLabel": "期間", - "xpack.watcher.durationSelect.durationTimeUnitAriaLabel": "期間の時間単位", - "xpack.watcher.forbiddenMessage.contactAdministratorTextMessage": "管理者にお問い合わせください。", - "xpack.watcher.indexSelect.buildingListTextMessage": "リストを作成中… お待ちください", - "xpack.watcher.indexSelect.startTypingPlaceholder": "入力開始…", - "xpack.watcher.list.breadcrumb": "Watcher", "xpack.watcher.models.action.actionJsonPropertyMissingBadRequestMessage": "json 引数には {actionJson} プロパティが含まれている必要があります", - "xpack.watcher.models.actionStatus.actionStatusJsonPropertyMissingBadRequestMessage": "json 引数には {actionStatusJson} プロパティが含まれている必要があります", "xpack.watcher.models.actionStatus.idPropertyMissingBadRequestMessage": "json 引数には {id} プロパティが含まれている必要があります", "xpack.watcher.models.actionStatus.notDetermineActionStatusBadImplementationMessage": "アクションステータスを把握できませんでした; action = {actionStatusJson}", "xpack.watcher.models.baseAction.idPropertyMissingBadRequestMessage": "json 引数には {id} プロパティが含まれている必要があります", @@ -9649,27 +9637,20 @@ "xpack.watcher.models.baseWatch.watchStatusJsonPropertyMissingBadRequestMessage": "json 引数には {watchStatusJson} プロパティが含まれている必要があります", "xpack.watcher.models.emailAction.actionJsonEmailPropertyMissingBadRequestMessage": "json 引数には {actionJsonEmail} プロパティが含まれている必要があります", "xpack.watcher.models.emailAction.actionJsonEmailToPropertyMissingBadRequestMessage": "json 引数には {actionJsonEmailTo} プロパティが含まれている必要があります", - "xpack.watcher.models.emailAction.description": "「{subject}」が {toList} に送信されます", "xpack.watcher.models.emailAction.selectMessageText": "サーバーからメールを送信します。", "xpack.watcher.models.emailAction.simulateButtonLabel": "今すぐメールをテスト送信", "xpack.watcher.models.emailAction.simulateFailMessage": "{toList} へのメールの送信に失敗しました。", "xpack.watcher.models.emailAction.simulateMessage": "{toList} にサンプルメールが送信されました", "xpack.watcher.models.emailAction.typeName": "メール", "xpack.watcher.models.fields.fieldsPropertyMissingBadRequestMessage": "json 引数には {fields} プロパティが含まれている必要があります", - "xpack.watcher.models.hipchatAction.description": "{body} が Hipchat で送信されます", - "xpack.watcher.models.hipchatAction.simulateFailMessage": "Hipchat メッセージの送信に失敗しました.。", - "xpack.watcher.models.hipchatAction.simulateMessage": "Hipchat メッセージが送信されました。", - "xpack.watcher.models.indexAction.description": "{index} が {docType} としてインデックスされます", "xpack.watcher.models.indexAction.simulateFailMessage": "{index} のインデックスに失敗しました。", "xpack.watcher.models.indexAction.simulateMessage": "インデックス {index} がインデックスされました。", - "xpack.watcher.models.jiraAction.description": "Jira で {issueName} が作成されます", "xpack.watcher.models.jiraAction.simulateFailMessage": "Jira の問題の作成に失敗しました。", "xpack.watcher.models.jiraAction.simulateMessage": "Jira の問題が作成されました。", "xpack.watcher.models.jsonWatch.selectMessageText": "生 JSON のカスタムウォッチをセットアップします。", "xpack.watcher.models.jsonWatch.typeName": "高度なウォッチ", "xpack.watcher.models.loggingAction.actionJsonLoggingPropertyMissingBadRequestMessage": "json 引数には {actionJsonLogging} プロパティが含まれている必要があります", "xpack.watcher.models.loggingAction.actionJsonLoggingTextPropertyMissingBadRequestMessage": "json 引数には {actionJsonLoggingText} プロパティが含まれている必要があります", - "xpack.watcher.models.loggingAction.description": "ログメッセージ「{text}」", "xpack.watcher.models.loggingAction.selectMessageText": "ログに新規項目を追加します。", "xpack.watcher.models.loggingAction.simulateButtonLabel": "今すぐサンプルメッセージを記録", "xpack.watcher.models.loggingAction.simulateFailMessage": "サンプルメッセージの記録に失敗しました", @@ -9679,12 +9660,10 @@ "xpack.watcher.models.monitoringWatch.fromDownstreamJsonCalledBadRequestMessage": "ウォッチの監視に {fromDownstreamJson} が必要です", "xpack.watcher.models.monitoringWatch.getVisualizeQueryCalledBadRequestMessage": "ウォッチの監視に {getVisualizeQuery} が必要です", "xpack.watcher.models.monitoringWatch.upstreamJsonCalledBadRequestMessage": "ウォッチの監視に {upstreamJson} が必要です", - "xpack.watcher.models.pagerDutyAction.description": "{description} が PagerDuty に送信されます", "xpack.watcher.models.pagerDutyAction.simulateFailMessage": "Hipchat イベントの送信に失敗しました.。", "xpack.watcher.models.pagerDutyAction.simulateMessage": "PagerDuty イベントが送信されました。", "xpack.watcher.models.slackAction.actionJsonSlackMessagePropertyMissingBadRequestMessage": "json 引数には {actionJsonSlackMessage} プロパティが含まれている必要があります", "xpack.watcher.models.slackAction.actionJsonSlackPropertyMissingBadRequestMessage": "json 引数には {actionJsonSlack} プロパティが含まれている必要があります", - "xpack.watcher.models.slackAction.description": "{toList} に Slack メッセージが送信されます", "xpack.watcher.models.slackAction.selectMessageText": "Slack ユーザーまたはチャンネルにメッセージを送信します。", "xpack.watcher.models.slackAction.simulateButtonLabel": "今すぐサンプルメッセージを送信", "xpack.watcher.models.slackAction.simulateFailMessage": "{toList} へのサンプル Slack メッセージの送信に失敗しました。", @@ -9703,209 +9682,39 @@ "xpack.watcher.models.watchHistoryItem.watchIdPropertyMissingBadRequestMessage": "json 引数には {watchId} プロパティが含まれている必要があります", "xpack.watcher.models.watchStatus.idPropertyMissingBadRequestMessage": "json 引数には {id} プロパティが含まれている必要があります", "xpack.watcher.models.watchStatus.watchStatusJsonPropertyMissingBadRequestMessage": "json 引数には {watchStatusJson} プロパティが含まれている必要があります", - "xpack.watcher.models.webhookAction.description": "Webhook が {fullPath} に {method} リクエストを送信します", "xpack.watcher.models.webhookAction.simulateFailMessage": "{fullPath} へのリクエストの送信に失敗しました。", "xpack.watcher.models.webhookAction.simulateMessage": "{fullPath} にサンプルリクエストが送信されました", - "xpack.watcher.sections.watchDetail.actionStatusTable.acknowledgeButtonLabel": "承認", - "xpack.watcher.sections.watchDetail.actionStatusTable.acknowledgeButtonTitle": "承認", - "xpack.watcher.sections.watchDetail.actionStatusTable.actionColumnLabel": "アクション", - "xpack.watcher.sections.watchDetail.actionStatusTable.stateColumnLabel": "ステータス", - "xpack.watcher.sections.watchDetail.actionStatusTotalErrors": "{total, number} 件の{total, plural, one {エラー} other {エラー}}", - "xpack.watcher.sections.watchDetail.activateButtonLabel": "有効化", - "xpack.watcher.sections.watchDetail.currentStatusTitle": "現在のステータス", - "xpack.watcher.sections.watchDetail.deactivateButtonLabel": "無効化", - "xpack.watcher.sections.watchDetail.deleteButtonLabel": "削除", - "xpack.watcher.sections.watchDetail.deleteConfirmModal.deleteWatchButtonLabel": "ウォッチを削除", - "xpack.watcher.sections.watchDetail.deleteConfirmModal.description": "ウォッチが永久に削除されます。よろしいですか?", - "xpack.watcher.sections.watchDetail.deleteWatchSuccessNotificationText": "{watchName} が削除されました", - "xpack.watcher.sections.watchDetail.errorDisplayModalTitleText": "「{actionId}」アクションのエラー", - "xpack.watcher.sections.watchDetail.noActionsFoundText": "アクションが見つかりません。", - "xpack.watcher.sections.watchDetail.watchHistory.watchHistoryLoadingText": "ウォッチ履歴を読み込み中", - "xpack.watcher.sections.watchDetail.watchHistory.watchHistoryNotFoundText": "ウォッチ履歴が見つかりません。", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.allLabel": "すべて", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last1HourLabel": "過去 1 時間", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last1YearLabel": "過去 1 年間", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last24HoursLabel": "過去 24 時間", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last30DaysLabel": "過去 30 日間", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last6MonthsLabel": "過去 6 か月間", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last7DaysLabel": "過去 7 日間", - "xpack.watcher.sections.watchDetail.watchHistoryTable.commentColumnLabel": "コメント", - "xpack.watcher.sections.watchDetail.watchHistoryTable.stateColumnLabel": "ステータス", - "xpack.watcher.sections.watchDetail.watchHistoryTable.triggerTimeColumnLabel": "実行時刻", - "xpack.watcher.sections.watchDetail.watchHistoryTitle": "ウォッチ履歴", - "xpack.watcher.sections.watchEdit.actionsExecuteSummary.actionColumnLabel": "アクション", - "xpack.watcher.sections.watchEdit.actionsExecuteSummary.modeColumnLabel": "モード", - "xpack.watcher.sections.watchEdit.actionsExecuteSummary.reasonColumnLabel": "理由", - "xpack.watcher.sections.watchEdit.actionsExecuteSummary.stateColumnLabel": "ステータス", - "xpack.watcher.sections.watchEdit.actionsExecuteSummary.typeColumnLabel": "タイプ", - "xpack.watcher.sections.watchEdit.detail.idIsRequiredValidationMessage": "ID が必要です。", - "xpack.watcher.sections.watchEdit.detail.idLabel": "ID", - "xpack.watcher.sections.watchEdit.detail.idNamingDescription": "ID は文字またはアンダーラインで始まり、文字、アンダーライン、ハイフン、数字のみ使用できます。", - "xpack.watcher.sections.watchEdit.detail.invalidJSONErrorMessage": "無効な JSON", - "xpack.watcher.sections.watchEdit.detail.nameLabel": "名前", - "xpack.watcher.sections.watchEdit.detail.syntaxLinkLabel": "構文", - "xpack.watcher.sections.watchEdit.detail.watchJSONLabel": "ウォッチ JSON", - "xpack.watcher.sections.watchEdit.executeDetail.actionOverrides.actionModesLabel": "アクションモード", - "xpack.watcher.sections.watchEdit.executeDetail.actionOverridesButtonLabel": "アクションオーバーライド", - "xpack.watcher.sections.watchEdit.executeDetail.conditionOverrides.ignoreConditionLabel": "条件を無視 (条件は常に true)", - "xpack.watcher.sections.watchEdit.executeDetail.conditionOverridesButtonLabel": "アクションオーバーライド", - "xpack.watcher.sections.watchEdit.executeDetail.inputOverrides.alternativeInputLabel": "代替インプット", - "xpack.watcher.sections.watchEdit.executeDetail.inputOverrides.alternativeInputLinkText": "以前の実行からコピー", - "xpack.watcher.sections.watchEdit.executeDetail.inputOverrides.invalidJSONErrorMessage": "無効な JSON", - "xpack.watcher.sections.watchEdit.executeDetail.inputOverridesButtonLabel": "インプットオーバーライド", - "xpack.watcher.sections.watchEdit.executeDetail.triggerOverrides.scheduledTimeLabel": "予定時刻", - "xpack.watcher.sections.watchEdit.executeDetail.triggerOverrides.scheduledTimePlaceholder": "今", - "xpack.watcher.sections.watchEdit.executeDetail.triggerOverrides.triggeredTimeLabel": "実行時刻", - "xpack.watcher.sections.watchEdit.executeDetail.triggerOverrides.triggeredTimePlaceholder": "今", - "xpack.watcher.sections.watchEdit.executeDetail.triggerOverridesButtonLabel": "実行オーバーライド", - "xpack.watcher.sections.watchEdit.executeInfoPanel.howToModifyWatchExecutionDescription": "ウォッチの実行方法を変更するには、下のウォッチを拡張します", - "xpack.watcher.sections.watchEdit.executeInfoPanel.testWatchExecutionTitle": "ウォッチの実行テスト", - "xpack.watcher.sections.watchEdit.json.deleteConfirmModal.description": "ウォッチが永久に削除されます。よろしいですか?", - "xpack.watcher.sections.watchEdit.json.deleteConfirmModal.overwriteWatchButtonLabel": "ウォッチを削除", - "xpack.watcher.sections.watchEdit.json.deleteSuccessNotificationText": "「{watchDisplayName}」が削除されました", "xpack.watcher.sections.watchEdit.json.editTabLabel": "編集", - "xpack.watcher.sections.watchEdit.json.editTabTitle": "ウォッチの編集", - "xpack.watcher.sections.watchEdit.json.saveConfirmModal.description": "ID「{watchId}」 {watchNameMessageFragment} のウォッチが既に存在します。上書きしますか?", - "xpack.watcher.sections.watchEdit.json.saveConfirmModal.descriptionFragmentText": "(名前: 「{existingWatchName}」)", - "xpack.watcher.sections.watchEdit.json.saveConfirmModal.overwriteWatchButtonLabel": "ウォッチを上書き", "xpack.watcher.sections.watchEdit.json.saveSuccessNotificationText": "「{watchDisplayName}」が保存されました", - "xpack.watcher.sections.watchEdit.json.simulateResultsTabLabel": "シミュレーション結果", - "xpack.watcher.sections.watchEdit.json.simulateResultsTabTitle": "結果をシミュレート", "xpack.watcher.sections.watchEdit.json.simulateTabLabel": "シミュレート", - "xpack.watcher.sections.watchEdit.json.simulateTabTitle": "ウォッチをシミュレート", - "xpack.watcher.sections.watchEdit.json.simulateWatchButtonLabel": "ウォッチをシミュレート", - "xpack.watcher.sections.watchEdit.json.simulationOutputLabel": "シミュレーションアウトプット", - "xpack.watcher.sections.watchEdit.json.warningPossibleInvalidSlackAction.description": "このウォッチには「to」プロパティのない Slack アクションがあります。 このウォッチは、Elasticsearch で Slack の「message_default」設定で「to」プロパティが指定された場合のみ有効になります。", - "xpack.watcher.sections.watchEdit.json.watchErrorsWarning.confirmSaveWatch": "ウォッチを保存", - "xpack.watcher.sections.watchEdit.threshold.matchingFollowingConditionTitle": "次の条件に一致", "xpack.watcher.sections.watchEdit.threshold.saveButtonLabel": "保存", - "xpack.watcher.sections.watchEdit.threshold.saveConfirmModal.description": "ID「{watchId}」 {watchNameMessageFragment} のウォッチが既に存在します。上書きしますか?", - "xpack.watcher.sections.watchEdit.threshold.saveConfirmModal.descriptionFragmentText": "(名前: 「{existingWatchName}」)", - "xpack.watcher.sections.watchEdit.threshold.saveConfirmModal.overwriteWatchButtonLabel": "ウォッチを上書き", - "xpack.watcher.sections.watchEdit.threshold.saveSuccessNotificationText": "「{watchDisplayName}」が保存されました", - "xpack.watcher.sections.watchEdit.titleBar.cancelButtonLabel": "キャンセル", - "xpack.watcher.sections.watchEdit.titleBar.deleteButtonLabel": "削除", - "xpack.watcher.sections.watchEdit.titleBar.deleteButtonTooltip": "ウォッチを削除", - "xpack.watcher.sections.watchEdit.titleBar.newWatchTitle": "新規ウォッチ", - "xpack.watcher.sections.watchEdit.titleBar.saveButtonLabel": "保存", - "xpack.watcher.sections.watchEdit.titlePanel.associatedTimeFieldIsMissedInIndexQueryValidationMessage": "インデックスクエリに時間フィールドが関連付けられていません", - "xpack.watcher.sections.watchEdit.titlePanel.createNewTypeOfWatchTitle": "新規 {typeName} の作成", - "xpack.watcher.sections.watchEdit.titlePanel.enterOneOrMoreIndicesValidationMessage": "1 つまたは複数のインデックスを入力してください", "xpack.watcher.sections.watchEdit.titlePanel.howToBroadenSearchQueryDescription": "* で検索クエリの範囲を広げます", "xpack.watcher.sections.watchEdit.titlePanel.indicesToQueryLabel": "クエリを実行するインデックス", - "xpack.watcher.sections.watchEdit.titlePanel.intervalSizeIsRequiredValidationMessage": "間隔サイズが必要です", - "xpack.watcher.sections.watchEdit.titlePanel.intervalSizeMinimumDurationValidationMessage": " 最低期間は {minSize}{minUnit} です", - "xpack.watcher.sections.watchEdit.titlePanel.intervalSizeNegativeNumberValidationMessage": "間隔サイズはマイナスの数字にできません", - "xpack.watcher.sections.watchEdit.titlePanel.intervalSizeUnitIsRequiredValidationMessage": "間隔サイズが必要です", - "xpack.watcher.sections.watchEdit.titlePanel.intervalSizeValidNumberValidationMessage": "間隔サイズは有効な数字でなければなりません", - "xpack.watcher.sections.watchEdit.titlePanel.queryDoesNotMatchAnyValidIndicesValidationMessage": "インデックスクエリが有効なインデックスと一致していません", - "xpack.watcher.sections.watchEdit.titlePanel.timeFieldIsRequiredValidationText": "時間フィールドドが必要です", "xpack.watcher.sections.watchEdit.titlePanel.timeFieldLabel": "時間フィールド", "xpack.watcher.sections.watchEdit.titlePanel.timeFieldOptionLabel": "フィールドを選択", "xpack.watcher.sections.watchEdit.titlePanel.watchIntervalLabel": "次の間隔でウォッチを実行:", - "xpack.watcher.sections.watchEdit.titlePanel.watchNameIsRequiredValidationMessage": "名前が必要です", "xpack.watcher.sections.watchEdit.titlePanel.watchNameLabel": "名前", - "xpack.watcher.sections.watchEdit.titlePanel.watchNamePlaceholder": "マイしきい値ウォッチ", - "xpack.watcher.sections.watchEdit.watchActionModeTable.actionColumnLabel": "アクション", - "xpack.watcher.sections.watchEdit.watchActionModeTable.modeColumnLabel": "モード", - "xpack.watcher.sections.watchEdit.watchActionModeTable.typeColumnLabel": "タイプ", - "xpack.watcher.sections.watchEdit.watchExecuteSummary.simulationStatusLabel": "シミュレーションステータス:", - "xpack.watcher.sections.watchHistoryItem.actionsSummary.actionHeaderCellLabel": "アクション", - "xpack.watcher.sections.watchHistoryItem.actionsSummary.reasonHeaderCellLabel": "理由", - "xpack.watcher.sections.watchHistoryItem.actionsSummary.stateHeaderCellLabel": "ステータス", - "xpack.watcher.sections.watchHistoryItem.actionsSummary.typeHeaderCellLabel": "タイプ", - "xpack.watcher.sections.watchHistoryItem.executionOutputLabel": "実行アウトプット:", - "xpack.watcher.sections.watchHistoryItem.watchSummary.executionStatusLabel": "実行ステータス:", "xpack.watcher.sections.watchList.createAdvancedWatchButtonLabel": "高度なウォッチを作成", "xpack.watcher.sections.watchList.createAdvancedWatchTooltip": "生 JSON のカスタムウォッチをセットアップします", "xpack.watcher.sections.watchList.createThresholdAlertButtonLabel": "しきい値アラートを作成", "xpack.watcher.sections.watchList.createThresholdAlertButtonTooltip": "特定の条件でアラートを送信します", - "xpack.watcher.sections.watchList.deleteSelectedWatchesConfirmModal.deleteButtonLabel": "{numWatchesToDelete, plural, one {# ウォッチ} other {# ウォッチ}}を削除 ", - "xpack.watcher.sections.watchList.deleteSelectedWatchesConfirmModal.descriptionText": "{numWatchesToDelete, plural, one {# ウォッチ} other {# ウォッチ}}が永久に削除されます。よろしいですか?", - "xpack.watcher.sections.watchList.deleteSelectedWatchesErrorNotification.descriptionText": "選択された {numTotal} 件中 {numErrors} 件の{numWatchesToDelete, plural, one {# ウォッチ} other {# ウォッチ}}を削除できませんでした", - "xpack.watcher.sections.watchList.deleteSelectedWatchesSuccessNotification.descriptionText": "選択された {numTotal} 件中 {numSuccesses} 件の{numWatchesToDelete, plural, one {# ウォッチ} other {# ウォッチ}}が削除されました", - "xpack.watcher.sections.watchList.deleteWatchButtonLabel": "削除", "xpack.watcher.sections.watchList.header": "しきい値アラートを作成", "xpack.watcher.sections.watchList.managementSection.editDisplayName": "編集", "xpack.watcher.sections.watchList.managementSection.newWatchDisplayName": "新規ウォッチ", "xpack.watcher.sections.watchList.managementSection.statusDisplayName": "ステータス", "xpack.watcher.sections.watchList.managementSection.watcherDisplayName": "Watcher", "xpack.watcher.sections.watchList.managementSection.watchesDisplayName": "ウォッチ", - "xpack.watcher.sections.watchList.noPermissionToManageWatchesText": "ウォッチを管理するパーミッションがありません。", - "xpack.watcher.sections.watchList.selectedMultipleWatchText": "ウォッチ", - "xpack.watcher.sections.watchList.selectedSingleWatchText": "ウォッチ", "xpack.watcher.sections.watchList.subhead": "特定のパラメーターに到達した際にメール、Slack メッセージ、ログイベントを送信します。", - "xpack.watcher.sections.watchList.watchesNotFoundText": "ウォッチが見つかりません。", - "xpack.watcher.sections.watchList.watchTable.commentColumnLabel": "コメント", - "xpack.watcher.sections.watchList.watchTable.idColumnLabel": "ID", - "xpack.watcher.sections.watchList.watchTable.lastFiredColumnLabel": "最終実行", - "xpack.watcher.sections.watchList.watchTable.lastTriggeredColumnLabel": "最終実行", - "xpack.watcher.sections.watchList.watchTable.menuEditButtonLabel": "編集", - "xpack.watcher.sections.watchList.watchTable.menuEditButtonTitle": "編集", - "xpack.watcher.sections.watchList.watchTable.nameColumnLabel": "名前", - "xpack.watcher.sections.watchList.watchTable.stateColumnLabel": "ステータス", - "xpack.watcher.server.checkLicense.licenseExpiredTextMessage": "{licenseType} ライセンスが期限切れのため {watcher} を使用できません", - "xpack.watcher.sortableColumn.sortAscendingAriaLabel": "{field} を昇順に並べ替える", - "xpack.watcher.sortableColumn.sortDescendingAriaLabel": "{field} を降順に並べ替える", "xpack.watcher.thresholdPreviewChart.dataDoesNotExistTextMessage": "インデックスと条件がデータを返しませんでした", - "xpack.watcher.thresholdWatchExpression.aggField.itemDescription": "Of", - "xpack.watcher.thresholdWatchExpression.aggField.selectFieldTextMessage": "フィールドを選択します", - "xpack.watcher.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。", - "xpack.watcher.thresholdWatchExpression.aggType.itemDescription": "タイミング", - "xpack.watcher.thresholdWatchExpression.aggType.requiredTypeValidationMessage": "集約タイプを選択してください。", - "xpack.watcher.thresholdWatchExpression.aggType.selectFieldDropdownPlaceholder": "フィールドを選択…", "xpack.watcher.thresholdWatchExpression.comparators.isAboveLabel": "より大きい:", "xpack.watcher.thresholdWatchExpression.comparators.isBelowLabel": "より小さい:", "xpack.watcher.thresholdWatchExpression.fixErrorInExpressionBelowValidationMessage": "下の表現のエラーを修正してください。", - "xpack.watcher.thresholdWatchExpression.groupBy.groupedItemDescription": "次に対してグループ化:", - "xpack.watcher.thresholdWatchExpression.groupBy.itemDescription": "Over", "xpack.watcher.thresholdWatchExpression.groupBy.requiredFieldValidationMessage": "フィールドを選択してください。", - "xpack.watcher.thresholdWatchExpression.groupBy.requiredValueValidationMessage": "値が必要です。", - "xpack.watcher.thresholdWatchExpression.groupBy.selectFieldDropdownPlaceholder": "フィールドを選択…", - "xpack.watcher.thresholdWatchExpression.groupBy.valueMustBeGraterThanZeroValidationMessage": "値は 0 よりも大きい値でなければなりません。", - "xpack.watcher.thresholdWatchExpression.groupBy.valueMustBeValidNumberValidationMessage": "値は有効な数字でなければなりません", "xpack.watcher.thresholdWatchExpression.groupByLabel.allDocumentsLabel": "すべてのドキュメント", "xpack.watcher.thresholdWatchExpression.groupByLabel.topLabel": "トップ", "xpack.watcher.thresholdWatchExpression.thresholdLevel.valueIsRequiredValidationMessage": "値が必要です。", - "xpack.watcher.thresholdWatchExpression.thresholdLevel.valueMustBeValidNumberValidationMessage": "値は有効な数字でなければなりません", - "xpack.watcher.thresholdWatchExpression.timeUnits.dayPluralLabel": "日", - "xpack.watcher.thresholdWatchExpression.timeUnits.daySingularLabel": "日", - "xpack.watcher.thresholdWatchExpression.timeUnits.hourPluralLabel": "時間", - "xpack.watcher.thresholdWatchExpression.timeUnits.hourSingularLabel": "時間", - "xpack.watcher.thresholdWatchExpression.timeUnits.minutePluralLabel": "分", - "xpack.watcher.thresholdWatchExpression.timeUnits.minuteSingularLabel": "分", - "xpack.watcher.thresholdWatchExpression.timeUnits.secondPluralLabel": "秒", - "xpack.watcher.thresholdWatchExpression.timeUnits.secondSingularLabel": "秒", - "xpack.watcher.thresholdWatchExpression.timeWindow.durationIsTooShortValidationMessage": "ウィンドウ期間が短すぎます。使用可能な最低期間は {minimumSize}{minimumUnit} です。", "xpack.watcher.thresholdWatchExpression.timeWindow.durationSizeIsRequiredValidationMessage": "ウィンドウ期間サイズが必要です。", - "xpack.watcher.thresholdWatchExpression.timeWindow.durationSizeMustBePositiveNumberValidationMessage": "ウィンドウ期間サイズはマイナスの数字にできません。", - "xpack.watcher.thresholdWatchExpression.timeWindow.durationSizeMustBeValidNumberValidationMessage": "ウィンドウ期間サイズは有効な数字でなければなりません。", - "xpack.watcher.thresholdWatchExpression.timeWindow.durationUnitIsRequiredValidationMessage": "ウィンドウ期間単位が必要です。", - "xpack.watcher.thresholdWatchExpression.timeWindow.itemDescription": "過去", - "xpack.watcher.timeUnits.dayPluralLabel": "日", - "xpack.watcher.timeUnits.daySingularLabel": "日", - "xpack.watcher.timeUnits.hourPluralLabel": "時間", - "xpack.watcher.timeUnits.hourSingularLabel": "時間", - "xpack.watcher.timeUnits.minutePluralLabel": "分", - "xpack.watcher.timeUnits.minuteSingularLabel": "分", - "xpack.watcher.timeUnits.secondPluralLabel": "秒", - "xpack.watcher.timeUnits.secondSingularLabel": "秒", - "xpack.watcher.toolBarSelectedCountText": "選択済み", - "xpack.watcher.watchActions.email.bodyIsRequiredValidationMessage": "本文が必要です。", - "xpack.watcher.watchActions.email.bodyLabel": "本文", - "xpack.watcher.watchActions.email.emailAddressIsRequiredValidationMessage": "送信先メールアドレスが必要です。", - "xpack.watcher.watchActions.email.emailAddressLabel": "送信先メールアドレス", - "xpack.watcher.watchActions.email.subjectIsRequiredValidationMessage": "件名が必要です。", - "xpack.watcher.watchActions.email.subjectLabel": "件名", - "xpack.watcher.watchActions.errorInActionValidationMessage": "このアクションには保存する前に対処が必要なエラーがあります。", "xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage": "ログテキストが必要です。", - "xpack.watcher.watchActions.logging.logTextLabel": "ログテキスト", - "xpack.watcher.watchActions.removeActionButtonLabel": "{actionTypeName} アクションを削除", - "xpack.watcher.watchActions.slack.messageIsRequiredValidationMessage": "メッセージが必要です。", - "xpack.watcher.watchActions.slack.messageLabel": "メッセージ", - "xpack.watcher.watchActions.slack.recipientIsRequiredValidationMessage": "受取人が必要です。", - "xpack.watcher.watchActions.slack.recipientLabel": "受取人", - "xpack.watcher.watchActionsTitle": "条件が満たされた際に {watchActionsCount, plural, one{# アクション} other {# アクション}} を実行します", "xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。" } } \ No newline at end of file diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 1868441478d40..f39d7f315cd90 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9605,10 +9605,6 @@ "xpack.uptime.snapshotHistogram.series.downLabel": "关闭", "xpack.uptime.snapshotHistogram.series.upLabel": "运行", "xpack.uptime.uptimeFeatureCatalogueTitle": "运行时间", - "xpack.watcher.actionTypeSelect.actionDisabledTextMessage": "已禁用。配置 {elasticsearchYmlText}。", - "xpack.watcher.actionTypeSelect.addNewActionPlaceholder": "添加新操作", - "xpack.watcher.checkLicense.licenseDoesNotSupportTextMessage": "您的 {licenseType} 许可证不支持 {watcher}。请升级您的许可。", - "xpack.watcher.checkLicense.licenseInformationNotAvailableTextMessage": "您不能使用 {watcher},因为许可证信息当前不可用。", "xpack.watcher.constants.actionStates.acknowledgedStateText": "已确认", "xpack.watcher.constants.actionStates.configErrorStateText": "配置错误", "xpack.watcher.constants.actionStates.errorStateText": "错误", @@ -9625,15 +9621,7 @@ "xpack.watcher.constants.watchStates.errorStateText": "错误!", "xpack.watcher.constants.watchStates.firingStateText": "正在发送", "xpack.watcher.constants.watchStates.okStateText": "确定", - "xpack.watcher.create.breadcrumb": "创建", - "xpack.watcher.durationSelect.durationAmountAriaLabel": "持续时间量", - "xpack.watcher.durationSelect.durationTimeUnitAriaLabel": "持续时间单位", - "xpack.watcher.forbiddenMessage.contactAdministratorTextMessage": "请联系您的管理员。", - "xpack.watcher.indexSelect.buildingListTextMessage": "正在生成列表…… 请稍候", - "xpack.watcher.indexSelect.startTypingPlaceholder": "开始键入……", - "xpack.watcher.list.breadcrumb": "Watcher", "xpack.watcher.models.action.actionJsonPropertyMissingBadRequestMessage": "json 参数必须包含 {actionJson} 属性", - "xpack.watcher.models.actionStatus.actionStatusJsonPropertyMissingBadRequestMessage": "json 参数必须包含 {actionStatusJson} 属性", "xpack.watcher.models.actionStatus.idPropertyMissingBadRequestMessage": "json 参数必须包含 {id} 属性", "xpack.watcher.models.actionStatus.notDetermineActionStatusBadImplementationMessage": "无法确定操作状态;操作 = {actionStatusJson}", "xpack.watcher.models.baseAction.idPropertyMissingBadRequestMessage": "json 参数必须包含 {id} 属性", @@ -9650,7 +9638,6 @@ "xpack.watcher.models.baseWatch.watchStatusJsonPropertyMissingBadRequestMessage": "json 参数必须包含 {watchStatusJson} 属性", "xpack.watcher.models.emailAction.actionJsonEmailPropertyMissingBadRequestMessage": "json 参数必须包含 {actionJsonEmail} 属性", "xpack.watcher.models.emailAction.actionJsonEmailToPropertyMissingBadRequestMessage": "json 参数必须包含 {actionJsonEmailTo} 属性", - "xpack.watcher.models.emailAction.description": "“{subject}” 将发送至 {toList}", "xpack.watcher.models.emailAction.selectMessageText": "从您的服务器发送电子邮件。", "xpack.watcher.models.emailAction.simulateButtonLabel": "立即试发电子邮件", "xpack.watcher.models.emailAction.simulateFailMessage": "无法将电子邮件发至 {toList}。", @@ -9670,7 +9657,6 @@ "xpack.watcher.models.jsonWatch.typeName": "高级监视", "xpack.watcher.models.loggingAction.actionJsonLoggingPropertyMissingBadRequestMessage": "json 参数必须包含 {actionJsonLogging} 属性", "xpack.watcher.models.loggingAction.actionJsonLoggingTextPropertyMissingBadRequestMessage": "json 参数必须包含 {actionJsonLoggingText} 属性", - "xpack.watcher.models.loggingAction.description": "记录消息“{text}”", "xpack.watcher.models.loggingAction.selectMessageText": "向日志添加新项。", "xpack.watcher.models.loggingAction.simulateButtonLabel": "立即记录示例消息", "xpack.watcher.models.loggingAction.simulateFailMessage": "无法记录示例消息。", @@ -9685,7 +9671,6 @@ "xpack.watcher.models.pagerDutyAction.simulateMessage": "PagerDuty 事件已发送。", "xpack.watcher.models.slackAction.actionJsonSlackMessagePropertyMissingBadRequestMessage": "json 参数必须包含 {actionJsonSlackMessage} 属性", "xpack.watcher.models.slackAction.actionJsonSlackPropertyMissingBadRequestMessage": "json 参数必须包含 {actionJsonSlack} 属性", - "xpack.watcher.models.slackAction.description": "Slack 消息将发至 {toList}", "xpack.watcher.models.slackAction.selectMessageText": "向 slack 用户或渠道发送消息。", "xpack.watcher.models.slackAction.simulateButtonLabel": "立即发送示例消息", "xpack.watcher.models.slackAction.simulateFailMessage": "无法将示例 Slack 消息发至 {toList}。", @@ -9704,209 +9689,35 @@ "xpack.watcher.models.watchHistoryItem.watchIdPropertyMissingBadRequestMessage": "json 参数必须包含 {watchId} 属性", "xpack.watcher.models.watchStatus.idPropertyMissingBadRequestMessage": "json 参数必须包含 {id} 属性", "xpack.watcher.models.watchStatus.watchStatusJsonPropertyMissingBadRequestMessage": "json 参数必须包含 {watchStatusJson} 属性", - "xpack.watcher.models.webhookAction.description": "Webhook 将在 {fullPath} 上触发 {method} 请求。", - "xpack.watcher.models.webhookAction.simulateFailMessage": "无法将请求发送至 {fullPath}", - "xpack.watcher.models.webhookAction.simulateMessage": "样例请求已发送到 {fullPath}", - "xpack.watcher.sections.watchDetail.actionStatusTable.acknowledgeButtonLabel": "确认", - "xpack.watcher.sections.watchDetail.actionStatusTable.acknowledgeButtonTitle": "确认", - "xpack.watcher.sections.watchDetail.actionStatusTable.actionColumnLabel": "操作", - "xpack.watcher.sections.watchDetail.actionStatusTable.stateColumnLabel": "状态", - "xpack.watcher.sections.watchDetail.actionStatusTotalErrors": "{total, number} {total, plural, one {个错误} other {个错误}}", - "xpack.watcher.sections.watchDetail.activateButtonLabel": "激活", - "xpack.watcher.sections.watchDetail.currentStatusTitle": "当前状态", - "xpack.watcher.sections.watchDetail.deactivateButtonLabel": "停用", - "xpack.watcher.sections.watchDetail.deleteButtonLabel": "删除", - "xpack.watcher.sections.watchDetail.deleteConfirmModal.deleteWatchButtonLabel": "删除监视", - "xpack.watcher.sections.watchDetail.deleteConfirmModal.description": "这将永久删除该监视。是否确定?", - "xpack.watcher.sections.watchDetail.deleteWatchSuccessNotificationText": "已删除 {watchName}", - "xpack.watcher.sections.watchDetail.errorDisplayModalTitleText": "“{actionId}” 操作中有错误", - "xpack.watcher.sections.watchDetail.noActionsFoundText": "未找到操作。", - "xpack.watcher.sections.watchDetail.watchHistory.watchHistoryLoadingText": "正在加载监视历史记录", - "xpack.watcher.sections.watchDetail.watchHistory.watchHistoryNotFoundText": "未找到任何监视历史记录。", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.allLabel": "全部", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last1HourLabel": "过去 1 小时", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last1YearLabel": "过去 1 年", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last24HoursLabel": "过去 24 小时", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last30DaysLabel": "过去 30 天", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last6MonthsLabel": "过去 6 个月", - "xpack.watcher.sections.watchDetail.watchHistoryRangeFilter.last7DaysLabel": "过去 7 天", - "xpack.watcher.sections.watchDetail.watchHistoryTable.commentColumnLabel": "注释", - "xpack.watcher.sections.watchDetail.watchHistoryTable.stateColumnLabel": "状态", - "xpack.watcher.sections.watchDetail.watchHistoryTable.triggerTimeColumnLabel": "触发时间", - "xpack.watcher.sections.watchDetail.watchHistoryTitle": "监视历史记录", - "xpack.watcher.sections.watchEdit.actionsExecuteSummary.actionColumnLabel": "操作", - "xpack.watcher.sections.watchEdit.actionsExecuteSummary.modeColumnLabel": "模式", - "xpack.watcher.sections.watchEdit.actionsExecuteSummary.reasonColumnLabel": "原因", - "xpack.watcher.sections.watchEdit.actionsExecuteSummary.stateColumnLabel": "状态", - "xpack.watcher.sections.watchEdit.actionsExecuteSummary.typeColumnLabel": "类型", - "xpack.watcher.sections.watchEdit.detail.idIsRequiredValidationMessage": "ID 必填。", - "xpack.watcher.sections.watchEdit.detail.idLabel": "ID", - "xpack.watcher.sections.watchEdit.detail.idNamingDescription": "ID 必须以字母或下划线开头,并只能包含字母、下划线、短划线和数字。", - "xpack.watcher.sections.watchEdit.detail.invalidJSONErrorMessage": "JSON 无效", - "xpack.watcher.sections.watchEdit.detail.nameLabel": "名称", - "xpack.watcher.sections.watchEdit.detail.syntaxLinkLabel": "语法", - "xpack.watcher.sections.watchEdit.detail.watchJSONLabel": "监视 JSON", - "xpack.watcher.sections.watchEdit.executeDetail.actionOverrides.actionModesLabel": "操作模式", - "xpack.watcher.sections.watchEdit.executeDetail.actionOverridesButtonLabel": "操作覆盖", - "xpack.watcher.sections.watchEdit.executeDetail.conditionOverrides.ignoreConditionLabel": "忽略条件(条件始终为 true)", - "xpack.watcher.sections.watchEdit.executeDetail.conditionOverridesButtonLabel": "条件覆盖", - "xpack.watcher.sections.watchEdit.executeDetail.inputOverrides.alternativeInputLabel": "备用输入", - "xpack.watcher.sections.watchEdit.executeDetail.inputOverrides.alternativeInputLinkText": "从之前执行复制", - "xpack.watcher.sections.watchEdit.executeDetail.inputOverrides.invalidJSONErrorMessage": "JSON 无效", - "xpack.watcher.sections.watchEdit.executeDetail.inputOverridesButtonLabel": "输入覆盖", - "xpack.watcher.sections.watchEdit.executeDetail.triggerOverrides.scheduledTimeLabel": "排定时间", - "xpack.watcher.sections.watchEdit.executeDetail.triggerOverrides.scheduledTimePlaceholder": "现在", - "xpack.watcher.sections.watchEdit.executeDetail.triggerOverrides.triggeredTimeLabel": "触发时间", - "xpack.watcher.sections.watchEdit.executeDetail.triggerOverrides.triggeredTimePlaceholder": "现在", - "xpack.watcher.sections.watchEdit.executeDetail.triggerOverridesButtonLabel": "触发覆盖", - "xpack.watcher.sections.watchEdit.executeInfoPanel.howToModifyWatchExecutionDescription": "要修改执行监视的方式,请展开以下部分", - "xpack.watcher.sections.watchEdit.executeInfoPanel.testWatchExecutionTitle": "测试您的监视执行", - "xpack.watcher.sections.watchEdit.json.deleteConfirmModal.description": "这将永久删除该监视。是否确定?", - "xpack.watcher.sections.watchEdit.json.deleteConfirmModal.overwriteWatchButtonLabel": "删除监视", - "xpack.watcher.sections.watchEdit.json.deleteSuccessNotificationText": "已删除“{watchDisplayName}”", "xpack.watcher.sections.watchEdit.json.editTabLabel": "编辑", - "xpack.watcher.sections.watchEdit.json.editTabTitle": "编辑监视", - "xpack.watcher.sections.watchEdit.json.saveConfirmModal.description": "ID “{watchId}{watchNameMessageFragment}” 的监视已存在。确定要覆盖?", - "xpack.watcher.sections.watchEdit.json.saveConfirmModal.descriptionFragmentText": "(名称:“{existingWatchName}”)", - "xpack.watcher.sections.watchEdit.json.saveConfirmModal.overwriteWatchButtonLabel": "覆盖监视", "xpack.watcher.sections.watchEdit.json.saveSuccessNotificationText": "已保存“{watchDisplayName}”", - "xpack.watcher.sections.watchEdit.json.simulateResultsTabLabel": "模拟结果", - "xpack.watcher.sections.watchEdit.json.simulateResultsTabTitle": "模拟结果", "xpack.watcher.sections.watchEdit.json.simulateTabLabel": "模拟", - "xpack.watcher.sections.watchEdit.json.simulateTabTitle": "模拟监视", - "xpack.watcher.sections.watchEdit.json.simulateWatchButtonLabel": "模拟监视", - "xpack.watcher.sections.watchEdit.json.simulationOutputLabel": "模拟输出:", - "xpack.watcher.sections.watchEdit.json.warningPossibleInvalidSlackAction.description": "此监视具有不包含“to”属性的 Slack 操作。 只有在 Elasticsearch 的 Slack“message_default”中指定了“to”属性,此监视才有效。", - "xpack.watcher.sections.watchEdit.json.watchErrorsWarning.confirmSaveWatch": "保存监视", - "xpack.watcher.sections.watchEdit.threshold.matchingFollowingConditionTitle": "匹配以下条件", "xpack.watcher.sections.watchEdit.threshold.saveButtonLabel": "保存", - "xpack.watcher.sections.watchEdit.threshold.saveConfirmModal.description": "ID “{watchId}{watchNameMessageFragment}” 的监视已存在。确定要覆盖?", - "xpack.watcher.sections.watchEdit.threshold.saveConfirmModal.descriptionFragmentText": "(名称:“{existingWatchName}”)", - "xpack.watcher.sections.watchEdit.threshold.saveConfirmModal.overwriteWatchButtonLabel": "覆盖监视", - "xpack.watcher.sections.watchEdit.threshold.saveSuccessNotificationText": "已保存“{watchDisplayName}”", - "xpack.watcher.sections.watchEdit.titleBar.cancelButtonLabel": "取消", - "xpack.watcher.sections.watchEdit.titleBar.deleteButtonLabel": "删除", - "xpack.watcher.sections.watchEdit.titleBar.deleteButtonTooltip": "删除监视", - "xpack.watcher.sections.watchEdit.titleBar.newWatchTitle": "新建监视", - "xpack.watcher.sections.watchEdit.titleBar.saveButtonLabel": "保存", - "xpack.watcher.sections.watchEdit.titlePanel.associatedTimeFieldIsMissedInIndexQueryValidationMessage": "您的索引查询没有关联的时间字段", - "xpack.watcher.sections.watchEdit.titlePanel.createNewTypeOfWatchTitle": "创建新的 {typeName}", - "xpack.watcher.sections.watchEdit.titlePanel.enterOneOrMoreIndicesValidationMessage": "输入一个或多个索引", "xpack.watcher.sections.watchEdit.titlePanel.howToBroadenSearchQueryDescription": "使用 * 拓宽您的搜索查询", "xpack.watcher.sections.watchEdit.titlePanel.indicesToQueryLabel": "要查询的索引", - "xpack.watcher.sections.watchEdit.titlePanel.intervalSizeIsRequiredValidationMessage": "“时间间隔大小”必填", - "xpack.watcher.sections.watchEdit.titlePanel.intervalSizeMinimumDurationValidationMessage": " 最小持续时间为 {minSize}{minUnit}", - "xpack.watcher.sections.watchEdit.titlePanel.intervalSizeNegativeNumberValidationMessage": "时间间隔大小不能为负数", - "xpack.watcher.sections.watchEdit.titlePanel.intervalSizeUnitIsRequiredValidationMessage": "需要时间间隔大小", - "xpack.watcher.sections.watchEdit.titlePanel.intervalSizeValidNumberValidationMessage": "时间间隔大小必须为有效数值", - "xpack.watcher.sections.watchEdit.titlePanel.queryDoesNotMatchAnyValidIndicesValidationMessage": "您的索引查询不匹配任何有效索引", - "xpack.watcher.sections.watchEdit.titlePanel.timeFieldIsRequiredValidationText": "需要时间字段", "xpack.watcher.sections.watchEdit.titlePanel.timeFieldLabel": "时间字段", "xpack.watcher.sections.watchEdit.titlePanel.timeFieldOptionLabel": "选择字段", "xpack.watcher.sections.watchEdit.titlePanel.watchIntervalLabel": "运行监视的频率", - "xpack.watcher.sections.watchEdit.titlePanel.watchNameIsRequiredValidationMessage": "“名称”必填", "xpack.watcher.sections.watchEdit.titlePanel.watchNameLabel": "名称", - "xpack.watcher.sections.watchEdit.titlePanel.watchNamePlaceholder": "我的阈值监视", - "xpack.watcher.sections.watchEdit.watchActionModeTable.actionColumnLabel": "操作", - "xpack.watcher.sections.watchEdit.watchActionModeTable.modeColumnLabel": "模式", - "xpack.watcher.sections.watchEdit.watchActionModeTable.typeColumnLabel": "类型", - "xpack.watcher.sections.watchEdit.watchExecuteSummary.simulationStatusLabel": "模拟状态:", - "xpack.watcher.sections.watchHistoryItem.actionsSummary.actionHeaderCellLabel": "操作", - "xpack.watcher.sections.watchHistoryItem.actionsSummary.reasonHeaderCellLabel": "原因", - "xpack.watcher.sections.watchHistoryItem.actionsSummary.stateHeaderCellLabel": "状态", - "xpack.watcher.sections.watchHistoryItem.actionsSummary.typeHeaderCellLabel": "类型", - "xpack.watcher.sections.watchHistoryItem.executionOutputLabel": "执行输出:", - "xpack.watcher.sections.watchHistoryItem.watchSummary.executionStatusLabel": "执行状态:", "xpack.watcher.sections.watchList.createAdvancedWatchButtonLabel": "创建高级监视", "xpack.watcher.sections.watchList.createAdvancedWatchTooltip": "以原始 JSON 格式设置定制监视", "xpack.watcher.sections.watchList.createThresholdAlertButtonLabel": "创建阈值告警", "xpack.watcher.sections.watchList.createThresholdAlertButtonTooltip": "在特定条件下发送告警", - "xpack.watcher.sections.watchList.deleteSelectedWatchesConfirmModal.deleteButtonLabel": "删除 {numWatchesToDelete, plural, one {# 个监视} other {# 个监视}} ", - "xpack.watcher.sections.watchList.deleteSelectedWatchesConfirmModal.descriptionText": "这将永久删除 {numWatchesToDelete, plural, one {# 个监视} other {# 个监视}}。是否确定?", - "xpack.watcher.sections.watchList.deleteSelectedWatchesErrorNotification.descriptionText": "已选的 {numTotal}{numWatchesToDelete, plural, one {# 个监视} other {# 个监视}}有 {numErrors} 个无法删除", - "xpack.watcher.sections.watchList.deleteSelectedWatchesSuccessNotification.descriptionText": "已选的 {numTotal}{numWatchesToDelete, plural, one {# 个监视} other {# 个监视}}有 {numSuccesses} 个已删除", - "xpack.watcher.sections.watchList.deleteWatchButtonLabel": "删除", - "xpack.watcher.sections.watchList.header": "创建阈值告警", "xpack.watcher.sections.watchList.managementSection.editDisplayName": "编辑", "xpack.watcher.sections.watchList.managementSection.newWatchDisplayName": "新建监视", "xpack.watcher.sections.watchList.managementSection.statusDisplayName": "状态", "xpack.watcher.sections.watchList.managementSection.watcherDisplayName": "Watcher", "xpack.watcher.sections.watchList.managementSection.watchesDisplayName": "监视", - "xpack.watcher.sections.watchList.noPermissionToManageWatchesText": "您无权管理监视。", - "xpack.watcher.sections.watchList.selectedMultipleWatchText": "监视", - "xpack.watcher.sections.watchList.selectedSingleWatchText": "监视", - "xpack.watcher.sections.watchList.subhead": "特定参数命中时,发出电子邮件、Slack 消息和日志事件", - "xpack.watcher.sections.watchList.watchesNotFoundText": "未找到任何监视。", - "xpack.watcher.sections.watchList.watchTable.commentColumnLabel": "注释", - "xpack.watcher.sections.watchList.watchTable.idColumnLabel": "ID", - "xpack.watcher.sections.watchList.watchTable.lastFiredColumnLabel": "最后发送时间", - "xpack.watcher.sections.watchList.watchTable.lastTriggeredColumnLabel": "最后触发时间", - "xpack.watcher.sections.watchList.watchTable.menuEditButtonLabel": "编辑", - "xpack.watcher.sections.watchList.watchTable.menuEditButtonTitle": "编辑", - "xpack.watcher.sections.watchList.watchTable.nameColumnLabel": "名称", - "xpack.watcher.sections.watchList.watchTable.stateColumnLabel": "状态", - "xpack.watcher.server.checkLicense.licenseExpiredTextMessage": "您不能使用 {watcher},因为您的{licenseType}许可已过期", - "xpack.watcher.sortableColumn.sortAscendingAriaLabel": "升序排序 {field}", - "xpack.watcher.sortableColumn.sortDescendingAriaLabel": "降序排序 {field}", "xpack.watcher.thresholdPreviewChart.dataDoesNotExistTextMessage": "您的索引和条件未返回任何数据", - "xpack.watcher.thresholdWatchExpression.aggField.itemDescription": "/", - "xpack.watcher.thresholdWatchExpression.aggField.selectFieldTextMessage": "选择字段", - "xpack.watcher.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "请选择字段。", - "xpack.watcher.thresholdWatchExpression.aggType.itemDescription": "当", - "xpack.watcher.thresholdWatchExpression.aggType.requiredTypeValidationMessage": "请选择聚合类型。", - "xpack.watcher.thresholdWatchExpression.aggType.selectFieldDropdownPlaceholder": "选择字段……", "xpack.watcher.thresholdWatchExpression.comparators.isAboveLabel": "高于", "xpack.watcher.thresholdWatchExpression.comparators.isBelowLabel": "低于", "xpack.watcher.thresholdWatchExpression.fixErrorInExpressionBelowValidationMessage": "请解决如下表达式中的错误。", - "xpack.watcher.thresholdWatchExpression.groupBy.groupedItemDescription": "分组依据", - "xpack.watcher.thresholdWatchExpression.groupBy.itemDescription": "依据", "xpack.watcher.thresholdWatchExpression.groupBy.requiredFieldValidationMessage": "请选择字段。", - "xpack.watcher.thresholdWatchExpression.groupBy.requiredValueValidationMessage": "需要值。", - "xpack.watcher.thresholdWatchExpression.groupBy.selectFieldDropdownPlaceholder": "选择字段……", - "xpack.watcher.thresholdWatchExpression.groupBy.valueMustBeGraterThanZeroValidationMessage": "值必须大于 0。", - "xpack.watcher.thresholdWatchExpression.groupBy.valueMustBeValidNumberValidationMessage": "值必须为有效数值。", "xpack.watcher.thresholdWatchExpression.groupByLabel.allDocumentsLabel": "所有文档", "xpack.watcher.thresholdWatchExpression.groupByLabel.topLabel": "顶部", "xpack.watcher.thresholdWatchExpression.thresholdLevel.valueIsRequiredValidationMessage": "需要值。", - "xpack.watcher.thresholdWatchExpression.thresholdLevel.valueMustBeValidNumberValidationMessage": "值必须为有效数值。", - "xpack.watcher.thresholdWatchExpression.timeUnits.dayPluralLabel": "天", - "xpack.watcher.thresholdWatchExpression.timeUnits.daySingularLabel": "天", - "xpack.watcher.thresholdWatchExpression.timeUnits.hourPluralLabel": "小时", - "xpack.watcher.thresholdWatchExpression.timeUnits.hourSingularLabel": "小时", - "xpack.watcher.thresholdWatchExpression.timeUnits.minutePluralLabel": "分钟", - "xpack.watcher.thresholdWatchExpression.timeUnits.minuteSingularLabel": "分钟", - "xpack.watcher.thresholdWatchExpression.timeUnits.secondPluralLabel": "秒", - "xpack.watcher.thresholdWatchExpression.timeUnits.secondSingularLabel": "秒", - "xpack.watcher.thresholdWatchExpression.timeWindow.durationIsTooShortValidationMessage": "窗口持续时间过短。允许的最小值为 {minimumSize}{minimumUnit}。", "xpack.watcher.thresholdWatchExpression.timeWindow.durationSizeIsRequiredValidationMessage": "“窗口持续时间大小”必填。", - "xpack.watcher.thresholdWatchExpression.timeWindow.durationSizeMustBePositiveNumberValidationMessage": "窗口持续时间大小不能为负数。", - "xpack.watcher.thresholdWatchExpression.timeWindow.durationSizeMustBeValidNumberValidationMessage": "窗口持续时间大小必须为有效数值。", - "xpack.watcher.thresholdWatchExpression.timeWindow.durationUnitIsRequiredValidationMessage": "“窗口持续时间单位”必填。", - "xpack.watcher.thresholdWatchExpression.timeWindow.itemDescription": "过去", - "xpack.watcher.timeUnits.dayPluralLabel": "天", - "xpack.watcher.timeUnits.daySingularLabel": "天", - "xpack.watcher.timeUnits.hourPluralLabel": "小时", - "xpack.watcher.timeUnits.hourSingularLabel": "小时", - "xpack.watcher.timeUnits.minutePluralLabel": "分钟", - "xpack.watcher.timeUnits.minuteSingularLabel": "分钟", - "xpack.watcher.timeUnits.secondPluralLabel": "秒", - "xpack.watcher.timeUnits.secondSingularLabel": "秒", - "xpack.watcher.toolBarSelectedCountText": "已选", - "xpack.watcher.watchActions.email.bodyIsRequiredValidationMessage": "“正文”必填。", - "xpack.watcher.watchActions.email.bodyLabel": "正文", - "xpack.watcher.watchActions.email.emailAddressIsRequiredValidationMessage": "“收件人电子邮件地址”必填。", - "xpack.watcher.watchActions.email.emailAddressLabel": "收件人电子邮件地址", - "xpack.watcher.watchActions.email.subjectIsRequiredValidationMessage": "“主题”必填。", - "xpack.watcher.watchActions.email.subjectLabel": "主题", - "xpack.watcher.watchActions.errorInActionValidationMessage": "此操作中有错误,在可以保存之前,需要解决这些错误。", "xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage": "“日志文本”必填。", - "xpack.watcher.watchActions.logging.logTextLabel": "日志文本", - "xpack.watcher.watchActions.removeActionButtonLabel": "删除 {actionTypeName} 操作", - "xpack.watcher.watchActions.slack.messageIsRequiredValidationMessage": "“消息”必填。", - "xpack.watcher.watchActions.slack.messageLabel": "消息", - "xpack.watcher.watchActions.slack.recipientIsRequiredValidationMessage": "“收件人”必填。", - "xpack.watcher.watchActions.slack.recipientLabel": "收件人", - "xpack.watcher.watchActionsTitle": "满足后将执行 {watchActionsCount, plural, one{# 个操作} other {# 个操作}}", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} \ No newline at end of file +} diff --git a/x-pack/test/functional/apps/watcher/watcher_test.js b/x-pack/test/functional/apps/watcher/watcher_test.js index c07ab3b7155d7..7477e9554af87 100644 --- a/x-pack/test/functional/apps/watcher/watcher_test.js +++ b/x-pack/test/functional/apps/watcher/watcher_test.js @@ -12,6 +12,8 @@ const watchName = 'watch Name'; const updatedName = 'updatedName'; export default function ({ getService, getPageObjects }) { const browser = getService('browser'); + const find = getService('find'); + const retry = getService('retry'); const testSubjects = getService('testSubjects'); const log = getService('log'); const PageObjects = getPageObjects(['security', 'common', 'header', 'settings', 'watcher']); @@ -19,8 +21,7 @@ export default function ({ getService, getPageObjects }) { describe('watcher_test', function () { before('initialize tests', async () => { await browser.setWindowSize(1600, 1000); - await PageObjects.common.navigateToApp('settings'); - await testSubjects.click('watcher'); + await PageObjects.common.navigateToApp('watcher'); await PageObjects.watcher.clearAllWatches(); }); @@ -31,32 +32,28 @@ export default function ({ getService, getPageObjects }) { expect(watch.name).to.be(watchName); }); - it('should prompt user to check to see if you can override a watch with a sameID', async () => { + it('should not allow a user to save a watch with the same ID', async () => { await PageObjects.watcher.createWatch(watchID, updatedName); - const modal = await testSubjects.find('confirmModalBodyText'); - const modalText = await modal.getVisibleText(); - expect(modalText).to.be(`Watch with ID "${watchID}" (name: "${watchName}") already exists. Do you want to overwrite it?`); - await testSubjects.click('confirmModalConfirmButton'); - const watch = await PageObjects.watcher.getWatch(watchID); - expect(watch.id).to.be(watchID); - expect(watch.name).to.be(updatedName); + const errorCallout = await testSubjects.find('sectionErrorMessage'); + const errorCalloutText = await errorCallout.getVisibleText(); + expect(errorCalloutText).to.be(`There is already a watch with ID '${watchID}'.`); }); //delete the watch it('should delete the watch', async () => { + // Navigate to the main list page + await PageObjects.common.navigateToApp('watcher'); const watchList = indexBy(await PageObjects.watcher.getWatches(), 'id'); log.debug(watchList); - expect(watchList.watchID.name).to.eql([updatedName]); + expect(watchList.watchID.name).to.eql([watchName]); await PageObjects.watcher.deleteWatch(watchID); - const modal = await testSubjects.find('confirmModalBodyText'); - const modalText = await modal.getVisibleText(); - expect(modalText).to.be('This will permanently delete 1 Watch. Are you sure?'); await testSubjects.click('confirmModalConfirmButton'); await PageObjects.header.waitUntilLoadingHasFinished(); - const watchList1 = indexBy(await PageObjects.watcher.getWatches(), 'id'); - log.debug(watchList1); - expect(watchList1).to.not.have.key(watchID); + retry.try(async () => { + const row = await find.byCssSelector('.euiTableRow'); + const cell = await row.findByCssSelector('td:nth-child(1)'); + expect(cell.getVisibleText()).to.equal('No watches to show'); + }); }); - }); } diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 3b59509ab4d22..21abab9238f69 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -282,7 +282,11 @@ export default async function ({ readConfigFile }) { }, apm: { pathname: '/app/apm', - } + }, + watcher: { + pathname: '/app/kibana', + hash: '/management/elasticsearch/watcher/watches/', + }, }, // choose where esArchiver should load archives from diff --git a/x-pack/test/functional/page_objects/watcher_page.js b/x-pack/test/functional/page_objects/watcher_page.js index b34750b2e6d95..5b09f561ca777 100644 --- a/x-pack/test/functional/page_objects/watcher_page.js +++ b/x-pack/test/functional/page_objects/watcher_page.js @@ -13,9 +13,9 @@ export function WatcherPageProvider({ getPageObjects, getService }) { class WatcherPage { async clearAllWatches() { - const checkBoxExists = await testSubjects.exists('selectAllWatchesCheckBox'); + const checkBoxExists = await testSubjects.exists('checkboxSelectAll'); if (checkBoxExists) { - await testSubjects.click('selectAllWatchesCheckBox'); + await testSubjects.click('checkboxSelectAll'); await testSubjects.click('btnDeleteWatches'); await testSubjects.click('confirmModalConfirmButton'); await PageObjects.header.waitUntilLoadingHasFinished(); @@ -23,31 +23,33 @@ export function WatcherPageProvider({ getPageObjects, getService }) { } async createWatch(watchName, name) { - await testSubjects.click('createAdvancedWatchButton'); + await testSubjects.click('createWatchButton'); + await testSubjects.click('jsonWatchCreateLink'); await find.setValue('#id', watchName); - await find.setValue('#name', name); - await testSubjects.click('btnSaveWatch'); + await find.setValue('#watchName', name); + await find.clickByCssSelector('[type="submit"]'); await PageObjects.header.waitUntilLoadingHasFinished(); } async getWatch(watchID) { - const watchRow = await testSubjects.find(`watchRow-${watchID}`); - const text = await watchRow.getVisibleText(); - const columns = text.split('\n'); + const watchIdColumn = await testSubjects.find(`watchIdColumn-${watchID}`); + const watchNameColumn = await testSubjects.find(`watchNameColumn-${watchID}`); + const id = await watchIdColumn.getVisibleText(); + const name = await watchNameColumn.getVisibleText(); return { - id: columns[0], - name: columns[1] + id, + name, }; } async deleteWatch() { - await testSubjects.click('selectAllWatchesCheckBox'); + await testSubjects.click('checkboxSelectAll'); await testSubjects.click('btnDeleteWatches'); } //get all the watches in the list async getWatches() { - const watches = await find.allByCssSelector('.kuiTableRow'); + const watches = await find.allByCssSelector('.euiTableRow'); return mapAsync(watches, async watch => { const checkBox = await watch.findByCssSelector('td:nth-child(1)'); const id = await watch.findByCssSelector('td:nth-child(2)'); @@ -56,7 +58,7 @@ export function WatcherPageProvider({ getPageObjects, getService }) { return { checkBox: (await checkBox.getProperty('innerHTML')).includes('input'), id: await id.getVisibleText(), - name: (await name.getVisibleText()).split(',').map(role => role.trim()) + name: (await name.getVisibleText()).split(',').map(role => role.trim()), }; }); }