From f82f5b39ff52ad6fcd8f317e38fe780b0aa94367 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Fri, 29 Jan 2021 10:29:21 -0500 Subject: [PATCH 01/13] Adding tooltips to alert list and modal for license upgrade --- .../components/alert_details.tsx | 453 ++++++++++-------- .../components/manage_license_modal.tsx | 45 ++ .../alerts_list/components/alerts_list.tsx | 17 +- .../sections/alerts_list/translations.ts | 18 +- 4 files changed, 311 insertions(+), 222 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/manage_license_modal.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index c2a64bfa3a207..53e4b8672a216 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -39,9 +39,13 @@ import { AlertInstancesRouteWithApi } from './alert_instances_route'; import { ViewInApp } from './view_in_app'; import { AlertEdit } from '../../alert_form'; import { routeToAlertDetails } from '../../../constants'; -import { alertsErrorReasonTranslationsMapping } from '../../alerts_list/translations'; +import { + alertsErrorReasonTranslationsMapping, + AlertErrorReasons, +} from '../../alerts_list/translations'; import { useKibana } from '../../../../common/lib/kibana'; import { alertReducer } from '../../alert_form/alert_reducer'; +import { ManageLicenseModal } from './manage_license_modal'; type AlertDetailsProps = { alert: Alert; @@ -99,12 +103,15 @@ export const AlertDetails: React.FunctionComponent = ({ ? !alertTypeRegistry.get(alert.alertTypeId).requiresAppContext : false); + const licenseManagementLink = `${http.basePath.get()}/app/management/stack/license_management`; + const alertActions = alert.actions; const uniqueActions = Array.from(new Set(alertActions.map((item: any) => item.actionTypeId))); const [isEnabled, setIsEnabled] = useState(alert.enabled); const [isMuted, setIsMuted] = useState(alert.muteAll); const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false); - const [dissmissAlertErrors, setDissmissAlertErrors] = useState(false); + const [dismissAlertErrors, setDismissAlertErrors] = useState(false); + const [isManageLicenseModalOpen, setIsManageLicenseModalOpen] = useState(false); const setAlert = async () => { history.push(routeToAlertDetails.replace(`:alertId`, alert.id)); @@ -118,234 +125,254 @@ export const AlertDetails: React.FunctionComponent = ({ } }; + useEffect(() => { + setIsManageLicenseModalOpen( + alert.executionStatus.error && + alert.executionStatus.error.reason && + alert.executionStatus.error.reason === AlertErrorReasons.LICENSE + ); + }, [alert.executionStatus.error]); + return ( - - - - - - -

- {alert.name} -

-
-
- - - {hasEditButton ? ( + <> + + + + + + +

+ {alert.name} +

+
+
+ + + {hasEditButton ? ( + + + {' '} + setEditFlyoutVisibility(true)} + name="edit" + disabled={!alertType.enabledInLicense} + > + + + {editFlyoutVisible && ( + { + setInitialAlert(alert); + setEditFlyoutVisibility(false); + }} + actionTypeRegistry={actionTypeRegistry} + alertTypeRegistry={alertTypeRegistry} + reloadAlerts={setAlert} + /> + )} + + + ) : null} - - {' '} - setEditFlyoutVisibility(true)} - name="edit" - disabled={!alertType.enabledInLicense} - > - - - {editFlyoutVisible && ( - { - setInitialAlert(alert); - setEditFlyoutVisibility(false); - }} - actionTypeRegistry={actionTypeRegistry} - alertTypeRegistry={alertTypeRegistry} - reloadAlerts={setAlert} - /> - )} - + + + + + + - ) : null} + + +
+ + - - - + +

+ +

+
+ + {alertType.name} +
+ + {uniqueActions && uniqueActions.length ? ( + + +

+ +

+
+ + + {uniqueActions.map((action, index) => ( + + + {actionTypesByTypeId[action].name ?? action} + + + ))} + +
+ ) : null}
- + + + + { + if (isEnabled) { + setIsEnabled(false); + await disableAlert(alert); + } else { + setIsEnabled(true); + await enableAlert(alert); + } + requestRefresh(); + }} + label={ + + } + /> + + + { + if (isMuted) { + setIsMuted(false); + await unmuteAlert(alert); + } else { + setIsMuted(true); + await muteAlert(alert); + } + requestRefresh(); + }} + label={ + + } + /> + +
-
-
- - - - -

- -

-
- - {alertType.name} -
- - {uniqueActions && uniqueActions.length ? ( - - -

- -

-
- - - {uniqueActions.map((action, index) => ( - - - {actionTypesByTypeId[action].name ?? action} - + {!dismissAlertErrors && alert.executionStatus.status === 'error' ? ( + + + + + {alert.executionStatus.error?.message} + + + + + setDismissAlertErrors(true)}> + + - ))} - -
- ) : null} -
- - - - - { - if (isEnabled) { - setIsEnabled(false); - await disableAlert(alert); - } else { - setIsEnabled(true); - await enableAlert(alert); - } - requestRefresh(); - }} - label={ - - } - /> - - - { - if (isMuted) { - setIsMuted(false); - await unmuteAlert(alert); - } else { - setIsMuted(true); - await muteAlert(alert); - } - requestRefresh(); - }} - label={ - - } - /> + {alert.executionStatus.error?.reason === + AlertExecutionStatusErrorReasons.License && ( + + + + + + )} + +
- - - {!dissmissAlertErrors && alert.executionStatus.status === 'error' ? ( + ) : null} - - - {alert.executionStatus.error?.message} - - - - - setDissmissAlertErrors(true)}> + {alert.enabled ? ( + + ) : ( + + + +

- - - {alert.executionStatus.error?.reason === - AlertExecutionStatusErrorReasons.License && ( - - - - - - )} - - +

+
+
+ )}
- ) : null} - - - {alert.enabled ? ( - - ) : ( - - - -

- -

-
-
- )} -
-
-
-
-
-
+ + + + + {isManageLicenseModalOpen && ( + { + window.open(licenseManagementLink, '_blank'); + }} + onCancel={() => setIsManageLicenseModalOpen(false)} + /> + )} + ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/manage_license_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/manage_license_modal.tsx new file mode 100644 index 0000000000000..e2e2212943357 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/manage_license_modal.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * 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 { i18n } from '@kbn/i18n'; +import { EuiOverlayMask, EuiConfirmModal } from '@elastic/eui'; + +interface Props { + message: string; + onConfirm: () => void; + onCancel: () => void; +} + +export const ManageLicenseModal: React.FC = ({ message, onConfirm, onCancel }) => { + return ( + + +

{message}

+
+
+ ); +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index 293d471560503..4cbe915769b99 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -96,7 +96,7 @@ export const AlertsList: React.FunctionComponent = () => { const [actionTypesFilter, setActionTypesFilter] = useState([]); const [alertStatusesFilter, setAlertStatusesFilter] = useState([]); const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState(false); - const [dissmissAlertErrors, setDissmissAlertErrors] = useState(false); + const [dismissAlertErrors, setDismissAlertErrors] = useState(false); const [alertsStatusesTotal, setAlertsStatusesTotal] = useState>( AlertExecutionStatusValues.reduce( (prev: Record, status: string) => @@ -249,11 +249,20 @@ export const AlertsList: React.FunctionComponent = () => { 'data-test-subj': 'alertsTableCell-status', render: (executionStatus: AlertExecutionStatus) => { const healthColor = getHealthColor(executionStatus.status); - return ( + const tooltipMessage = + executionStatus.status === 'error' ? `Error: ${executionStatus?.error?.message}` : null; + const health = ( {alertsStatusesTranslationsMapping[executionStatus.status]} ); + return tooltipMessage ? ( + + {health} + + ) : ( + health + ); }, }, { @@ -491,7 +500,7 @@ export const AlertsList: React.FunctionComponent = () => { - {!dissmissAlertErrors && alertsStatusesTotal.error > 0 ? ( + {!dismissAlertErrors && alertsStatusesTotal.error > 0 ? ( { defaultMessage="View" /> - setDissmissAlertErrors(true)}> + setDismissAlertErrors(true)}> Date: Mon, 1 Feb 2021 10:19:25 -0500 Subject: [PATCH 02/13] Fixing typings --- .../alert_details/components/alert_details.tsx | 11 +++++------ .../components/manage_license_modal.tsx | 2 +- .../sections/alerts_list/translations.ts | 18 +++++------------- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 53e4b8672a216..a7f99663dbf0f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -39,10 +39,7 @@ import { AlertInstancesRouteWithApi } from './alert_instances_route'; import { ViewInApp } from './view_in_app'; import { AlertEdit } from '../../alert_form'; import { routeToAlertDetails } from '../../../constants'; -import { - alertsErrorReasonTranslationsMapping, - AlertErrorReasons, -} from '../../alerts_list/translations'; +import { alertsErrorReasonTranslationsMapping } from '../../alerts_list/translations'; import { useKibana } from '../../../../common/lib/kibana'; import { alertReducer } from '../../alert_form/alert_reducer'; import { ManageLicenseModal } from './manage_license_modal'; @@ -127,9 +124,11 @@ export const AlertDetails: React.FunctionComponent = ({ useEffect(() => { setIsManageLicenseModalOpen( - alert.executionStatus.error && + !!( + alert.executionStatus.error && alert.executionStatus.error.reason && - alert.executionStatus.error.reason === AlertErrorReasons.LICENSE + alert.executionStatus.error.reason === AlertExecutionStatusErrorReasons.License + ) ); }, [alert.executionStatus.error]); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/manage_license_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/manage_license_modal.tsx index e2e2212943357..489cf1d949454 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/manage_license_modal.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/manage_license_modal.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { EuiOverlayMask, EuiConfirmModal } from '@elastic/eui'; interface Props { - message: string; + message?: string; onConfirm: () => void; onCancel: () => void; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts index 0d29b42d22254..a57cca5476420 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts @@ -84,18 +84,10 @@ export const ALERT_ERROR_LICENSE_REASON = i18n.translate( } ); -export enum AlertErrorReasons { - READ = 'read', - DECRYPT = 'decrypt', - EXECUTE = 'execute', - UNKNOWN = 'unknown', - LICENSE = 'license', -} - export const alertsErrorReasonTranslationsMapping = { - [AlertErrorReasons.READ]: ALERT_ERROR_READING_REASON, - [AlertErrorReasons.DECRYPT]: ALERT_ERROR_DECRYPTING_REASON, - [AlertErrorReasons.EXECUTE]: ALERT_ERROR_EXECUTION_REASON, - [AlertErrorReasons.UNKNOWN]: ALERT_ERROR_UNKNOWN_REASON, - [AlertErrorReasons.LICENSE]: ALERT_ERROR_LICENSE_REASON, + read: ALERT_ERROR_READING_REASON, + decrypt: ALERT_ERROR_DECRYPTING_REASON, + execute: ALERT_ERROR_EXECUTION_REASON, + unknown: ALERT_ERROR_UNKNOWN_REASON, + license: ALERT_ERROR_LICENSE_REASON, }; From 9e3055ce6c3c01a29a0a219f4ec51ba41e091ad6 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 1 Feb 2021 14:56:35 -0500 Subject: [PATCH 03/13] Custom License Error status. Moving modal to alerts list page --- .../components/alert_details.tsx | 449 +++++++++--------- .../alerts_list/components/alerts_list.tsx | 99 ++-- .../components/manage_license_modal.tsx | 0 .../sections/alerts_list/translations.ts | 7 + 4 files changed, 290 insertions(+), 265 deletions(-) rename x-pack/plugins/triggers_actions_ui/public/application/sections/{alert_details => alerts_list}/components/manage_license_modal.tsx (100%) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index a7f99663dbf0f..1cf5a9a61997b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -42,7 +42,6 @@ import { routeToAlertDetails } from '../../../constants'; import { alertsErrorReasonTranslationsMapping } from '../../alerts_list/translations'; import { useKibana } from '../../../../common/lib/kibana'; import { alertReducer } from '../../alert_form/alert_reducer'; -import { ManageLicenseModal } from './manage_license_modal'; type AlertDetailsProps = { alert: Alert; @@ -100,15 +99,12 @@ export const AlertDetails: React.FunctionComponent = ({ ? !alertTypeRegistry.get(alert.alertTypeId).requiresAppContext : false); - const licenseManagementLink = `${http.basePath.get()}/app/management/stack/license_management`; - const alertActions = alert.actions; const uniqueActions = Array.from(new Set(alertActions.map((item: any) => item.actionTypeId))); const [isEnabled, setIsEnabled] = useState(alert.enabled); const [isMuted, setIsMuted] = useState(alert.muteAll); const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false); const [dismissAlertErrors, setDismissAlertErrors] = useState(false); - const [isManageLicenseModalOpen, setIsManageLicenseModalOpen] = useState(false); const setAlert = async () => { history.push(routeToAlertDetails.replace(`:alertId`, alert.id)); @@ -122,256 +118,235 @@ export const AlertDetails: React.FunctionComponent = ({ } }; - useEffect(() => { - setIsManageLicenseModalOpen( - !!( - alert.executionStatus.error && - alert.executionStatus.error.reason && - alert.executionStatus.error.reason === AlertExecutionStatusErrorReasons.License - ) - ); - }, [alert.executionStatus.error]); - return ( - <> - - - - - - -

- {alert.name} -

-
-
- - - {hasEditButton ? ( - - - {' '} - setEditFlyoutVisibility(true)} - name="edit" - disabled={!alertType.enabledInLicense} - > - - - {editFlyoutVisible && ( - { - setInitialAlert(alert); - setEditFlyoutVisibility(false); - }} - actionTypeRegistry={actionTypeRegistry} - alertTypeRegistry={alertTypeRegistry} - reloadAlerts={setAlert} - /> - )} - - - ) : null} + + + + + + +

+ {alert.name} +

+
+
+ + + {hasEditButton ? ( - - - - - - - - - -
- - - - -

- -

-
- - {alertType.name} -
- - {uniqueActions && uniqueActions.length ? ( - -

- -

-
- - - {uniqueActions.map((action, index) => ( - - - {actionTypesByTypeId[action].name ?? action} - - - ))} - + {' '} + setEditFlyoutVisibility(true)} + name="edit" + disabled={!alertType.enabledInLicense} + > + + + {editFlyoutVisible && ( + { + setInitialAlert(alert); + setEditFlyoutVisibility(false); + }} + actionTypeRegistry={actionTypeRegistry} + alertTypeRegistry={alertTypeRegistry} + reloadAlerts={setAlert} + /> + )}
- ) : null} +
+ ) : null} + + + + - - - - { - if (isEnabled) { - setIsEnabled(false); - await disableAlert(alert); - } else { - setIsEnabled(true); - await enableAlert(alert); - } - requestRefresh(); - }} - label={ - - } - /> - - - { - if (isMuted) { - setIsMuted(false); - await unmuteAlert(alert); - } else { - setIsMuted(true); - await muteAlert(alert); - } - requestRefresh(); - }} - label={ - - } - /> - - +
- {!dismissAlertErrors && alert.executionStatus.status === 'error' ? ( - - - - - {alert.executionStatus.error?.message} - - - - - setDismissAlertErrors(true)}> - - +
+
+ + + + +

+ +

+
+ + {alertType.name} +
+ + {uniqueActions && uniqueActions.length ? ( + + +

+ +

+
+ + + {uniqueActions.map((action, index) => ( + + + {actionTypesByTypeId[action].name ?? action} + - {alert.executionStatus.error?.reason === - AlertExecutionStatusErrorReasons.License && ( - - - - - - )} - -
+ ))} +
+ + ) : null} + + + + + + { + if (isEnabled) { + setIsEnabled(false); + await disableAlert(alert); + } else { + setIsEnabled(true); + await enableAlert(alert); + } + requestRefresh(); + }} + label={ + + } + /> + + + { + if (isMuted) { + setIsMuted(false); + await unmuteAlert(alert); + } else { + setIsMuted(true); + await muteAlert(alert); + } + requestRefresh(); + }} + label={ + + } + /> - ) : null} + + + {!dismissAlertErrors && alert.executionStatus.status === 'error' ? ( - {alert.enabled ? ( - - ) : ( - - - -

+ + + {alert.executionStatus.error?.message} + + + + + setDismissAlertErrors(true)}> -

-
-
- )} + +
+ {alert.executionStatus.error?.reason === + AlertExecutionStatusErrorReasons.License && ( + + + + + + )} +
+ - - - - - {isManageLicenseModalOpen && ( - { - window.open(licenseManagementLink, '_blank'); - }} - onCancel={() => setIsManageLicenseModalOpen(false)} - /> - )} - + ) : null} + + + {alert.enabled ? ( + + ) : ( + + + +

+ +

+
+
+ )} +
+
+ + + + ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index 4cbe915769b99..d4dd22ed3d557 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -52,14 +52,16 @@ import { AlertExecutionStatus, AlertExecutionStatusValues, ALERTS_FEATURE_ID, + AlertExecutionStatusErrorReasons, } from '../../../../../../alerts/common'; import { hasAllPrivilege } from '../../../lib/capabilities'; -import { alertsStatusesTranslationsMapping } from '../translations'; +import { alertsStatusesTranslationsMapping, ALERT_STATUS_LICENSE_ERROR } from '../translations'; import { useKibana } from '../../../../common/lib/kibana'; import { checkAlertTypeEnabled } from '../../../lib/check_alert_type_enabled'; import { DEFAULT_HIDDEN_ACTION_TYPES } from '../../../../common/constants'; import './alerts_list.scss'; import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; +import { ManageLicenseModal } from './manage_license_modal'; const ENTER_KEY = 13; @@ -97,6 +99,7 @@ export const AlertsList: React.FunctionComponent = () => { const [alertStatusesFilter, setAlertStatusesFilter] = useState([]); const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState(false); const [dismissAlertErrors, setDismissAlertErrors] = useState(false); + const [manageLicenseMessage, setManageLicenseMessage] = useState(undefined); const [alertsStatusesTotal, setAlertsStatusesTotal] = useState>( AlertExecutionStatusValues.reduce( (prev: Record, status: string) => @@ -237,34 +240,52 @@ export const AlertsList: React.FunctionComponent = () => { } } + const renderAlertExecutionStatus = (executionStatus: AlertExecutionStatus) => { + const healthColor = getHealthColor(executionStatus.status); + const tooltipMessage = + executionStatus.status === 'error' ? `Error: ${executionStatus?.error?.message}` : null; + const statusMessage = + executionStatus.error?.reason === AlertExecutionStatusErrorReasons.License + ? ALERT_STATUS_LICENSE_ERROR + : alertsStatusesTranslationsMapping[executionStatus.status]; + const showLicenseLink = + executionStatus.error?.reason === AlertExecutionStatusErrorReasons.License; + + const health = ( + + {statusMessage} + + ); + + const healthWithTooltip = tooltipMessage ? ( + + {health} + + ) : ( + health + ); + + return ( + + {healthWithTooltip} + {showLicenseLink && ( + + setManageLicenseMessage(executionStatus?.error?.message)} + > + + + + )} + + ); + }; + const alertsTableColumns = [ - { - field: 'executionStatus', - name: i18n.translate( - 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.statusTitle', - { defaultMessage: 'Status' } - ), - sortable: false, - truncateText: false, - 'data-test-subj': 'alertsTableCell-status', - render: (executionStatus: AlertExecutionStatus) => { - const healthColor = getHealthColor(executionStatus.status); - const tooltipMessage = - executionStatus.status === 'error' ? `Error: ${executionStatus?.error?.message}` : null; - const health = ( - - {alertsStatusesTranslationsMapping[executionStatus.status]} - - ); - return tooltipMessage ? ( - - {health} - - ) : ( - health - ); - }, - }, { field: 'name', name: i18n.translate( @@ -301,6 +322,19 @@ export const AlertsList: React.FunctionComponent = () => { ); }, }, + { + field: 'executionStatus', + name: i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.statusTitle', + { defaultMessage: 'Status' } + ), + sortable: false, + truncateText: false, + 'data-test-subj': 'alertsTableCell-status', + render: (executionStatus: AlertExecutionStatus) => { + return renderAlertExecutionStatus(executionStatus); + }, + }, { field: 'tagsText', name: i18n.translate( @@ -647,6 +681,15 @@ export const AlertsList: React.FunctionComponent = () => { setPage(changedPage); }} /> + {manageLicenseMessage !== undefined && ( + { + window.open(`${http.basePath.get()}/app/management/stack/license_management`, '_blank'); + }} + onCancel={() => setManageLicenseMessage(undefined)} + /> + )} ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/manage_license_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/manage_license_modal.tsx similarity index 100% rename from x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/manage_license_modal.tsx rename to x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/manage_license_modal.tsx diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts index a57cca5476420..db174661c020e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts @@ -27,6 +27,13 @@ export const ALERT_STATUS_ERROR = i18n.translate( } ); +export const ALERT_STATUS_LICENSE_ERROR = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertStatusLicenseError', + { + defaultMessage: 'License Error', + } +); + export const ALERT_STATUS_PENDING = i18n.translate( 'xpack.triggersActionsUI.sections.alertsList.alertStatusPending', { From 09af8f193e37ed280c2d0e7c18a7388bd4a11c13 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 1 Feb 2021 21:25:43 -0500 Subject: [PATCH 04/13] Adding unit test --- .../components/alerts_list.test.tsx | 265 +++++++++++------- .../alerts_list/components/alerts_list.tsx | 20 +- 2 files changed, 172 insertions(+), 113 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx index bd50bf3270f1a..9403e089d3ed6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -125,11 +125,16 @@ describe('alerts_list component empty', () => { wrapper.find('button[data-test-subj="createFirstAlertButton"]').simulate('click'); - // When the AlertAdd component is rendered, it waits for the healthcheck to resolve - await new Promise((resolve) => { - setTimeout(resolve, 1000); + await act(async () => { + // When the AlertAdd component is rendered, it waits for the healthcheck to resolve + await new Promise((resolve) => { + setTimeout(resolve, 1000); + }); + + await nextTick(); + wrapper.update(); }); - wrapper.update(); + expect(wrapper.find('AlertAdd').exists()).toEqual(true); }); }); @@ -137,104 +142,131 @@ describe('alerts_list component empty', () => { describe('alerts_list component with items', () => { let wrapper: ReactWrapper; + const mockedAlertsData = [ + { + id: '1', + name: 'test alert', + tags: ['tag1'], + enabled: true, + alertTypeId: 'test_alert_type', + schedule: { interval: '5d' }, + actions: [], + params: { name: 'test alert type name' }, + scheduledTaskId: null, + createdBy: null, + updatedBy: null, + apiKeyOwner: null, + throttle: '1m', + muteAll: false, + mutedInstanceIds: [], + executionStatus: { + status: 'active', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: null, + }, + }, + { + id: '2', + name: 'test alert ok', + tags: ['tag1'], + enabled: true, + alertTypeId: 'test_alert_type', + schedule: { interval: '5d' }, + actions: [], + params: { name: 'test alert type name' }, + scheduledTaskId: null, + createdBy: null, + updatedBy: null, + apiKeyOwner: null, + throttle: '1m', + muteAll: false, + mutedInstanceIds: [], + executionStatus: { + status: 'ok', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: null, + }, + }, + { + id: '3', + name: 'test alert pending', + tags: ['tag1'], + enabled: true, + alertTypeId: 'test_alert_type', + schedule: { interval: '5d' }, + actions: [], + params: { name: 'test alert type name' }, + scheduledTaskId: null, + createdBy: null, + updatedBy: null, + apiKeyOwner: null, + throttle: '1m', + muteAll: false, + mutedInstanceIds: [], + executionStatus: { + status: 'pending', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: null, + }, + }, + { + id: '4', + name: 'test alert error', + tags: ['tag1'], + enabled: true, + alertTypeId: 'test_alert_type', + schedule: { interval: '5d' }, + actions: [{ id: 'test', group: 'alert', params: { message: 'test' } }], + params: { name: 'test alert type name' }, + scheduledTaskId: null, + createdBy: null, + updatedBy: null, + apiKeyOwner: null, + throttle: '1m', + muteAll: false, + mutedInstanceIds: [], + executionStatus: { + status: 'error', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: { + reason: AlertExecutionStatusErrorReasons.Unknown, + message: 'test', + }, + }, + }, + { + id: '5', + name: 'test alert license error', + tags: ['tag1'], + enabled: true, + alertTypeId: 'test_alert_type', + schedule: { interval: '5d' }, + actions: [{ id: 'test', group: 'alert', params: { message: 'test' } }], + params: { name: 'test alert type name' }, + scheduledTaskId: null, + createdBy: null, + updatedBy: null, + apiKeyOwner: null, + throttle: '1m', + muteAll: false, + mutedInstanceIds: [], + executionStatus: { + status: 'error', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: { + reason: AlertExecutionStatusErrorReasons.License, + message: 'test', + }, + }, + }, + ]; + async function setup() { loadAlerts.mockResolvedValue({ page: 1, perPage: 10000, total: 4, - data: [ - { - id: '1', - name: 'test alert', - tags: ['tag1'], - enabled: true, - alertTypeId: 'test_alert_type', - schedule: { interval: '5d' }, - actions: [], - params: { name: 'test alert type name' }, - scheduledTaskId: null, - createdBy: null, - updatedBy: null, - apiKeyOwner: null, - throttle: '1m', - muteAll: false, - mutedInstanceIds: [], - executionStatus: { - status: 'active', - lastExecutionDate: new Date('2020-08-20T19:23:38Z'), - error: null, - }, - }, - { - id: '2', - name: 'test alert ok', - tags: ['tag1'], - enabled: true, - alertTypeId: 'test_alert_type', - schedule: { interval: '5d' }, - actions: [], - params: { name: 'test alert type name' }, - scheduledTaskId: null, - createdBy: null, - updatedBy: null, - apiKeyOwner: null, - throttle: '1m', - muteAll: false, - mutedInstanceIds: [], - executionStatus: { - status: 'ok', - lastExecutionDate: new Date('2020-08-20T19:23:38Z'), - error: null, - }, - }, - { - id: '3', - name: 'test alert pending', - tags: ['tag1'], - enabled: true, - alertTypeId: 'test_alert_type', - schedule: { interval: '5d' }, - actions: [], - params: { name: 'test alert type name' }, - scheduledTaskId: null, - createdBy: null, - updatedBy: null, - apiKeyOwner: null, - throttle: '1m', - muteAll: false, - mutedInstanceIds: [], - executionStatus: { - status: 'pending', - lastExecutionDate: new Date('2020-08-20T19:23:38Z'), - error: null, - }, - }, - { - id: '4', - name: 'test alert error', - tags: ['tag1'], - enabled: true, - alertTypeId: 'test_alert_type', - schedule: { interval: '5d' }, - actions: [{ id: 'test', group: 'alert', params: { message: 'test' } }], - params: { name: 'test alert type name' }, - scheduledTaskId: null, - createdBy: null, - updatedBy: null, - apiKeyOwner: null, - throttle: '1m', - muteAll: false, - mutedInstanceIds: [], - executionStatus: { - status: 'error', - lastExecutionDate: new Date('2020-08-20T19:23:38Z'), - error: { - reason: AlertExecutionStatusErrorReasons.Unknown, - message: 'test', - }, - }, - }, - ], + data: mockedAlertsData, }); loadActionTypes.mockResolvedValue([ { @@ -269,19 +301,40 @@ describe('alerts_list component with items', () => { it('renders table of alerts', async () => { await setup(); expect(wrapper.find('EuiBasicTable')).toHaveLength(1); - expect(wrapper.find('EuiTableRow')).toHaveLength(4); - expect(wrapper.find('[data-test-subj="alertsTableCell-status"]').length).toBeGreaterThan(0); - expect(wrapper.find('[data-test-subj="alertStatus-active"]').length).toBeGreaterThan(0); - expect(wrapper.find('[data-test-subj="alertStatus-error"]').length).toBeGreaterThan(0); - expect(wrapper.find('[data-test-subj="alertStatus-ok"]').length).toBeGreaterThan(0); - expect(wrapper.find('[data-test-subj="alertStatus-pending"]').length).toBeGreaterThan(0); - expect(wrapper.find('[data-test-subj="alertStatus-unknown"]').length).toBe(0); + expect(wrapper.find('EuiTableRow')).toHaveLength(mockedAlertsData.length); + expect(wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-status"]').length).toEqual( + mockedAlertsData.length + ); + expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-active"]').length).toEqual(1); + expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-ok"]').length).toEqual(1); + expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-pending"]').length).toEqual(1); + expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-unknown"]').length).toEqual(0); + + expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-error"]').length).toEqual(2); + expect(wrapper.find('[data-test-subj="alertStatus-error-tooltip"]').length).toEqual(2); + expect( + wrapper.find('EuiButtonEmpty[data-test-subj="alertStatus-error-license-fix"]').length + ).toEqual(1); + expect(wrapper.find('[data-test-subj="refreshAlertsButton"]').exists()).toBeTruthy(); + + expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-error"]').first().text()).toEqual( + 'Error' + ); + expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-error"]').last().text()).toEqual( + 'License Error' + ); }); it('loads alerts when refresh button is clicked', async () => { await setup(); wrapper.find('[data-test-subj="refreshAlertsButton"]').first().simulate('click'); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + expect(loadAlerts).toHaveBeenCalled(); }); }); @@ -306,7 +359,9 @@ describe('alerts_list component empty with show only capability', () => { name: 'Test2', }, ]); - loadAlertTypes.mockResolvedValue([{ id: 'test_alert_type', name: 'some alert type' }]); + loadAlertTypes.mockResolvedValue([ + { id: 'test_alert_type', name: 'some alert type', authorizedConsumers: {} }, + ]); loadAllActions.mockResolvedValue([]); // eslint-disable-next-line react-hooks/rules-of-hooks useKibanaMock().services.alertTypeRegistry = alertTypeRegistry; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index d4dd22ed3d557..75a377820b246 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -244,12 +244,11 @@ export const AlertsList: React.FunctionComponent = () => { const healthColor = getHealthColor(executionStatus.status); const tooltipMessage = executionStatus.status === 'error' ? `Error: ${executionStatus?.error?.message}` : null; - const statusMessage = - executionStatus.error?.reason === AlertExecutionStatusErrorReasons.License - ? ALERT_STATUS_LICENSE_ERROR - : alertsStatusesTranslationsMapping[executionStatus.status]; - const showLicenseLink = + const isLicenseError = executionStatus.error?.reason === AlertExecutionStatusErrorReasons.License; + const statusMessage = isLicenseError + ? ALERT_STATUS_LICENSE_ERROR + : alertsStatusesTranslationsMapping[executionStatus.status]; const health = ( @@ -258,7 +257,11 @@ export const AlertsList: React.FunctionComponent = () => { ); const healthWithTooltip = tooltipMessage ? ( - + {health} ) : ( @@ -267,11 +270,12 @@ export const AlertsList: React.FunctionComponent = () => { return ( - {healthWithTooltip} - {showLicenseLink && ( + {healthWithTooltip} + {isLicenseError && ( setManageLicenseMessage(executionStatus?.error?.message)} > Date: Mon, 1 Feb 2021 21:34:06 -0500 Subject: [PATCH 05/13] Cleanup --- .../sections/alert_details/components/alert_details.tsx | 7 +++---- .../sections/alerts_list/components/alerts_list.tsx | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 1cf5a9a61997b..c2a64bfa3a207 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -104,7 +104,7 @@ export const AlertDetails: React.FunctionComponent = ({ const [isEnabled, setIsEnabled] = useState(alert.enabled); const [isMuted, setIsMuted] = useState(alert.muteAll); const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false); - const [dismissAlertErrors, setDismissAlertErrors] = useState(false); + const [dissmissAlertErrors, setDissmissAlertErrors] = useState(false); const setAlert = async () => { history.push(routeToAlertDetails.replace(`:alertId`, alert.id)); @@ -170,7 +170,6 @@ export const AlertDetails: React.FunctionComponent = ({ onClick={requestRefresh} name="refresh" color="primary" - disabled={!alertType.enabledInLicense} > = ({ - {!dismissAlertErrors && alert.executionStatus.status === 'error' ? ( + {!dissmissAlertErrors && alert.executionStatus.status === 'error' ? ( = ({ - setDismissAlertErrors(true)}> + setDissmissAlertErrors(true)}> { message={manageLicenseMessage} onConfirm={() => { window.open(`${http.basePath.get()}/app/management/stack/license_management`, '_blank'); + setManageLicenseMessage(undefined); }} onCancel={() => setManageLicenseMessage(undefined)} /> From 5188f146e31cd174a044187bb241b56293bc6ab7 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Mon, 1 Feb 2021 22:39:30 -0500 Subject: [PATCH 06/13] Unit tests --- .../components/alerts_list.test.tsx | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx index 9403e089d3ed6..0482f352bdd18 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -337,6 +337,30 @@ describe('alerts_list component with items', () => { expect(loadAlerts).toHaveBeenCalled(); }); + + it('renders license errors and manage license modal on click', async () => { + global.open = jest.fn(); + await setup(); + expect(wrapper.find('ManageLicenseModal').exists()).toBeFalsy(); + expect( + wrapper.find('EuiButtonEmpty[data-test-subj="alertStatus-error-license-fix"]').length + ).toEqual(1); + wrapper + .find('EuiButtonEmpty[data-test-subj="alertStatus-error-license-fix"]') + .simulate('click'); + + await act(async () => { + await nextTick(); + wrapper.update(); + }); + + expect(wrapper.find('ManageLicenseModal').exists()).toBeTruthy(); + expect(wrapper.find('EuiButton[data-test-subj="confirmModalConfirmButton"]').text()).toEqual( + 'Manage license' + ); + wrapper.find('EuiButton[data-test-subj="confirmModalConfirmButton"]').simulate('click'); + expect(global.open).toHaveBeenCalled(); + }); }); describe('alerts_list component empty with show only capability', () => { From e5acc758849c98a336728f06f0625ba8aaa48297 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 2 Feb 2021 08:43:23 -0500 Subject: [PATCH 07/13] Removing tooltip from alert name --- .../alerts_list/components/alerts_list.tsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index fd40eaff3ff1a..d74b94473b175 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -57,7 +57,6 @@ import { import { hasAllPrivilege } from '../../../lib/capabilities'; import { alertsStatusesTranslationsMapping, ALERT_STATUS_LICENSE_ERROR } from '../translations'; import { useKibana } from '../../../../common/lib/kibana'; -import { checkAlertTypeEnabled } from '../../../lib/check_alert_type_enabled'; import { DEFAULT_HIDDEN_ACTION_TYPES } from '../../../../common/constants'; import './alerts_list.scss'; import { CenterJustifiedSpinner } from '../../../components/center_justified_spinner'; @@ -300,10 +299,7 @@ export const AlertsList: React.FunctionComponent = () => { truncateText: true, 'data-test-subj': 'alertsTableCell-name', render: (name: string, alert: AlertTableItem) => { - const checkEnabledResult = checkAlertTypeEnabled( - alertTypesState.data.get(alert.alertTypeId) - ); - const link = ( + return ( { @@ -313,17 +309,6 @@ export const AlertsList: React.FunctionComponent = () => { {name} ); - return checkEnabledResult.isEnabled ? ( - link - ) : ( - - {link} - - ); }, }, { From aa05bb55cb90ae6b6bb7df5b4ddbf933bb3962b9 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Thu, 4 Feb 2021 07:53:04 -0500 Subject: [PATCH 08/13] License --- .../sections/alerts_list/components/manage_license_modal.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/manage_license_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/manage_license_modal.tsx index 489cf1d949454..465e6234f9802 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/manage_license_modal.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/manage_license_modal.tsx @@ -1,7 +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. + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. */ import React from 'react'; From 95f6333779de6f049009c3ff266894954daef11a Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Fri, 5 Feb 2021 08:35:28 -0500 Subject: [PATCH 09/13] PR fixes --- .../sections/alerts_list/components/alerts_list.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index 655b5dfe775bc..a78dc796dbf8f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -280,7 +280,7 @@ export const AlertsList: React.FunctionComponent = () => { > @@ -298,6 +298,7 @@ export const AlertsList: React.FunctionComponent = () => { ), sortable: false, truncateText: true, + width: '35%', 'data-test-subj': 'alertsTableCell-name', render: (name: string, alert: AlertTableItem) => { return ( @@ -320,6 +321,7 @@ export const AlertsList: React.FunctionComponent = () => { ), sortable: false, truncateText: false, + width: '150px', 'data-test-subj': 'alertsTableCell-status', render: (executionStatus: AlertExecutionStatus) => { return renderAlertExecutionStatus(executionStatus); From 4a134a750e9a981b382b0639d5e4692328e2f20c Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 9 Feb 2021 11:59:40 -0500 Subject: [PATCH 10/13] Updating modal wording --- .../alerts_list/components/alerts_list.tsx | 30 +++++++++++++------ .../components/manage_license_modal.tsx | 24 ++++++++++++--- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index a78dc796dbf8f..11761cec7cdbb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -99,7 +99,10 @@ export const AlertsList: React.FunctionComponent = () => { const [alertStatusesFilter, setAlertStatusesFilter] = useState([]); const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState(false); const [dismissAlertErrors, setDismissAlertErrors] = useState(false); - const [manageLicenseMessage, setManageLicenseMessage] = useState(undefined); + const [manageLicenseModalOpts, setManageLicenseModalOpts] = useState<{ + licenseType: string; + alertTypeId: string; + } | null>(null); const [alertsStatusesTotal, setAlertsStatusesTotal] = useState>( AlertExecutionStatusValues.reduce( (prev: Record, status: string) => @@ -240,7 +243,10 @@ export const AlertsList: React.FunctionComponent = () => { } } - const renderAlertExecutionStatus = (executionStatus: AlertExecutionStatus) => { + const renderAlertExecutionStatus = ( + executionStatus: AlertExecutionStatus, + item: AlertTableItem + ) => { const healthColor = getHealthColor(executionStatus.status); const tooltipMessage = executionStatus.status === 'error' ? `Error: ${executionStatus?.error?.message}` : null; @@ -276,7 +282,12 @@ export const AlertsList: React.FunctionComponent = () => { setManageLicenseMessage(executionStatus?.error?.message)} + onClick={() => + setManageLicenseModalOpts({ + licenseType: alertTypesState.data.get(item.alertTypeId)?.minimumLicenseRequired!, + alertTypeId: item.alertTypeId, + }) + } > { truncateText: false, width: '150px', 'data-test-subj': 'alertsTableCell-status', - render: (executionStatus: AlertExecutionStatus) => { - return renderAlertExecutionStatus(executionStatus); + render: (executionStatus: AlertExecutionStatus, item: AlertTableItem) => { + return renderAlertExecutionStatus(executionStatus, item); }, }, { @@ -673,14 +684,15 @@ export const AlertsList: React.FunctionComponent = () => { setPage(changedPage); }} /> - {manageLicenseMessage !== undefined && ( + {manageLicenseModalOpts && ( { window.open(`${http.basePath.get()}/app/management/stack/license_management`, '_blank'); - setManageLicenseMessage(undefined); + setManageLicenseModalOpts(null); }} - onCancel={() => setManageLicenseMessage(undefined)} + onCancel={() => setManageLicenseModalOpts(null)} /> )} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/manage_license_modal.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/manage_license_modal.tsx index 465e6234f9802..f13e5fd96d2ad 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/manage_license_modal.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/manage_license_modal.tsx @@ -7,20 +7,30 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { EuiOverlayMask, EuiConfirmModal } from '@elastic/eui'; +import { capitalize } from 'lodash'; interface Props { - message?: string; + licenseType: string; + alertTypeId: string; onConfirm: () => void; onCancel: () => void; } -export const ManageLicenseModal: React.FC = ({ message, onConfirm, onCancel }) => { +export const ManageLicenseModal: React.FC = ({ + licenseType, + alertTypeId, + onConfirm, + onCancel, +}) => { + const licenseRequired = capitalize(licenseType); return ( = ({ message, onConfirm, onCanc defaultFocusedButton="confirm" data-test-subj="manageLicenseModal" > -

{message}

+

+ +

); From 9a1fccc9caf6f4676ff39c3652136d05ef079566 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 9 Feb 2021 19:03:02 -0500 Subject: [PATCH 11/13] Updating license state error message --- x-pack/plugins/alerts/server/lib/license_state.test.ts | 2 +- x-pack/plugins/alerts/server/lib/license_state.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/alerts/server/lib/license_state.test.ts b/x-pack/plugins/alerts/server/lib/license_state.test.ts index 07074b9187547..a1c326656f735 100644 --- a/x-pack/plugins/alerts/server/lib/license_state.test.ts +++ b/x-pack/plugins/alerts/server/lib/license_state.test.ts @@ -248,7 +248,7 @@ describe('ensureLicenseForAlertType()', () => { expect(() => licenseState.ensureLicenseForAlertType(alertType) ).toThrowErrorMatchingInlineSnapshot( - `"Alert test is disabled because it requires a Gold license. Contact your administrator to upgrade your license."` + `"Alert test is disabled because it requires a Gold license. Go to License Management to view upgrade options."` ); }); diff --git a/x-pack/plugins/alerts/server/lib/license_state.ts b/x-pack/plugins/alerts/server/lib/license_state.ts index f95c6cb42a17b..238b2e97c4cdf 100644 --- a/x-pack/plugins/alerts/server/lib/license_state.ts +++ b/x-pack/plugins/alerts/server/lib/license_state.ts @@ -9,6 +9,7 @@ import Boom from '@hapi/boom'; import { i18n } from '@kbn/i18n'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { assertNever } from '@kbn/std'; +import { capitalize } from 'lodash'; import { Observable, Subscription } from 'rxjs'; import { LicensingPluginStart } from '../../../licensing/server'; import { ILicense, LicenseType } from '../../../licensing/common/types'; @@ -190,8 +191,11 @@ export class LicenseState { throw new AlertTypeDisabledError( i18n.translate('xpack.alerts.serverSideErrors.invalidLicenseErrorMessage', { defaultMessage: - 'Alert {alertTypeId} is disabled because it requires a Gold license. Contact your administrator to upgrade your license.', - values: { alertTypeId: alertType.id }, + 'Alert {alertTypeId} is disabled because it requires a {licenseType} license. Go to License Management to view upgrade options.', + values: { + alertTypeId: alertType.id, + licenseType: capitalize(alertType.minimumLicenseRequired), + }, }), 'license_invalid' ); From ab2bb14d94b9740b37706aee641b38727d654b98 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 9 Feb 2021 20:04:58 -0500 Subject: [PATCH 12/13] i18n fix --- x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 2 files changed, 2 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 6e9d0329eaff8..52d6ed4452cea 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4830,7 +4830,6 @@ "xpack.alerts.server.healthStatus.degraded": "アラートフレームワークは劣化しました", "xpack.alerts.server.healthStatus.unavailable": "アラートフレームワークを使用できません", "xpack.alerts.serverSideErrors.expirerdLicenseErrorMessage": "{licenseType} ライセンスの期限が切れたのでアラートタイプ {alertTypeId} は無効です。", - "xpack.alerts.serverSideErrors.invalidLicenseErrorMessage": "アラート {alertTypeId} は無効です。Gold ライセンスが必要です。ライセンスをアップグレードするには、管理者に問い合わせてください。", "xpack.alerts.serverSideErrors.unavailableLicenseErrorMessage": "現時点でライセンス情報を入手できないため、アラートタイプ {alertTypeId} は無効です。", "xpack.alerts.serverSideErrors.unavailableLicenseInformationErrorMessage": "アラートを利用できません。現在ライセンス情報が利用できません。", "xpack.apm.a.thresholdMet": "しきい値一致", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index eeda709104479..082f42a5cb35c 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4836,7 +4836,6 @@ "xpack.alerts.server.healthStatus.degraded": "告警框架已降级", "xpack.alerts.server.healthStatus.unavailable": "告警框架不可用", "xpack.alerts.serverSideErrors.expirerdLicenseErrorMessage": "告警类型 {alertTypeId} 已禁用,因为您的{licenseType}许可证已过期。", - "xpack.alerts.serverSideErrors.invalidLicenseErrorMessage": "告警 {alertTypeId} 已禁用,因为它需要黄金级许可证。请联系管理员升级您的许可证。", "xpack.alerts.serverSideErrors.unavailableLicenseErrorMessage": "告警类型 {alertTypeId} 已禁用,因为许可证信息当前不可用。", "xpack.alerts.serverSideErrors.unavailableLicenseInformationErrorMessage": "告警不可用 - 许可信息当前不可用。", "xpack.apm.a.thresholdMet": "已达到阈值", From 5c8aa7611d100748e3369e8988bfb26663d5eeb9 Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 9 Feb 2021 21:35:15 -0500 Subject: [PATCH 13/13] Fixing functional test --- .../basic/tests/alerts/gold_noop_alert_type.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts b/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts index 488b39eabb637..211d1acb2a005 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/alerts/gold_noop_alert_type.ts @@ -22,7 +22,7 @@ export default function emailTest({ getService }: FtrProviderContext) { statusCode: 403, error: 'Forbidden', message: - 'Alert test.gold.noop is disabled because it requires a Gold license. Contact your administrator to upgrade your license.', + 'Alert test.gold.noop is disabled because it requires a Gold license. Go to License Management to view upgrade options.', }); }); });