From a170f7ab5b5319c3309f813a4bad8c409c846f1f Mon Sep 17 00:00:00 2001 From: Bill McConaghy Date: Thu, 7 Mar 2019 12:01:39 -0500 Subject: [PATCH] replacing Angular watch list UI with React one (#32401) * replacing Angular watch list UI with React one * fixing TS issues * addressing PR feedback * fixing TS issues * more TS fixes * TS fix * addressing more PR feedback * TS fixes * removing unused/incompatible translations * fixing functional test * prettier fix * fixing typp * fixing missing comma * fixing data-test-subject typo * fixing functional test * fixing functional tests * fixing delete functional tests * addressing PR feedback --- .../translations/translations/zh-CN.json | 17 -- .../{action_modes.js => action_modes.ts} | 6 +- .../{action_states.js => action_states.ts} | 16 +- .../{action_types.js => action_types.ts} | 6 +- .../constants/{agg_types.js => agg_types.ts} | 6 +- .../{comparators.js => comparators.ts} | 6 +- .../{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 +- .../{pagination.js => pagination.ts} | 4 +- .../constants/{watch_history.js => plugin.ts} | 4 +- ...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 +- .../watcher/common/constants/watch_history.ts | 9 + .../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 +- .../components/delete_watches_modal.tsx | 88 ++++++ x-pack/plugins/watcher/public/index.scss | 2 +- x-pack/plugins/watcher/public/lib/api.ts | 33 +++ .../public/lib/manage_angular_lifecycle.js | 23 ++ .../plugin.js => public/models/index.d.ts} | 7 +- .../watch_list/components/watch_list.tsx | 269 ++++++++++++++++++ .../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 | 210 -------------- .../components/watch_table/index.js | 7 - .../components/watch_table/watch_table.html | 174 ----------- .../components/watch_table/watch_table.js | 89 ------ .../public/sections/watch_list/index.scss | 6 + .../sections/watch_list/watch_list_route.html | 5 +- .../sections/watch_list/watch_list_route.js | 88 +++--- .../api/watches/register_delete_route.js | 35 ++- .../functional/apps/watcher/watcher_test.js | 20 +- .../functional/page_objects/watcher_page.js | 21 +- 42 files changed, 609 insertions(+), 807 deletions(-) rename x-pack/plugins/watcher/common/constants/{action_modes.js => action_modes.ts} (94%) rename x-pack/plugins/watcher/common/constants/{action_states.js => action_states.ts} (82%) rename x-pack/plugins/watcher/common/constants/{action_types.js => action_types.ts} (82%) rename x-pack/plugins/watcher/common/constants/{agg_types.js => agg_types.ts} (82%) rename x-pack/plugins/watcher/common/constants/{comparators.js => comparators.ts} (78%) rename x-pack/plugins/watcher/common/constants/{error_codes.js => error_codes.ts} (85%) rename x-pack/plugins/watcher/common/constants/{es_scroll_settings.js => es_scroll_settings.ts} (77%) rename x-pack/plugins/watcher/common/constants/{index.js => index.ts} (100%) rename x-pack/plugins/watcher/common/constants/{index_names.js => index_names.ts} (84%) rename x-pack/plugins/watcher/common/constants/{lists.js => lists.ts} (74%) rename x-pack/plugins/watcher/common/constants/{pagination.js => pagination.ts} (77%) rename x-pack/plugins/watcher/common/constants/{watch_history.js => plugin.ts} (78%) rename x-pack/plugins/watcher/common/constants/{refresh_intervals.js => refresh_intervals.ts} (78%) rename x-pack/plugins/watcher/common/constants/{routes.js => routes.ts} (84%) rename x-pack/plugins/watcher/common/constants/{sort_orders.js => sort_orders.ts} (77%) rename x-pack/plugins/watcher/common/constants/{time_units.js => time_units.ts} (81%) create mode 100644 x-pack/plugins/watcher/common/constants/watch_history.ts delete mode 100644 x-pack/plugins/watcher/common/constants/watch_state_comments.js create mode 100644 x-pack/plugins/watcher/common/constants/watch_state_comments.ts rename x-pack/plugins/watcher/common/constants/{watch_states.js => watch_states.ts} (77%) rename x-pack/plugins/watcher/common/constants/{watch_types.js => watch_types.ts} (77%) create mode 100644 x-pack/plugins/watcher/public/components/delete_watches_modal.tsx create mode 100644 x-pack/plugins/watcher/public/lib/api.ts create mode 100644 x-pack/plugins/watcher/public/lib/manage_angular_lifecycle.js rename x-pack/plugins/watcher/{common/constants/plugin.js => public/models/index.d.ts} (75%) create mode 100644 x-pack/plugins/watcher/public/sections/watch_list/components/watch_list.tsx delete mode 100644 x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/_index.scss delete mode 100644 x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/_watch_list.scss delete mode 100644 x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/index.js delete mode 100644 x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.html delete mode 100644 x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.js delete mode 100644 x-pack/plugins/watcher/public/sections/watch_list/components/watch_table/index.js delete mode 100644 x-pack/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.html delete mode 100644 x-pack/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.js create mode 100644 x-pack/plugins/watcher/public/sections/watch_list/index.scss diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 557603eb7d11a5..cc949f1a2c8188 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -8117,31 +8117,14 @@ "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.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.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.thresholdPreviewChart.dataDoesNotExistTextMessage": "您的索引和条件未返回任何数据", "xpack.watcher.thresholdWatchExpression.aggField.itemDescription": "/", diff --git a/x-pack/plugins/watcher/common/constants/action_modes.js b/x-pack/plugins/watcher/common/constants/action_modes.ts similarity index 94% rename from x-pack/plugins/watcher/common/constants/action_modes.js rename to x-pack/plugins/watcher/common/constants/action_modes.ts index 245f237f16efa1..5f703f14b39338 100644 --- a/x-pack/plugins/watcher/common/constants/action_modes.js +++ b/x-pack/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/plugins/watcher/common/constants/action_states.js b/x-pack/plugins/watcher/common/constants/action_states.ts similarity index 82% rename from x-pack/plugins/watcher/common/constants/action_states.js rename to x-pack/plugins/watcher/common/constants/action_states.ts index e34a5fd37ff3ab..e9501fc2a60c4e 100644 --- a/x-pack/plugins/watcher/common/constants/action_states.js +++ b/x-pack/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/plugins/watcher/common/constants/action_types.js b/x-pack/plugins/watcher/common/constants/action_types.ts similarity index 82% rename from x-pack/plugins/watcher/common/constants/action_types.js rename to x-pack/plugins/watcher/common/constants/action_types.ts index 7b408f0d3a0cc0..3bb0d5748347cc 100644 --- a/x-pack/plugins/watcher/common/constants/action_types.js +++ b/x-pack/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', @@ -22,6 +21,5 @@ export const ACTION_TYPES = { PAGERDUTY: 'pagerduty', - UNKNOWN: 'unknown/invalid' - + UNKNOWN: 'unknown/invalid', }; diff --git a/x-pack/plugins/watcher/common/constants/agg_types.js b/x-pack/plugins/watcher/common/constants/agg_types.ts similarity index 82% rename from x-pack/plugins/watcher/common/constants/agg_types.js rename to x-pack/plugins/watcher/common/constants/agg_types.ts index 3f53a36e89127d..1a0f78c33111c6 100644 --- a/x-pack/plugins/watcher/common/constants/agg_types.js +++ b/x-pack/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/plugins/watcher/common/constants/comparators.js b/x-pack/plugins/watcher/common/constants/comparators.ts similarity index 78% rename from x-pack/plugins/watcher/common/constants/comparators.js rename to x-pack/plugins/watcher/common/constants/comparators.ts index 69009b2f604b6a..4a27b7e275b65e 100644 --- a/x-pack/plugins/watcher/common/constants/comparators.js +++ b/x-pack/plugins/watcher/common/constants/comparators.ts @@ -4,10 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -export const COMPARATORS = { - +export const COMPARATORS: { [key: string]: string } = { GREATER_THAN: '>', - LESS_THAN: '<' - + LESS_THAN: '<', }; diff --git a/x-pack/plugins/watcher/common/constants/error_codes.js b/x-pack/plugins/watcher/common/constants/error_codes.ts similarity index 85% rename from x-pack/plugins/watcher/common/constants/error_codes.js rename to x-pack/plugins/watcher/common/constants/error_codes.ts index 2fa875549358f2..f5433ebb576e69 100644 --- a/x-pack/plugins/watcher/common/constants/error_codes.js +++ b/x-pack/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/plugins/watcher/common/constants/es_scroll_settings.js b/x-pack/plugins/watcher/common/constants/es_scroll_settings.ts similarity index 77% rename from x-pack/plugins/watcher/common/constants/es_scroll_settings.js rename to x-pack/plugins/watcher/common/constants/es_scroll_settings.ts index 25bdcca565ee9c..a51a7b117eea69 100644 --- a/x-pack/plugins/watcher/common/constants/es_scroll_settings.js +++ b/x-pack/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/plugins/watcher/common/constants/index.js b/x-pack/plugins/watcher/common/constants/index.ts similarity index 100% rename from x-pack/plugins/watcher/common/constants/index.js rename to x-pack/plugins/watcher/common/constants/index.ts diff --git a/x-pack/plugins/watcher/common/constants/index_names.js b/x-pack/plugins/watcher/common/constants/index_names.ts similarity index 84% rename from x-pack/plugins/watcher/common/constants/index_names.js rename to x-pack/plugins/watcher/common/constants/index_names.ts index 663d932fbe1337..b9ca85963a33da 100644 --- a/x-pack/plugins/watcher/common/constants/index_names.js +++ b/x-pack/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/plugins/watcher/common/constants/lists.js b/x-pack/plugins/watcher/common/constants/lists.ts similarity index 74% rename from x-pack/plugins/watcher/common/constants/lists.js rename to x-pack/plugins/watcher/common/constants/lists.ts index da89b99f828baa..1224b279f4816a 100644 --- a/x-pack/plugins/watcher/common/constants/lists.js +++ b/x-pack/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/plugins/watcher/common/constants/pagination.js b/x-pack/plugins/watcher/common/constants/pagination.ts similarity index 77% rename from x-pack/plugins/watcher/common/constants/pagination.js rename to x-pack/plugins/watcher/common/constants/pagination.ts index fd44ffdecfc3b5..8ae9d769831c56 100644 --- a/x-pack/plugins/watcher/common/constants/pagination.js +++ b/x-pack/plugins/watcher/common/constants/pagination.ts @@ -4,6 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export const PAGINATION = { - PAGE_SIZE: 20 +export const PAGINATION: { [key: string]: number } = { + PAGE_SIZE: 20, }; diff --git a/x-pack/plugins/watcher/common/constants/watch_history.js b/x-pack/plugins/watcher/common/constants/plugin.ts similarity index 78% rename from x-pack/plugins/watcher/common/constants/watch_history.js rename to x-pack/plugins/watcher/common/constants/plugin.ts index 3fad569ab7878b..118e6424679388 100644 --- a/x-pack/plugins/watcher/common/constants/watch_history.js +++ b/x-pack/plugins/watcher/common/constants/plugin.ts @@ -4,6 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export const WATCH_HISTORY = { - INITIAL_RANGE: 'now-1h' +export const PLUGIN: { [key: string]: string } = { + ID: 'watcher', }; diff --git a/x-pack/plugins/watcher/common/constants/refresh_intervals.js b/x-pack/plugins/watcher/common/constants/refresh_intervals.ts similarity index 78% rename from x-pack/plugins/watcher/common/constants/refresh_intervals.js rename to x-pack/plugins/watcher/common/constants/refresh_intervals.ts index 1aff358bcd636e..db1daa865c3234 100644 --- a/x-pack/plugins/watcher/common/constants/refresh_intervals.js +++ b/x-pack/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/plugins/watcher/common/constants/routes.js b/x-pack/plugins/watcher/common/constants/routes.ts similarity index 84% rename from x-pack/plugins/watcher/common/constants/routes.js rename to x-pack/plugins/watcher/common/constants/routes.ts index 97ae0ee6de181c..73a1824c507da4 100644 --- a/x-pack/plugins/watcher/common/constants/routes.js +++ b/x-pack/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/plugins/watcher/common/constants/sort_orders.js b/x-pack/plugins/watcher/common/constants/sort_orders.ts similarity index 77% rename from x-pack/plugins/watcher/common/constants/sort_orders.js rename to x-pack/plugins/watcher/common/constants/sort_orders.ts index 062941929ddc68..c73d458a3018ff 100644 --- a/x-pack/plugins/watcher/common/constants/sort_orders.js +++ b/x-pack/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/plugins/watcher/common/constants/time_units.js b/x-pack/plugins/watcher/common/constants/time_units.ts similarity index 81% rename from x-pack/plugins/watcher/common/constants/time_units.js rename to x-pack/plugins/watcher/common/constants/time_units.ts index ea63c21e222133..c861d47416a804 100644 --- a/x-pack/plugins/watcher/common/constants/time_units.js +++ b/x-pack/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/plugins/watcher/common/constants/watch_history.ts b/x-pack/plugins/watcher/common/constants/watch_history.ts new file mode 100644 index 00000000000000..520fe1019c8c30 --- /dev/null +++ b/x-pack/plugins/watcher/common/constants/watch_history.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 const WATCH_HISTORY: { [key: string]: string } = { + INITIAL_RANGE: 'now-1h', +}; diff --git a/x-pack/plugins/watcher/common/constants/watch_state_comments.js b/x-pack/plugins/watcher/common/constants/watch_state_comments.js deleted file mode 100644 index cfa4d93d8a1400..00000000000000 --- a/x-pack/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/plugins/watcher/common/constants/watch_state_comments.ts b/x-pack/plugins/watcher/common/constants/watch_state_comments.ts new file mode 100644 index 00000000000000..c88425c7c1616e --- /dev/null +++ b/x-pack/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/plugins/watcher/common/constants/watch_states.js b/x-pack/plugins/watcher/common/constants/watch_states.ts similarity index 77% rename from x-pack/plugins/watcher/common/constants/watch_states.js rename to x-pack/plugins/watcher/common/constants/watch_states.ts index 1c546139b688c3..6215b492975cfe 100644 --- a/x-pack/plugins/watcher/common/constants/watch_states.js +++ b/x-pack/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/plugins/watcher/common/constants/watch_types.js b/x-pack/plugins/watcher/common/constants/watch_types.ts similarity index 77% rename from x-pack/plugins/watcher/common/constants/watch_types.js rename to x-pack/plugins/watcher/common/constants/watch_types.ts index 51e89a19b6ca0d..f0ebd3efc40782 100644 --- a/x-pack/plugins/watcher/common/constants/watch_types.js +++ b/x-pack/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/plugins/watcher/public/components/delete_watches_modal.tsx b/x-pack/plugins/watcher/public/components/delete_watches_modal.tsx new file mode 100644 index 00000000000000..2ac41c159e3546 --- /dev/null +++ b/x-pack/plugins/watcher/public/components/delete_watches_modal.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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: + 'This will permanently delete {numWatchesToDelete, plural, one {a watch} other {# watches}}. Are you sure?', + 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 {numWatchesToDelete, plural, one {watch} other {{numSuccesses} out of # selected watches}} ', + values: { numSuccesses, numWatchesToDelete }, + } + ) + ); + } + + if (numErrors > 0) { + toastNotifications.addDanger( + i18n.translate( + 'xpack.watcher.sections.watchList.deleteSelectedWatchesErrorNotification.descriptionText', + { + defaultMessage: + "Couldn't delete {numWatchesToDelete, plural, one {watch} other {{numErrors} out of # selected watches}}", + values: { numErrors, numWatchesToDelete }, + } + ) + ); + } + }} + cancelButtonText={cancelButtonText} + confirmButtonText={confirmButtonText} + > + {confirmModalText} + + + ); +}; diff --git a/x-pack/plugins/watcher/public/index.scss b/x-pack/plugins/watcher/public/index.scss index b66733cc06c7e2..ebf042c7cbd1c4 100644 --- a/x-pack/plugins/watcher/public/index.scss +++ b/x-pack/plugins/watcher/public/index.scss @@ -26,4 +26,4 @@ @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'; +@import 'sections/watch_list/index'; diff --git a/x-pack/plugins/watcher/public/lib/api.ts b/x-pack/plugins/watcher/public/lib/api.ts new file mode 100644 index 00000000000000..e93adf6f71b2ad --- /dev/null +++ b/x-pack/plugins/watcher/public/lib/api.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 { Watch } from 'plugins/watcher/models/watch'; +import chrome from 'ui/chrome'; +import { ROUTES } from '../../common/constants'; +let httpClient: ng.IHttpService; +export const setHttpClient = (anHttpClient: ng.IHttpService) => { + httpClient = anHttpClient; +}; +export const getHttpClient = () => { + return httpClient; +}; +const basePath = chrome.addBasePath(ROUTES.API_ROOT); +export const fetchWatches = async () => { + const { + data: { watches }, + } = await getHttpClient().get(`${basePath}/watches`); + return watches.map((watch: any) => { + return Watch.fromUpstreamJson(watch); + }); +}; +export const deleteWatches = async (watchIds: string[]) => { + const body = { + watchIds, + }; + const { + data: { results }, + } = await getHttpClient().post(`${basePath}/watches/delete`, body); + return results; +}; diff --git a/x-pack/plugins/watcher/public/lib/manage_angular_lifecycle.js b/x-pack/plugins/watcher/public/lib/manage_angular_lifecycle.js new file mode 100644 index 00000000000000..3813e632a0a738 --- /dev/null +++ b/x-pack/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/plugins/watcher/common/constants/plugin.js b/x-pack/plugins/watcher/public/models/index.d.ts similarity index 75% rename from x-pack/plugins/watcher/common/constants/plugin.js rename to x-pack/plugins/watcher/public/models/index.d.ts index bcff9278630192..81787a375d44e3 100644 --- a/x-pack/plugins/watcher/common/constants/plugin.js +++ b/x-pack/plugins/watcher/public/models/index.d.ts @@ -3,7 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -export const PLUGIN = { - ID: 'watcher' -}; +declare module 'plugins/watcher/models/watch' { + export const Watch: any; +} diff --git a/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list.tsx b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list.tsx new file mode 100644 index 00000000000000..45437a5485c9c7 --- /dev/null +++ b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list.tsx @@ -0,0 +1,269 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiInMemoryTable, + EuiLink, + EuiPageContent, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; +import { Moment } from 'moment'; +import { REFRESH_INTERVALS, WATCH_STATES } from '../../../../common/constants'; +import { DeleteWatchesModal } from '../../../components/delete_watches_modal'; +import { fetchWatches } from '../../../lib/api'; + +const stateToIcon: { [key: string]: JSX.Element } = { + [WATCH_STATES.OK]: , + [WATCH_STATES.DISABLED]: , + [WATCH_STATES.FIRING]: , + [WATCH_STATES.ERROR]: , + [WATCH_STATES.CONFIG_ERROR]: , +}; + +const WatchListUi = ({ intl }: { intl: InjectedIntl }) => { + // hooks + const [isWatchesLoading, setIsWatchesLoading] = useState(true); + const [watchesToDelete, setWatchesToDelete] = useState([]); + const [watches, setWatches] = useState([]); + const [selection, setSelection] = useState([]); + const loadWatches = async () => { + const loadedWatches = await fetchWatches(); + setWatches(loadedWatches); + setIsWatchesLoading(false); + }; + useEffect(() => { + loadWatches(); + const refreshIntervalId = setInterval(loadWatches, REFRESH_INTERVALS.WATCH_LIST); + return () => { + clearInterval(refreshIntervalId); + }; + }, []); + 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, + render: (state: string) => { + return ( + + {stateToIcon[state]} + + {state} + + + ); + }, + }, + { + field: 'watchStatus.comment', + name: i18n.translate('xpack.watcher.sections.watchList.watchTable.commentHeader', { + defaultMessage: 'Comment', + }), + sortable: true, + truncateText: true, + }, + { + field: 'watchStatus.lastMetCondition', + name: i18n.translate('xpack.watcher.sections.watchList.watchTable.lastFiredHeader', { + defaultMessage: 'Last fired', + }), + sortable: true, + truncateText: true, + 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, + render: (lastChecked: Moment) => { + return lastChecked ? lastChecked.fromNow() : lastChecked; + }, + }, + { + actions: [ + { + render: (watch: any) => { + const disabled = watch.isSystemWatch; + return ( + + + + ); + }, + }, + ], + }, + ]; + const selectionConfig = { + onSelectionChange: setSelection, + }; + const pagination = { + initialPageSize: 10, + pageSizeOptions: [10, 50, 100], + }; + const searchConfig = { + box: { + incremental: true, + }, + toolsRight: ( + { + setWatchesToDelete(selection.map((selected: any) => selected.id)); + }} + color="danger" + disabled={!selection.length} + > + + + ), + }; + return ( + + { + if (deleted) { + setWatches( + watches.filter((watch: any) => { + return !deleted.includes(watch.id); + }) + ); + } + setWatchesToDelete([]); + }} + watchesToDelete={watchesToDelete} + /> + + + +

+ +

+
+ + +

+ +

+
+
+
+ + + + + + + + + + + + + + + + + } + /> +
+ ); +}; +export const WatchList = injectI18n(WatchListUi); diff --git a/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/_index.scss b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/_index.scss deleted file mode 100644 index 068a77f2e54b28..00000000000000 --- a/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './watch_list'; diff --git a/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/_watch_list.scss b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/_watch_list.scss deleted file mode 100644 index c26daaee9a08ce..00000000000000 --- a/x-pack/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/plugins/watcher/public/sections/watch_list/components/watch_list/index.js b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/index.js deleted file mode 100644 index 1097bb146ff053..00000000000000 --- a/x-pack/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/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.html b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.html deleted file mode 100644 index e0fc117fa0c5c0..00000000000000 --- a/x-pack/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/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.js b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.js deleted file mode 100644 index 548581ea6be7a5..00000000000000 --- a/x-pack/plugins/watcher/public/sections/watch_list/components/watch_list/watch_list.js +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * 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 { 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 'plugins/watcher/services/watches'; -import 'plugins/watcher/services/license'; - -const app = uiModules.get('xpack/watcher'); - -app.directive('watchList', function ($injector, i18n) { - const pagerFactory = $injector.get('pagerFactory'); - const watchesService = $injector.get('xpackWatcherWatchesService'); - const licenseService = $injector.get('xpackWatcherLicenseService'); - const confirmModal = $injector.get('confirmModal'); - const $interval = $injector.get('$interval'); - const kbnUrl = $injector.get('kbnUrl'); - - 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 = () => { - watchesService.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.data.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('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('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 watchesService.deleteWatches(watchIds) - .then(results => { - const numSuccesses = results.numSuccesses; - const numErrors = results.numErrors; - const numTotal = numWatchesToDelete; - - if (numSuccesses > 0) { - toastNotifications.addSuccess( - i18n('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('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.data.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/plugins/watcher/public/sections/watch_list/components/watch_table/index.js b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_table/index.js deleted file mode 100644 index cb748ea0489617..00000000000000 --- a/x-pack/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/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.html b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.html deleted file mode 100644 index b4c0adb60435d9..00000000000000 --- a/x-pack/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/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.js b/x-pack/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.js deleted file mode 100644 index f442b5f8447f4e..00000000000000 --- a/x-pack/plugins/watcher/public/sections/watch_list/components/watch_table/watch_table.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * 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 'ui/check_box'; -import 'ui/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) => { - _.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/plugins/watcher/public/sections/watch_list/index.scss b/x-pack/plugins/watcher/public/sections/watch_list/index.scss new file mode 100644 index 00000000000000..d46f170c06b491 --- /dev/null +++ b/x-pack/plugins/watcher/public/sections/watch_list/index.scss @@ -0,0 +1,6 @@ +/** + * 1. Prevent inherited flexbox layout from compressing this element on IE. + */ +.watchState__message { + flex-basis: auto !important; /* 1 */ +} diff --git a/x-pack/plugins/watcher/public/sections/watch_list/watch_list_route.html b/x-pack/plugins/watcher/public/sections/watch_list/watch_list_route.html index ce7b6a2e892dd4..13203e912b3706 100644 --- a/x-pack/plugins/watcher/public/sections/watch_list/watch_list_route.html +++ b/x-pack/plugins/watcher/public/sections/watch_list/watch_list_route.html @@ -1 +1,4 @@ - + +
+
+ diff --git a/x-pack/plugins/watcher/public/sections/watch_list/watch_list_route.js b/x-pack/plugins/watcher/public/sections/watch_list/watch_list_route.js index 822ac71eaebfc9..e171e0bf336444 100644 --- a/x-pack/plugins/watcher/public/sections/watch_list/watch_list_route.js +++ b/x-pack/plugins/watcher/public/sections/watch_list/watch_list_route.js @@ -3,61 +3,57 @@ * 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 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 { WatchList } from './components/watch_list'; +import { setHttpClient } from '../../lib/api'; +import { I18nContext } from 'ui/i18n'; +import { manageAngularLifecycle } from '../../lib/manage_angular_lifecycle'; -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 watchesService = $injector.get('xpackWatcherWatchesService'); - const licenseService = $injector.get('xpackWatcherLicenseService'); - const kbnUrl = $injector.get('kbnUrl'); - - return watchesService.getWatchList() - .catch(err => { - return licenseService.checkValidity() - .then(() => { - if (err.status === 403) { - return null; - } +let elem; +const renderReact = async elem => { + render( + + + , + elem + ); +}; +routes.when('/management/elasticsearch/watcher/', { + redirectTo: '/management/elasticsearch/watcher/watches/', +}); - toastNotifications.addDanger(err.data.message); - kbnUrl.redirect('/management'); - return Promise.reject(); - }); - }); - }, - checkLicense: ($injector) => { - const licenseService = $injector.get('xpackWatcherLicenseService'); - return licenseService.checkValidity(); - } +routes.when('/management/elasticsearch/watcher/watches/', { + template, + controller: class WatchListRouteController { + constructor($injector, $scope, $http) { + const $route = $injector.get('$route'); + this.watches = $route.current.locals.watches; + // clean up previously rendered React app if one exists + // this happens because of React Router redirects + elem && unmountComponentAtNode(elem); + // 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('watchListReactRoot'); + renderReact(elem); + manageAngularLifecycle($scope, $route, elem); + }); } - }); + }, + controllerAs: 'watchListRoute', + k7Breadcrumbs: getWatchListBreadcrumbs, +}); routes.defaults(/\/management/, { resolve: { - watcherManagementSection: ($injector) => { + watcherManagementSection: $injector => { const licenseService = $injector.get('xpackWatcherLicenseService'); const watchesSection = management.getSection('elasticsearch/watcher'); @@ -74,6 +70,6 @@ routes.defaults(/\/management/, { watchesSection.disable(); watchesSection.tooltip = licenseService.message; } - } - } + }, + }, }); diff --git a/x-pack/plugins/watcher/server/routes/api/watches/register_delete_route.js b/x-pack/plugins/watcher/server/routes/api/watches/register_delete_route.js index 29ee6f60676bc3..a0bbfb954b755a 100644 --- a/x-pack/plugins/watcher/server/routes/api/watches/register_delete_route.js +++ b/x-pack/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/test/functional/apps/watcher/watcher_test.js b/x-pack/test/functional/apps/watcher/watcher_test.js index 1af7da85036bfe..d82d9f0049655d 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']); @@ -34,8 +36,10 @@ export default function ({ getService, getPageObjects }) { it('should prompt user to check to see if you can override a watch with a sameID', 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?`); + 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); @@ -48,15 +52,13 @@ export default function ({ getService, getPageObjects }) { log.debug(watchList); expect(watchList.watchID.name).to.eql([updatedName]); 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/page_objects/watcher_page.js b/x-pack/test/functional/page_objects/watcher_page.js index b34750b2e6d950..f0758313af32b2 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(); @@ -31,23 +31,24 @@ export function WatcherPageProvider({ getPageObjects, getService }) { } 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 +57,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()), }; }); }