mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Infra UI] Add alerts to asset details flyout (#161677)
Closes #160371 ## Summary This PR adds alerts section to the overview tab inside the asset details flyout component. Notes: A lot of changes are extracting common components from the alerts tab to a common folder. The flyout version is not showing the chart so it's not exactly the same component but a big part of the logic is reused there. The tooltip content can be found in a [Figma comment ](https://www.figma.com/file/XBVpHX6pOBaTPoGHWhEQJH?node-id=843:435665&mode=design#492130894) <img width="1616" alt="alerts_section" src="399dd1ea
-e1cb-4e7f-9ed5-917ced7cc490"> ## Alerts summary widget changes: After introducing the `hideChart` prop [here](https://github.com/elastic/kibana/pull/161263) in this PR I change the spinner type and size in case of no chart we want to have a smaller section with a smaller spinner:   ## Storybook I added some changes to make the alerts widget show in the storybook [[Workaround for storybook](d97a2b1736
)] <img width="1905" alt="image" src="539c9443
-f977-4301-8d2b-d24f1d01b44e"> ## Testing - Go to Hosts view and open the single host flyout - alerts section should be visible - Alerts title icon should open a tooltip with links to alerts and alerts documentation - Alerts links: - The Create rule link will open a flyout (on top, not closing the existing flyout) to create an inventory rule, when closed/saved rule the single host flyout should remain open - The Show All link should navigate to alerts and apply time range / host.name filter selected in the hosts viewb362042a
-b9de-460c-86ae-282154b586ff --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
b222f7a7d1
commit
5a7f395003
26 changed files with 612 additions and 156 deletions
|
@ -25,7 +25,7 @@ export const AlertFlyout = ({ options, nodeType, filter, visible, setVisible }:
|
|||
const { triggersActionsUI } = useContext(TriggerActionsContext);
|
||||
|
||||
const { inventoryPrefill } = useAlertPrefillContext();
|
||||
const { customMetrics } = inventoryPrefill;
|
||||
const { customMetrics = [] } = inventoryPrefill ?? {};
|
||||
const onCloseFlyout = useCallback(() => setVisible(false), [setVisible]);
|
||||
const AddAlertFlyout = useMemo(
|
||||
() =>
|
||||
|
|
57
x-pack/plugins/infra/public/common/alerts/constants.ts
Normal file
57
x-pack/plugins/infra/public/common/alerts/constants.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED } from '@kbn/rule-data-utils';
|
||||
import type { AlertStatusFilter } from './types';
|
||||
|
||||
export const ALERT_STATUS_ALL = 'all';
|
||||
|
||||
export const ALL_ALERTS: AlertStatusFilter = {
|
||||
status: ALERT_STATUS_ALL,
|
||||
label: i18n.translate('xpack.infra.hostsViewPage.tabs.alerts.alertStatusFilter.showAll', {
|
||||
defaultMessage: 'Show all',
|
||||
}),
|
||||
};
|
||||
|
||||
export const ACTIVE_ALERTS: AlertStatusFilter = {
|
||||
status: ALERT_STATUS_ACTIVE,
|
||||
query: {
|
||||
term: {
|
||||
[ALERT_STATUS]: {
|
||||
value: ALERT_STATUS_ACTIVE,
|
||||
},
|
||||
},
|
||||
},
|
||||
label: i18n.translate('xpack.infra.hostsViewPage.tabs.alerts.alertStatusFilter.active', {
|
||||
defaultMessage: 'Active',
|
||||
}),
|
||||
};
|
||||
|
||||
export const RECOVERED_ALERTS: AlertStatusFilter = {
|
||||
status: ALERT_STATUS_RECOVERED,
|
||||
query: {
|
||||
term: {
|
||||
[ALERT_STATUS]: {
|
||||
value: ALERT_STATUS_RECOVERED,
|
||||
},
|
||||
},
|
||||
},
|
||||
label: i18n.translate('xpack.infra.hostsViewPage.tabs.alerts.alertStatusFilter.recovered', {
|
||||
defaultMessage: 'Recovered',
|
||||
}),
|
||||
};
|
||||
|
||||
export const ALERT_STATUS_QUERY = {
|
||||
[ACTIVE_ALERTS.status]: ACTIVE_ALERTS.query,
|
||||
[RECOVERED_ALERTS.status]: RECOVERED_ALERTS.query,
|
||||
};
|
||||
|
||||
export const ALERTS_DOC_HREF =
|
||||
'https://www.elastic.co/guide/en/observability/current/create-alerts.html';
|
||||
|
||||
export const ALERTS_PATH = '/app/observability/alerts';
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { getTime } from '@kbn/data-plugin/common';
|
||||
import { ALERT_TIME_RANGE } from '@kbn/rule-data-utils';
|
||||
import { buildEsQuery, Filter, type TimeRange } from '@kbn/es-query';
|
||||
import type { AlertStatus } from '@kbn/observability-plugin/common/typings';
|
||||
import { ALERT_STATUS_QUERY } from './constants';
|
||||
import { buildCombinedHostsFilter } from '../../utils/filters/build';
|
||||
import type { AlertsEsQuery } from './types';
|
||||
|
||||
export const createAlertsEsQuery = ({
|
||||
dateRange,
|
||||
hostNodeNames,
|
||||
status,
|
||||
}: {
|
||||
dateRange: TimeRange;
|
||||
hostNodeNames: string[];
|
||||
status?: AlertStatus;
|
||||
}): AlertsEsQuery => {
|
||||
const alertStatusFilter = createAlertStatusFilter(status);
|
||||
|
||||
const dateFilter = createDateFilter(dateRange);
|
||||
const hostsFilter = buildCombinedHostsFilter({
|
||||
field: 'host.name',
|
||||
values: hostNodeNames,
|
||||
});
|
||||
|
||||
const filters = [alertStatusFilter, dateFilter, hostsFilter].filter(Boolean) as Filter[];
|
||||
|
||||
return buildEsQuery(undefined, [], filters);
|
||||
};
|
||||
|
||||
const createDateFilter = (date: TimeRange) =>
|
||||
getTime(undefined, date, { fieldName: ALERT_TIME_RANGE });
|
||||
|
||||
const createAlertStatusFilter = (status: AlertStatus = 'all'): Filter | null =>
|
||||
ALERT_STATUS_QUERY[status] ? { query: ALERT_STATUS_QUERY[status], meta: {} } : null;
|
18
x-pack/plugins/infra/public/common/alerts/types.ts
Normal file
18
x-pack/plugins/infra/public/common/alerts/types.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* 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 type { BoolQuery, Filter } from '@kbn/es-query';
|
||||
import type { AlertStatus } from '@kbn/observability-plugin/common/typings';
|
||||
export interface AlertStatusFilter {
|
||||
status: AlertStatus;
|
||||
query?: Filter['query'];
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface AlertsEsQuery {
|
||||
bool: BoolQuery;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
const summaryResponse = {
|
||||
activeAlertCount: 3,
|
||||
recoveredAlertCount: 3,
|
||||
activeAlerts: [
|
||||
{
|
||||
key_as_string: '1689172920000',
|
||||
key: 1689172920000,
|
||||
doc_count: 3,
|
||||
},
|
||||
{
|
||||
key_as_string: '1689172980000',
|
||||
key: 1689172980000,
|
||||
doc_count: 3,
|
||||
},
|
||||
],
|
||||
recoveredAlerts: [
|
||||
{
|
||||
key_as_string: '2023-07-12T14:42:00.000Z',
|
||||
key: 1689172920000,
|
||||
doc_count: 3,
|
||||
},
|
||||
{
|
||||
key_as_string: '2023-07-12T14:43:00.000Z',
|
||||
key: 1689172980000,
|
||||
doc_count: 3,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const alertsSummaryHttpResponse = {
|
||||
default: () => Promise.resolve({ ...summaryResponse }),
|
||||
};
|
||||
|
||||
export type AlertsSummaryHttpMocks = keyof typeof alertsSummaryHttpResponse;
|
|
@ -11,6 +11,7 @@ export {
|
|||
processesChartHttpResponse,
|
||||
type ProcessesHttpMocks,
|
||||
} from './processes';
|
||||
export { alertsSummaryHttpResponse, type AlertsSummaryHttpMocks } from './alerts';
|
||||
export { anomaliesHttpResponse, type AnomaliesHttpMocks } from './anomalies';
|
||||
export { snapshotAPItHttpResponse, type SnapshotAPIHttpMocks } from './snapshot_api';
|
||||
export { getLogEntries } from './log_entries';
|
||||
|
|
|
@ -9,11 +9,13 @@ import type { HttpStart, HttpHandler } from '@kbn/core/public';
|
|||
import type { Parameters } from '@storybook/react';
|
||||
import { INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH } from '../../../../../common/http_api/infra_ml';
|
||||
import {
|
||||
alertsSummaryHttpResponse,
|
||||
anomaliesHttpResponse,
|
||||
metadataHttpResponse,
|
||||
processesChartHttpResponse,
|
||||
processesHttpResponse,
|
||||
snapshotAPItHttpResponse,
|
||||
type AlertsSummaryHttpMocks,
|
||||
type AnomaliesHttpMocks,
|
||||
type MetadataResponseMocks,
|
||||
type ProcessesHttpMocks,
|
||||
|
@ -43,6 +45,14 @@ export const getHttp = (params: Parameters): HttpStart => {
|
|||
return Promise.resolve({});
|
||||
}
|
||||
}) as HttpHandler,
|
||||
post: (async (path: string) => {
|
||||
switch (path) {
|
||||
case '/internal/rac/alerts/_alert_summary':
|
||||
return alertsSummaryHttpResponse[params.mock as AlertsSummaryHttpMocks]();
|
||||
default:
|
||||
return Promise.resolve({});
|
||||
}
|
||||
}) as HttpHandler,
|
||||
} as unknown as HttpStart;
|
||||
|
||||
return http;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { JSXElementConstructor, ReactElement } from 'react';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import {
|
||||
KibanaContextProvider,
|
||||
|
@ -18,6 +18,9 @@ import { useParameter } from '@storybook/addons';
|
|||
import type { DeepPartial } from 'utility-types';
|
||||
import type { LocatorPublic } from '@kbn/share-plugin/public';
|
||||
import type { IKibanaSearchRequest, ISearchOptions } from '@kbn/data-plugin/public';
|
||||
import { AlertSummaryWidget } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alert_summary_widget/alert_summary_widget';
|
||||
import type { Theme } from '@elastic/charts/dist/utils/themes/theme';
|
||||
import type { AlertSummaryWidgetProps } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alert_summary_widget';
|
||||
import type { PluginKibanaContextValue } from '../../../hooks/use_kibana';
|
||||
import { SourceProvider } from '../../../containers/metrics_source';
|
||||
import { getHttp } from './context/http';
|
||||
|
@ -66,6 +69,20 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => {
|
|||
return Promise.resolve([]);
|
||||
},
|
||||
},
|
||||
uiSettings: {
|
||||
get: () => ({ key: 'mock', defaultOverride: undefined } as any),
|
||||
},
|
||||
triggersActionsUi: {
|
||||
getAlertSummaryWidget: AlertSummaryWidget as (
|
||||
props: AlertSummaryWidgetProps
|
||||
) => ReactElement<AlertSummaryWidgetProps, string | JSXElementConstructor<any>>,
|
||||
},
|
||||
charts: {
|
||||
theme: {
|
||||
useChartsTheme: () => ({} as Theme),
|
||||
useChartsBaseTheme: () => ({} as Theme),
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
client: {
|
||||
get$: (key: string) => of(getSettings(key)),
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiText, EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { ALERTS_DOC_HREF } from '../../../common/alerts/constants';
|
||||
import { LinkToAlertsHomePage } from '../links/link_to_alerts_page';
|
||||
|
||||
export const AlertsTooltipContent = React.memo(() => {
|
||||
const onClick = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiText size="xs" onClick={onClick}>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.assetDetails.alerts.tooltip.alertsLabel"
|
||||
defaultMessage="Showing alerts for this host. You can create and manage alerts in {alerts}"
|
||||
values={{
|
||||
alerts: <LinkToAlertsHomePage />,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.assetDetails.alerts.tooltip.documentationLabel"
|
||||
defaultMessage="See {documentation} for more information"
|
||||
values={{
|
||||
documentation: (
|
||||
<EuiLink
|
||||
data-test-subj="assetDetailsTooltipDocumentationLink"
|
||||
href={ALERTS_DOC_HREF}
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.assetDetails.alerts.tooltip.documentationLink"
|
||||
defaultMessage="documentation"
|
||||
/>
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
);
|
||||
});
|
|
@ -8,11 +8,11 @@ import React from 'react';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiButtonEmpty } from '@elastic/eui';
|
||||
|
||||
export interface LinkToAlertsRule {
|
||||
export interface LinkToAlertsRuleProps {
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export const LinkToAlertsRule = ({ onClick }: LinkToAlertsRule) => {
|
||||
export const LinkToAlertsRule = ({ onClick }: LinkToAlertsRuleProps) => {
|
||||
return (
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="infraNodeContextPopoverCreateInventoryRuleButton"
|
||||
|
@ -23,8 +23,8 @@ export const LinkToAlertsRule = ({ onClick }: LinkToAlertsRule) => {
|
|||
iconType="bell"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.infra.nodeDetails.createAlertLink"
|
||||
defaultMessage="Create inventory rule"
|
||||
id="xpack.infra.infra.assetDetails.alerts.createAlertLink"
|
||||
defaultMessage="Create rule"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* 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';
|
||||
import { encode } from '@kbn/rison';
|
||||
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiButtonEmpty, EuiLink } from '@elastic/eui';
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import { ALERTS_PATH } from '../../../common/alerts/constants';
|
||||
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
|
||||
|
||||
export interface LinkToAlertsPageProps {
|
||||
nodeName: string;
|
||||
queryField: string;
|
||||
dateRange: TimeRange;
|
||||
}
|
||||
|
||||
export const LinkToAlertsPage = ({ nodeName, queryField, dateRange }: LinkToAlertsPageProps) => {
|
||||
const { services } = useKibanaContextForPlugin();
|
||||
const { http } = services;
|
||||
|
||||
const linkToAlertsPage = http.basePath.prepend(
|
||||
`${ALERTS_PATH}?_a=${encode({
|
||||
kuery: `${queryField}:"${nodeName}"`,
|
||||
rangeFrom: dateRange.from,
|
||||
rangeTo: dateRange.to,
|
||||
status: 'all',
|
||||
})}`
|
||||
);
|
||||
|
||||
return (
|
||||
<RedirectAppLinks coreStart={services}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="assetDetails-flyout-alerts-link"
|
||||
size="xs"
|
||||
iconSide="right"
|
||||
iconType="sortRight"
|
||||
flush="both"
|
||||
href={linkToAlertsPage}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.assetDetails.flyout.AlertsPageLinkLabel"
|
||||
defaultMessage="Show all"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
};
|
||||
|
||||
export const LinkToAlertsHomePage = () => {
|
||||
const { services } = useKibanaContextForPlugin();
|
||||
const { http } = services;
|
||||
|
||||
const linkToAlertsPage = http.basePath.prepend(ALERTS_PATH);
|
||||
|
||||
return (
|
||||
<RedirectAppLinks coreStart={services}>
|
||||
<EuiLink data-test-subj="assetDetailsTooltipDocumentationLink" href={linkToAlertsPage}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.assetDetails.table.tooltip.alertsLink"
|
||||
defaultMessage="alerts."
|
||||
/>
|
||||
</EuiLink>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* 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, { useMemo } from 'react';
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiPopover, EuiIcon, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useSummaryTimeRange } from '@kbn/observability-plugin/public';
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import type { AlertsEsQuery } from '../../../../common/alerts/types';
|
||||
import { AlertsTooltipContent } from '../../components/alerts_tooltip_content';
|
||||
import type { InventoryItemType } from '../../../../../common/inventory_models/types';
|
||||
import { findInventoryFields } from '../../../../../common/inventory_models';
|
||||
import { createAlertsEsQuery } from '../../../../common/alerts/create_alerts_es_query';
|
||||
import { infraAlertFeatureIds } from '../../../../pages/metrics/hosts/components/tabs/config';
|
||||
|
||||
import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana';
|
||||
import { LinkToAlertsRule } from '../../links/link_to_alerts';
|
||||
import { LinkToAlertsPage } from '../../links/link_to_alerts_page';
|
||||
import { AlertFlyout } from '../../../../alerting/inventory/components/alert_flyout';
|
||||
import { useBoolean } from '../../../../hooks/use_boolean';
|
||||
import { ALERT_STATUS_ALL } from '../../../../common/alerts/constants';
|
||||
|
||||
export const AlertsSummaryContent = ({
|
||||
nodeName,
|
||||
nodeType,
|
||||
dateRange,
|
||||
}: {
|
||||
nodeName: string;
|
||||
nodeType: InventoryItemType;
|
||||
dateRange: TimeRange;
|
||||
}) => {
|
||||
const [isAlertFlyoutVisible, { toggle: toggleAlertFlyout }] = useBoolean(false);
|
||||
|
||||
const alertsEsQueryByStatus = useMemo(
|
||||
() =>
|
||||
createAlertsEsQuery({
|
||||
dateRange,
|
||||
hostNodeNames: [nodeName],
|
||||
status: ALERT_STATUS_ALL,
|
||||
}),
|
||||
[nodeName, dateRange]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
|
||||
<EuiFlexItem>
|
||||
<AlertsSectionTitle />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<LinkToAlertsRule onClick={toggleAlertFlyout} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<LinkToAlertsPage
|
||||
nodeName={nodeName}
|
||||
queryField={`${nodeType}.name`}
|
||||
dateRange={dateRange}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
<MemoAlertSummaryWidget alertsQuery={alertsEsQueryByStatus} dateRange={dateRange} />
|
||||
<AlertFlyout
|
||||
filter={`${findInventoryFields(nodeType).name}: "${nodeName}"`}
|
||||
nodeType={nodeType}
|
||||
setVisible={toggleAlertFlyout}
|
||||
visible={isAlertFlyoutVisible}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface MemoAlertSummaryWidgetProps {
|
||||
alertsQuery: AlertsEsQuery;
|
||||
dateRange: TimeRange;
|
||||
}
|
||||
|
||||
const MemoAlertSummaryWidget = React.memo(
|
||||
({ alertsQuery, dateRange }: MemoAlertSummaryWidgetProps) => {
|
||||
const { services } = useKibanaContextForPlugin();
|
||||
|
||||
const summaryTimeRange = useSummaryTimeRange(dateRange);
|
||||
|
||||
const { charts, triggersActionsUi } = services;
|
||||
const { getAlertSummaryWidget: AlertSummaryWidget } = triggersActionsUi;
|
||||
|
||||
const chartProps = {
|
||||
theme: charts.theme.useChartsTheme(),
|
||||
baseTheme: charts.theme.useChartsBaseTheme(),
|
||||
};
|
||||
|
||||
return (
|
||||
<AlertSummaryWidget
|
||||
chartProps={chartProps}
|
||||
featureIds={infraAlertFeatureIds}
|
||||
filter={alertsQuery}
|
||||
timeRange={summaryTimeRange}
|
||||
fullSize
|
||||
hideChart
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
const AlertsSectionTitle = () => {
|
||||
const [isPopoverOpen, { off: closePopover, toggle: togglePopover }] = useBoolean(false);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle data-test-subj="assetDetailsAlertsTitle" size="xxs">
|
||||
<h5>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.assetDetails.overview.alertsSectionTitle"
|
||||
defaultMessage="Alerts"
|
||||
/>
|
||||
</h5>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiPopover
|
||||
button={
|
||||
<EuiIcon
|
||||
data-test-subj="assetDetailsAlertsPopoverButton"
|
||||
type="iInCircle"
|
||||
onClick={togglePopover}
|
||||
/>
|
||||
}
|
||||
isOpen={isPopoverOpen}
|
||||
closePopover={closePopover}
|
||||
repositionOnScroll
|
||||
anchorPosition="upCenter"
|
||||
>
|
||||
<AlertsTooltipContent />
|
||||
</EuiPopover>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLink, EuiHorizontalRule } from '@elastic/eui';
|
||||
import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
|
@ -17,6 +17,7 @@ import { findInventoryModel } from '../../../../../common/inventory_models';
|
|||
import { useMetadata } from '../../hooks/use_metadata';
|
||||
import { useSourceContext } from '../../../../containers/metrics_source';
|
||||
import { MetadataSummary } from './metadata_summary';
|
||||
import { AlertsSummaryContent } from './alerts';
|
||||
import { KPIGrid } from './kpis/kpi_grid';
|
||||
import { MetricsGrid } from './metrics/metrics_grid';
|
||||
import { toTimestampRange } from '../../utils';
|
||||
|
@ -92,6 +93,10 @@ export const Overview = ({
|
|||
)}
|
||||
<SectionSeparator />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<AlertsSummaryContent nodeName={nodeName} nodeType={nodeType} dateRange={dateRange} />
|
||||
<SectionSeparator />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<MetricsGrid
|
||||
timeRange={dateRange}
|
||||
|
|
|
@ -8,8 +8,12 @@
|
|||
import { EuiButtonGroup, EuiButtonGroupOptionProps } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { ACTIVE_ALERTS, ALL_ALERTS, RECOVERED_ALERTS } from '../../../constants';
|
||||
import { AlertStatus } from '../../../types';
|
||||
import type { AlertStatus } from '@kbn/observability-plugin/common/typings';
|
||||
import {
|
||||
ACTIVE_ALERTS,
|
||||
ALL_ALERTS,
|
||||
RECOVERED_ALERTS,
|
||||
} from '../../../../../../common/alerts/constants';
|
||||
export interface AlertStatusFilterProps {
|
||||
status: AlertStatus;
|
||||
onChange: (id: AlertStatus) => void;
|
||||
|
|
|
@ -4,29 +4,18 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import {
|
||||
calculateTimeRangeBucketSize,
|
||||
getAlertSummaryTimeRange,
|
||||
useTimeBuckets,
|
||||
} from '@kbn/observability-plugin/public';
|
||||
import { AlertConsumers } from '@kbn/rule-data-utils';
|
||||
import { TimeRange } from '@kbn/es-query';
|
||||
import { BrushEndListener, XYBrushEvent } from '@elastic/charts';
|
||||
import { BrushEndListener, type XYBrushEvent } from '@elastic/charts';
|
||||
import { useSummaryTimeRange } from '@kbn/observability-plugin/public';
|
||||
import type { AlertsEsQuery } from '../../../../../../common/alerts/types';
|
||||
import { useKibanaContextForPlugin } from '../../../../../../hooks/use_kibana';
|
||||
import { HeightRetainer } from '../../../../../../components/height_retainer';
|
||||
import { useUnifiedSearchContext } from '../../../hooks/use_unified_search';
|
||||
|
||||
import {
|
||||
ALERTS_PER_PAGE,
|
||||
ALERTS_TABLE_ID,
|
||||
DEFAULT_DATE_FORMAT,
|
||||
DEFAULT_INTERVAL,
|
||||
infraAlertFeatureIds,
|
||||
} from '../config';
|
||||
import { AlertsEsQuery, useAlertsQuery } from '../../../hooks/use_alerts_query';
|
||||
import { useAlertsQuery } from '../../../hooks/use_alerts_query';
|
||||
import AlertsStatusFilter from './alerts_status_filter';
|
||||
import { ALERTS_PER_PAGE, ALERTS_TABLE_ID, infraAlertFeatureIds } from '../config';
|
||||
import { HostsState, HostsStateUpdater } from '../../../hooks/use_unified_search_url_state';
|
||||
|
||||
export const AlertsTabContent = () => {
|
||||
|
@ -120,18 +109,3 @@ const MemoAlertSummaryWidget = React.memo(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
const useSummaryTimeRange = (unifiedSearchDateRange: TimeRange) => {
|
||||
const timeBuckets = useTimeBuckets();
|
||||
|
||||
const bucketSize = useMemo(
|
||||
() => calculateTimeRangeBucketSize(unifiedSearchDateRange, timeBuckets),
|
||||
[unifiedSearchDateRange, timeBuckets]
|
||||
);
|
||||
|
||||
return getAlertSummaryTimeRange(
|
||||
unifiedSearchDateRange,
|
||||
bucketSize?.intervalString || DEFAULT_INTERVAL,
|
||||
bucketSize?.dateFormat || DEFAULT_DATE_FORMAT
|
||||
);
|
||||
};
|
||||
|
|
|
@ -13,6 +13,3 @@ export const ALERTS_TABLE_ID = 'xpack.infra.hosts.alerts.table';
|
|||
|
||||
export const INFRA_ALERT_FEATURE_ID = 'infrastructure';
|
||||
export const infraAlertFeatureIds: ValidFeatureId[] = [AlertConsumers.INFRASTRUCTURE];
|
||||
|
||||
export const DEFAULT_INTERVAL = '60s';
|
||||
export const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm';
|
||||
|
|
|
@ -5,11 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED } from '@kbn/rule-data-utils';
|
||||
import { AlertStatusFilter, HostLimitOptions } from './types';
|
||||
import { HostLimitOptions } from './types';
|
||||
|
||||
export const ALERT_STATUS_ALL = 'all';
|
||||
export const TIMESTAMP_FIELD = '@timestamp';
|
||||
export const DATA_VIEW_PREFIX = 'infra_metrics';
|
||||
|
||||
|
@ -21,45 +18,5 @@ export const LOCAL_STORAGE_PAGE_SIZE_KEY = 'hostsView:pageSizeSelection';
|
|||
export const KPI_CHART_MIN_HEIGHT = 150;
|
||||
export const METRIC_CHART_MIN_HEIGHT = 300;
|
||||
|
||||
export const ALL_ALERTS: AlertStatusFilter = {
|
||||
status: ALERT_STATUS_ALL,
|
||||
label: i18n.translate('xpack.infra.hostsViewPage.tabs.alerts.alertStatusFilter.showAll', {
|
||||
defaultMessage: 'Show all',
|
||||
}),
|
||||
};
|
||||
|
||||
export const ACTIVE_ALERTS: AlertStatusFilter = {
|
||||
status: ALERT_STATUS_ACTIVE,
|
||||
query: {
|
||||
term: {
|
||||
[ALERT_STATUS]: {
|
||||
value: ALERT_STATUS_ACTIVE,
|
||||
},
|
||||
},
|
||||
},
|
||||
label: i18n.translate('xpack.infra.hostsViewPage.tabs.alerts.alertStatusFilter.active', {
|
||||
defaultMessage: 'Active',
|
||||
}),
|
||||
};
|
||||
|
||||
export const RECOVERED_ALERTS: AlertStatusFilter = {
|
||||
status: ALERT_STATUS_RECOVERED,
|
||||
query: {
|
||||
term: {
|
||||
[ALERT_STATUS]: {
|
||||
value: ALERT_STATUS_RECOVERED,
|
||||
},
|
||||
},
|
||||
},
|
||||
label: i18n.translate('xpack.infra.hostsViewPage.tabs.alerts.alertStatusFilter.recovered', {
|
||||
defaultMessage: 'Recovered',
|
||||
}),
|
||||
};
|
||||
|
||||
export const ALERT_STATUS_QUERY = {
|
||||
[ACTIVE_ALERTS.status]: ACTIVE_ALERTS.query,
|
||||
[RECOVERED_ALERTS.status]: RECOVERED_ALERTS.query,
|
||||
};
|
||||
|
||||
export const HOST_LIMIT_OPTIONS = [10, 20, 50, 100, 500] as const;
|
||||
export const HOST_METRICS_DOC_HREF = 'https://ela.st/docs-infra-host-metrics';
|
||||
|
|
|
@ -6,20 +6,10 @@
|
|||
*/
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import createContainer from 'constate';
|
||||
import { getTime } from '@kbn/data-plugin/common';
|
||||
import { ALERT_TIME_RANGE } from '@kbn/rule-data-utils';
|
||||
import { BoolQuery, buildEsQuery, Filter } from '@kbn/es-query';
|
||||
import { InfraAssetMetricsItem } from '../../../../../common/http_api';
|
||||
import type { AlertStatus } from '@kbn/observability-plugin/common/typings';
|
||||
import { createAlertsEsQuery } from '../../../../common/alerts/create_alerts_es_query';
|
||||
import { useUnifiedSearchContext } from './use_unified_search';
|
||||
import { HostsState } from './use_unified_search_url_state';
|
||||
import { useHostsViewContext } from './use_hosts_view';
|
||||
import { AlertStatus } from '../types';
|
||||
import { ALERT_STATUS_QUERY } from '../constants';
|
||||
import { buildCombinedHostsFilter } from '../../../../utils/filters/build';
|
||||
|
||||
export interface AlertsEsQuery {
|
||||
bool: BoolQuery;
|
||||
}
|
||||
|
||||
export const useAlertsQueryImpl = () => {
|
||||
const { hostNodes } = useHostsViewContext();
|
||||
|
@ -28,10 +18,12 @@ export const useAlertsQueryImpl = () => {
|
|||
|
||||
const [alertStatus, setAlertStatus] = useState<AlertStatus>('all');
|
||||
|
||||
const hostNodeNames = useMemo(() => hostNodes.map((n) => n.name), [hostNodes]);
|
||||
|
||||
const getAlertsEsQuery = useCallback(
|
||||
(status?: AlertStatus) =>
|
||||
createAlertsEsQuery({ dateRange: searchCriteria.dateRange, hostNodes, status }),
|
||||
[hostNodes, searchCriteria.dateRange]
|
||||
createAlertsEsQuery({ dateRange: searchCriteria.dateRange, hostNodeNames, status }),
|
||||
[hostNodeNames, searchCriteria.dateRange]
|
||||
);
|
||||
|
||||
// Regenerate the query when status change even if is not used.
|
||||
|
@ -53,34 +45,3 @@ export const useAlertsQueryImpl = () => {
|
|||
|
||||
export const AlertsQueryContainer = createContainer(useAlertsQueryImpl);
|
||||
export const [AlertsQueryProvider, useAlertsQuery] = AlertsQueryContainer;
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
const createAlertsEsQuery = ({
|
||||
dateRange,
|
||||
hostNodes,
|
||||
status,
|
||||
}: {
|
||||
dateRange: HostsState['dateRange'];
|
||||
hostNodes: InfraAssetMetricsItem[];
|
||||
status?: AlertStatus;
|
||||
}): AlertsEsQuery => {
|
||||
const alertStatusFilter = createAlertStatusFilter(status);
|
||||
|
||||
const dateFilter = createDateFilter(dateRange);
|
||||
const hostsFilter = buildCombinedHostsFilter({
|
||||
field: 'host.name',
|
||||
values: hostNodes.map((p) => p.name),
|
||||
});
|
||||
|
||||
const filters = [alertStatusFilter, dateFilter, hostsFilter].filter(Boolean) as Filter[];
|
||||
|
||||
return buildEsQuery(undefined, [], filters);
|
||||
};
|
||||
|
||||
const createDateFilter = (date: HostsState['dateRange']) =>
|
||||
getTime(undefined, date, { fieldName: ALERT_TIME_RANGE });
|
||||
|
||||
const createAlertStatusFilter = (status: AlertStatus = 'all'): Filter | null =>
|
||||
ALERT_STATUS_QUERY[status] ? { query: ALERT_STATUS_QUERY[status], meta: {} } : null;
|
||||
|
|
|
@ -4,20 +4,6 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Filter } from '@kbn/es-query';
|
||||
import { ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED } from '@kbn/rule-data-utils';
|
||||
import { ALERT_STATUS_ALL, HOST_LIMIT_OPTIONS } from './constants';
|
||||
|
||||
export type AlertStatus =
|
||||
| typeof ALERT_STATUS_ACTIVE
|
||||
| typeof ALERT_STATUS_RECOVERED
|
||||
| typeof ALERT_STATUS_ALL;
|
||||
|
||||
export interface AlertStatusFilter {
|
||||
status: AlertStatus;
|
||||
query?: Filter['query'];
|
||||
label: string;
|
||||
}
|
||||
import { HOST_LIMIT_OPTIONS } from './constants';
|
||||
|
||||
export type HostLimitOptions = typeof HOST_LIMIT_OPTIONS[number];
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import { useUiSetting } from '@kbn/kibana-react-plugin/public';
|
||||
import { calculateTimeRangeBucketSize, getAlertSummaryTimeRange, useTimeBuckets } from '..';
|
||||
import { DEFAULT_INTERVAL, DEFAULT_DATE_FORMAT } from '../constants';
|
||||
|
||||
export const useSummaryTimeRange = (unifiedSearchDateRange: TimeRange) => {
|
||||
const timeBuckets = useTimeBuckets();
|
||||
const dateFormat = useUiSetting<string>('dateFormat');
|
||||
|
||||
const bucketSize = useMemo(
|
||||
() => calculateTimeRangeBucketSize(unifiedSearchDateRange, timeBuckets),
|
||||
[unifiedSearchDateRange, timeBuckets]
|
||||
);
|
||||
|
||||
return getAlertSummaryTimeRange(
|
||||
unifiedSearchDateRange,
|
||||
bucketSize?.intervalString ?? DEFAULT_INTERVAL,
|
||||
bucketSize?.dateFormat ?? dateFormat ?? DEFAULT_DATE_FORMAT
|
||||
);
|
||||
};
|
|
@ -70,6 +70,7 @@ export { observabilityFeatureId, observabilityAppId } from '../common';
|
|||
|
||||
export { useTimeBuckets } from './hooks/use_time_buckets';
|
||||
export { createUseRulesLink } from './hooks/create_use_rules_link';
|
||||
export { useSummaryTimeRange } from './hooks/use_summary_time_range';
|
||||
|
||||
export { getApmTraceUrl } from './utils/get_apm_trace_url';
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ export const AlertSummaryWidget = ({
|
|||
timeRange,
|
||||
});
|
||||
|
||||
if (isLoading) return <AlertSummaryWidgetLoader fullSize={fullSize} />;
|
||||
if (isLoading)
|
||||
return <AlertSummaryWidgetLoader fullSize={fullSize} isLoadingWithoutChart={hideChart} />;
|
||||
|
||||
if (error) return <AlertSummaryWidgetError />;
|
||||
|
||||
|
|
|
@ -6,22 +6,29 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiLoadingChart } from '@elastic/eui';
|
||||
import { EuiLoadingChart, EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { AlertSummaryWidgetProps } from '..';
|
||||
|
||||
type Props = Pick<AlertSummaryWidgetProps, 'fullSize'>;
|
||||
type Props = { isLoadingWithoutChart: boolean | undefined } & Pick<
|
||||
AlertSummaryWidgetProps,
|
||||
'fullSize'
|
||||
>;
|
||||
|
||||
export const AlertSummaryWidgetLoader = ({ fullSize }: Props) => {
|
||||
export const AlertSummaryWidgetLoader = ({ fullSize, isLoadingWithoutChart }: Props) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
minHeight: fullSize ? 238 : 224,
|
||||
minHeight: isLoadingWithoutChart ? 44 : fullSize ? 238 : 224,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
justifyContent: isLoadingWithoutChart ? 'flex-start' : 'center',
|
||||
}}
|
||||
>
|
||||
<EuiLoadingChart size="l" data-test-subj="alertSummaryWidgetLoading" />
|
||||
{isLoadingWithoutChart ? (
|
||||
<EuiLoadingSpinner size="m" />
|
||||
) : (
|
||||
<EuiLoadingChart size="l" data-test-subj="alertSummaryWidgetLoading" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -15,7 +15,14 @@ const AlertSummaryWidgetLazy: React.FC<AlertSummaryWidgetProps> = lazy(
|
|||
|
||||
export const getAlertSummaryWidgetLazy = (props: AlertSummaryWidgetProps) => {
|
||||
return (
|
||||
<Suspense fallback={<AlertSummaryWidgetLoader fullSize={props.fullSize} />}>
|
||||
<Suspense
|
||||
fallback={
|
||||
<AlertSummaryWidgetLoader
|
||||
fullSize={props.fullSize}
|
||||
isLoadingWithoutChart={props.hideChart}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AlertSummaryWidgetLazy {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
|
|
@ -295,6 +295,40 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
await pageObjects.infraHostsView.clickShowAllMetadataOverviewTab();
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
await pageObjects.infraHostsView.metadataTableExist();
|
||||
await pageObjects.infraHostsView.clickOverviewFlyoutTab();
|
||||
});
|
||||
|
||||
it('should show alerts', async () => {
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
await pageObjects.infraHostsView.overviewAlertsTitleExist();
|
||||
});
|
||||
|
||||
it('should open alerts flyout', async () => {
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
await pageObjects.infraHostsView.clickOverviewOpenAlertsFlyout();
|
||||
// There are 2 flyouts open (asset details and alerts)
|
||||
// so we need a stricter selector
|
||||
// to be sure that we are closing the alerts flyout
|
||||
const closeAlertFlyout = await find.byCssSelector(
|
||||
'[aria-labelledby="flyoutRuleAddTitle"] > [data-test-subj="euiFlyoutCloseButton"]'
|
||||
);
|
||||
await closeAlertFlyout.click();
|
||||
});
|
||||
|
||||
it('should navigate to alerts', async () => {
|
||||
await pageObjects.infraHostsView.clickOverviewLinkToAlerts();
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
const url = parse(await browser.getCurrentUrl());
|
||||
|
||||
const query = decodeURIComponent(url.query ?? '');
|
||||
|
||||
const alertsQuery =
|
||||
"_a=(kuery:'host.name:\"Jennys-MBP.fritz.box\"',rangeFrom:'2023-03-28T18:20:00.000Z',rangeTo:'2023-03-28T18:21:00.000Z',status:all)";
|
||||
|
||||
expect(url.pathname).to.eql('/app/observability/alerts');
|
||||
expect(query).to.contain(alertsQuery);
|
||||
|
||||
await returnTo(HOSTS_VIEW_PATH);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -479,6 +513,26 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should render alerts count for a host inside a flyout', async () => {
|
||||
await pageObjects.infraHostsView.clickHostCheckbox('demo-stack-mysql-01', '-');
|
||||
await pageObjects.infraHostsView.clickSelectedHostsButton();
|
||||
await pageObjects.infraHostsView.clickSelectedHostsAddFilterButton();
|
||||
await pageObjects.infraHostsView.clickTableOpenFlyoutButton();
|
||||
|
||||
const activeAlertsCount = await pageObjects.infraHostsView.getActiveAlertsCountText();
|
||||
const totalAlertsCount = await pageObjects.infraHostsView.getTotalAlertsCountText();
|
||||
|
||||
expect(activeAlertsCount).to.equal('2 ');
|
||||
expect(totalAlertsCount).to.equal('3');
|
||||
|
||||
const deleteFilterButton = await find.byCssSelector(
|
||||
`[title="Delete host.name: demo-stack-mysql-01"]`
|
||||
);
|
||||
await deleteFilterButton.click();
|
||||
|
||||
await pageObjects.infraHostsView.clickCloseFlyoutButton();
|
||||
});
|
||||
|
||||
it('should render "N/A" when processes summary is not available in flyout', async () => {
|
||||
await pageObjects.infraHostsView.clickTableOpenFlyoutButton();
|
||||
await pageObjects.infraHostsView.clickProcessesFlyoutTab();
|
||||
|
|
|
@ -48,6 +48,14 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) {
|
|||
return testSubjects.click('hostsView-flyout-tabs-metadata');
|
||||
},
|
||||
|
||||
async clickOverviewLinkToAlerts() {
|
||||
return testSubjects.click('assetDetails-flyout-alerts-link');
|
||||
},
|
||||
|
||||
async clickOverviewOpenAlertsFlyout() {
|
||||
return testSubjects.click('infraNodeContextPopoverCreateInventoryRuleButton');
|
||||
},
|
||||
|
||||
async clickProcessesFlyoutTab() {
|
||||
return testSubjects.click('hostsView-flyout-tabs-processes');
|
||||
},
|
||||
|
@ -203,6 +211,22 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) {
|
|||
return div.getAttribute('title');
|
||||
},
|
||||
|
||||
overviewAlertsTitleExist() {
|
||||
return testSubjects.exists('assetDetailsAlertsTitle');
|
||||
},
|
||||
|
||||
async getActiveAlertsCountText() {
|
||||
const container = await testSubjects.find('activeAlertCount');
|
||||
const containerText = await container.getVisibleText();
|
||||
return containerText;
|
||||
},
|
||||
|
||||
async getTotalAlertsCountText() {
|
||||
const container = await testSubjects.find('totalAlertCount');
|
||||
const containerText = await container.getVisibleText();
|
||||
return containerText;
|
||||
},
|
||||
|
||||
async getAssetDetailsMetricsCharts() {
|
||||
const container = await testSubjects.find('assetDetailsMetricsChartGrid');
|
||||
return container.findAllByCssSelector('[data-test-subj*="assetDetailsMetricsChart"]');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue