[Infra] Add alert links in hosts alert tab (#181039)

## Summary

close #180507

- Move the alert links to a shared folder
- Refactor `LinkToAlertsPage` to accept abstract `kuery` 
- Display the `Create rule` and `Show all` links in the alert tab in the
hosts view page

## After 


2abb2327-a0d1-4ffa-804f-7ceee85ac42d




## Notes
Passing the kuery `kuery={`${ALERT_RULE_PRODUCER}:
${INFRA_ALERT_FEATURE_ID}`}` to the alertPage in order to show all
alerts produced by infrastructure. However, the total count may differ
from the Alert tab, as we only show alerts for visible hosts in the
table.
This commit is contained in:
Katerina 2024-04-22 17:13:52 +03:00 committed by GitHub
parent c66c019f25
commit 0170f5eba5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 205 additions and 33 deletions

View file

@ -8,8 +8,8 @@
import React from 'react';
import { EuiText, EuiLink } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { LinkToAlertsHomePage } from '../links/link_to_alerts_page';
import { ALERTS_DOC_HREF } from '../../shared/alerts/constants';
import { LinkToAlertsHomePage } from '../../shared/alerts/links/link_to_alerts_page';
export const AlertsTooltipContent = React.memo(() => {
const onClick = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
@ -23,7 +23,7 @@ export const AlertsTooltipContent = React.memo(() => {
id="xpack.infra.assetDetails.alerts.tooltip.alertsLabel"
defaultMessage="Showing alerts for this host. You can create and manage alerts in {alerts}"
values={{
alerts: <LinkToAlertsHomePage />,
alerts: <LinkToAlertsHomePage dataTestSubj="assetDetailsTooltipDocumentationLink" />,
}}
/>
</p>

View file

@ -21,8 +21,9 @@ import { useHistory, useLocation } from 'react-router-dom';
import { usePluginConfig } from '../../../containers/plugin_config_context';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
import { useProfilingIntegrationSetting } from '../../../hooks/use_profiling_integration_setting';
import { CreateAlertRuleButton } from '../../shared/alerts/links/create_alert_rule_button';
import { APM_HOST_FILTER_FIELD } from '../constants';
import { LinkToAlertsRule, LinkToNodeDetails } from '../links';
import { LinkToNodeDetails } from '../links';
import { ContentTabIds, type LinkOptions, type RouteState, type Tab, type TabIds } from '../types';
import { useAssetDetailsRenderPropsContext } from './use_asset_details_render_props';
import { useTabSwitcherContext } from './use_tab_switcher';
@ -97,7 +98,9 @@ const useRightSideItems = (links?: LinkOptions[]) => {
nodeDetails: (
<LinkToNodeDetails assetId={asset.id} assetName={asset.name} assetType={asset.type} />
),
alertRule: <LinkToAlertsRule />,
alertRule: (
<CreateAlertRuleButton data-test-subj="infraAssetDetailsPageHeaderCreateAlertsRuleButton" />
),
}),
[asset.id, asset.name, asset.type]
);

View file

@ -6,5 +6,4 @@
*/
export { LinkToApmServices } from './link_to_apm_services';
export { LinkToAlertsRule } from './link_to_alerts';
export { LinkToNodeDetails } from './link_to_node_details';

View file

@ -11,8 +11,6 @@ import type { TimeRange } from '@kbn/es-query';
import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common';
import { usePluginConfig } from '../../../../../containers/plugin_config_context';
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 { AlertsSectionTitle } from '../section_titles';
@ -21,6 +19,8 @@ import { Section } from '../../../components/section';
import { AlertsClosedContent } from './alerts_closed_content';
import { type AlertsCount } from '../../../../../hooks/use_alerts_count';
import { AlertsOverview } from '../../../../shared/alerts/alerts_overview';
import { CreateAlertRuleButton } from '../../../../shared/alerts/links/create_alert_rule_button';
import { LinkToAlertsPage } from '../../../../shared/alerts/links/link_to_alerts_page';
export const AlertsSummaryContent = ({
assetId,
@ -61,11 +61,18 @@ export const AlertsSummaryContent = ({
<EuiFlexGroup alignItems="center" responsive={false}>
{featureFlags.inventoryThresholdAlertRuleEnabled && (
<EuiFlexItem grow={false}>
<LinkToAlertsRule onClick={toggleAlertFlyout} />
<CreateAlertRuleButton
onClick={toggleAlertFlyout}
data-test-subj="infraAssetDetailsAlertsTabCreateAlertsRuleButton"
/>
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<LinkToAlertsPage assetId={assetId} queryField={assetIdField} dateRange={dateRange} />
<LinkToAlertsPage
kuery={`${assetIdField}:"${assetId}"`}
dateRange={dateRange}
data-test-subj="nfraAssetDetailsAlertsTabAlertsShowAllButton"
/>
</EuiFlexItem>
</EuiFlexGroup>
}

View file

@ -0,0 +1,55 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LinkToAlertsPage component renders correctly with default props 1`] = `
<div>
<a
class="euiButtonEmpty emotion-euiButtonDisplay-euiButtonEmpty-xs-empty-primary-flush-both"
data-test-subj="test-link"
href="/app/observability/alerts?_a=(rangeFrom:'2024-04-01',rangeTo:'2024-04-15',status:all)"
rel="noreferrer"
>
<span
class="euiButtonEmpty__content emotion-euiButtonDisplayContent"
>
<span
class="eui-textTruncate euiButtonEmpty__text"
>
<span>
Show all
</span>
</span>
<span
color="inherit"
data-euiicon-type="sortRight"
/>
</span>
</a>
</div>
`;
exports[`LinkToAlertsPage component renders correctly with optional props 1`] = `
<div>
<a
class="euiButtonEmpty emotion-euiButtonDisplay-euiButtonEmpty-xs-empty-primary-flush-both"
data-test-subj="test-link"
href="/app/observability/alerts?_a=(kuery:'foo:bar',rangeFrom:'2024-04-01',rangeTo:'2024-04-15',status:all)"
rel="noreferrer"
>
<span
class="euiButtonEmpty__content emotion-euiButtonDisplayContent"
>
<span
class="eui-textTruncate euiButtonEmpty__text"
>
<span>
Show all
</span>
</span>
<span
color="inherit"
data-euiicon-type="sortRight"
/>
</span>
</a>
</div>
`;

View file

@ -10,12 +10,16 @@ import { EuiButtonEmpty } from '@elastic/eui';
export interface LinkToAlertsRuleProps {
onClick?: () => void;
['data-test-subj']: string;
}
export const LinkToAlertsRule = ({ onClick }: LinkToAlertsRuleProps) => {
export const CreateAlertRuleButton = ({
onClick,
['data-test-subj']: dataTestSubj,
}: LinkToAlertsRuleProps) => {
return (
<EuiButtonEmpty
data-test-subj="infraAssetDetailsCreateAlertsRuleButton"
data-test-subj={dataTestSubj}
onClick={onClick}
size="xs"
iconSide="left"
@ -23,7 +27,7 @@ export const LinkToAlertsRule = ({ onClick }: LinkToAlertsRuleProps) => {
iconType="bell"
>
<FormattedMessage
id="xpack.infra.infra.assetDetails.alerts.createAlertLink"
id="xpack.infra.infra.alerts.createAlertLink"
defaultMessage="Create rule"
/>
</EuiButtonEmpty>

View file

@ -0,0 +1,78 @@
/*
* 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 { render, screen } from '@testing-library/react';
import { LinkToAlertsPage, LinkToAlertsPageProps } from './link_to_alerts_page';
import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana';
import { coreMock } from '@kbn/core/public/mocks';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
const useKibanaContextForPluginMock = useKibanaContextForPlugin as jest.MockedFunction<
typeof useKibanaContextForPlugin
>;
jest.mock('../../../../hooks/use_kibana');
describe('LinkToAlertsPage component', () => {
const mockUseKibana = () => {
useKibanaContextForPluginMock.mockReturnValue({
services: {
...coreMock.createStart(),
},
} as unknown as ReturnType<typeof useKibanaContextForPlugin>);
};
beforeEach(() => {
mockUseKibana();
});
it('renders correctly with default props', () => {
const props: LinkToAlertsPageProps = {
dateRange: { from: '2024-04-01', to: '2024-04-15' },
['data-test-subj']: 'test-link',
};
const { container } = render(
<IntlProvider locale="en">
<LinkToAlertsPage {...props} />
</IntlProvider>
);
expect(container).toMatchSnapshot();
});
it('renders correctly with optional props', () => {
const props: LinkToAlertsPageProps = {
dateRange: { from: '2024-04-01', to: '2024-04-15' },
kuery: 'foo:bar',
['data-test-subj']: 'test-link',
};
const { container } = render(
<IntlProvider locale="en">
<LinkToAlertsPage {...props} />
</IntlProvider>
);
const link = screen.getByTestId('test-link');
expect(link).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
it('generates correct link', () => {
const props: LinkToAlertsPageProps = {
dateRange: { from: '2024-04-01', to: '2024-04-15' },
kuery: 'foo:bar',
['data-test-subj']: 'test-link',
};
render(
<IntlProvider locale="en">
<LinkToAlertsPage {...props} />
</IntlProvider>
);
const href = screen.getByRole('link', { name: 'Show all' }).getAttribute('href');
expect(href).toContain(
"/app/observability/alerts?_a=(kuery:'foo:bar',rangeFrom:'2024-04-01',rangeTo:'2024-04-15',status:all)"
);
});
});

View file

@ -9,46 +9,46 @@ import { encode } from '@kbn/rison';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiButtonEmpty, EuiLink } from '@elastic/eui';
import type { TimeRange } from '@kbn/es-query';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
import { ALERTS_PATH } from '../../shared/alerts/constants';
import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana';
import { ALERTS_PATH } from '../constants';
export interface LinkToAlertsPageProps {
assetId: string;
dateRange: TimeRange;
queryField: string;
kuery?: string;
['data-test-subj']: string;
}
export const LinkToAlertsPage = ({ assetId, queryField, dateRange }: LinkToAlertsPageProps) => {
export const LinkToAlertsPage = ({
kuery,
dateRange,
['data-test-subj']: dataTestSubj,
}: LinkToAlertsPageProps) => {
const { services } = useKibanaContextForPlugin();
const { http } = services;
const linkToAlertsPage = http.basePath.prepend(
`${ALERTS_PATH}?_a=${encode({
kuery: `${queryField}:"${assetId}"`,
kuery,
rangeFrom: dateRange.from,
rangeTo: dateRange.to,
status: 'all',
})}`
);
return (
<EuiButtonEmpty
data-test-subj="infraAssetDetailsAlertsShowAllButton"
data-test-subj={dataTestSubj}
size="xs"
iconSide="right"
iconType="sortRight"
flush="both"
href={linkToAlertsPage}
>
<FormattedMessage
id="xpack.infra.assetDetails.flyout.AlertsPageLinkLabel"
defaultMessage="Show all"
/>
<FormattedMessage id="xpack.infra.AlertsPageLinkLabel" defaultMessage="Show all" />
</EuiButtonEmpty>
);
};
export const LinkToAlertsHomePage = () => {
export const LinkToAlertsHomePage = ({ dataTestSubj }: { dataTestSubj?: string }) => {
const { services } = useKibanaContextForPlugin();
const { http } = services;
@ -57,7 +57,7 @@ export const LinkToAlertsHomePage = () => {
return (
<EuiLink
style={{ display: 'inline-block' }}
data-test-subj="assetDetailsTooltipDocumentationLink"
data-test-subj={dataTestSubj}
href={linkToAlertsPage}
>
<FormattedMessage

View file

@ -6,7 +6,7 @@
*/
import React from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { AlertConsumers } from '@kbn/rule-data-utils';
import { AlertConsumers, ALERT_RULE_PRODUCER } from '@kbn/rule-data-utils';
import { BrushEndListener, type XYBrushEvent } from '@elastic/charts';
import { useSummaryTimeRange } from '@kbn/observability-plugin/public';
import { useKibanaContextForPlugin } from '../../../../../../hooks/use_kibana';
@ -21,11 +21,19 @@ import {
infraAlertFeatureIds,
} from '../../../../../../components/shared/alerts/constants';
import AlertsStatusFilter from '../../../../../../components/shared/alerts/alerts_status_filter';
import { CreateAlertRuleButton } from '../../../../../../components/shared/alerts/links/create_alert_rule_button';
import { LinkToAlertsPage } from '../../../../../../components/shared/alerts/links/link_to_alerts_page';
import { INFRA_ALERT_FEATURE_ID } from '../../../../../../../common/constants';
import { AlertFlyout } from '../../../../../../alerting/inventory/components/alert_flyout';
import { useBoolean } from '../../../../../../hooks/use_boolean';
import { usePluginConfig } from '../../../../../../containers/plugin_config_context';
export const AlertsTabContent = () => {
const { services } = useKibanaContextForPlugin();
const { featureFlags } = usePluginConfig();
const { alertStatus, setAlertStatus, alertsEsQueryByStatus } = useAlertsQuery();
const [isAlertFlyoutVisible, { toggle: toggleAlertFlyout }] = useBoolean(false);
const { onSubmit, searchCriteria } = useUnifiedSearchContext();
@ -41,6 +49,23 @@ export const AlertsTabContent = () => {
<EuiFlexItem grow={false}>
<AlertsStatusFilter onChange={setAlertStatus} status={alertStatus} />
</EuiFlexItem>
<EuiFlexGroup alignItems="center" justifyContent="flexEnd" responsive={false}>
{featureFlags.inventoryThresholdAlertRuleEnabled && (
<EuiFlexItem grow={false}>
<CreateAlertRuleButton
onClick={toggleAlertFlyout}
data-test-subj="infraHostAlertsTabCreateAlertsRuleButton"
/>
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<LinkToAlertsPage
dateRange={searchCriteria.dateRange}
data-test-subj="infraHostAlertsTabAlertsShowAllButton"
kuery={`${ALERT_RULE_PRODUCER}: ${INFRA_ALERT_FEATURE_ID}`}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexGroup>
<EuiFlexItem>
<MemoAlertSummaryWidget
@ -63,6 +88,13 @@ export const AlertsTabContent = () => {
</EuiFlexItem>
)}
</EuiFlexGroup>
{featureFlags.inventoryThresholdAlertRuleEnabled && (
<AlertFlyout
nodeType="host"
setVisible={toggleAlertFlyout}
visible={isAlertFlyoutVisible}
/>
)}
</HeightRetainer>
);
};

View file

@ -21271,7 +21271,6 @@
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last7Days": "7 derniers jours",
"xpack.infra.assetDetails.datePicker.tooltip.autoRefresh": "Les données chargent continuellement ?",
"xpack.infra.assetDetails.datePicker.tooltip.autoRefresh.troubleshoot": "Essayez d'augmenter l'intervalle d'actualisation, de raccourcir la plage de dates ou de désactiver l'actualisation automatique.",
"xpack.infra.assetDetails.flyout.AlertsPageLinkLabel": "Afficher tout",
"xpack.infra.assetDetails.header.return": "Renvoyer",
"xpack.infra.assetDetails.metadata.tooltip.documentationLink": "host.name",
"xpack.infra.assetDetails.metadata.tooltip.metadata": "Métadonnées",
@ -21412,7 +21411,6 @@
"xpack.infra.hostsViewPage.tabs.metricsCharts.title": "Indicateurs",
"xpack.infra.hostsViewPage.tooltip.whatAreTheseMetricsLink": "Que sont ces indicateurs ?",
"xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "Pourquoi des lignes pointillées apparaissent-elles ?",
"xpack.infra.infra.assetDetails.alerts.createAlertLink": "Créer une règle",
"xpack.infra.infra.nodeDetails.openAsPage": "Ouvrir en tant que page",
"xpack.infra.inventory.alerting.groupActionVariableDescription": "Nom des données de reporting du groupe",
"xpack.infra.inventoryId.host.ipCodeLabel": "host.ip",

View file

@ -21234,7 +21234,6 @@
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last7Days": "過去 7 日間",
"xpack.infra.assetDetails.datePicker.tooltip.autoRefresh": "データの読み込みが継続的に行われていますか?",
"xpack.infra.assetDetails.datePicker.tooltip.autoRefresh.troubleshoot": "更新間隔を長くするか、日付範囲を短くするか、自動更新をオフにしてみてください。",
"xpack.infra.assetDetails.flyout.AlertsPageLinkLabel": "すべて表示",
"xpack.infra.assetDetails.header.return": "戻る",
"xpack.infra.assetDetails.metadata.tooltip.documentationLink": "host.name",
"xpack.infra.assetDetails.metadata.tooltip.metadata": "メタデータ",
@ -21386,7 +21385,6 @@
"xpack.infra.hostsViewPage.tabs.metricsCharts.title": "メトリック",
"xpack.infra.hostsViewPage.tooltip.whatAreTheseMetricsLink": "これらのメトリックは何か。",
"xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "点線が表示されている理由",
"xpack.infra.infra.assetDetails.alerts.createAlertLink": "ルールを作成",
"xpack.infra.infra.nodeDetails.openAsPage": "ページとして開く",
"xpack.infra.inventory.alerting.groupActionVariableDescription": "データを報告するグループの名前",
"xpack.infra.inventoryId.host.ipCodeLabel": "host.ip",

View file

@ -21277,7 +21277,6 @@
"xpack.infra.assetDetails.datePicker.commonlyUsedRanges.last7Days": "过去 7 天",
"xpack.infra.assetDetails.datePicker.tooltip.autoRefresh": "持续出现加载数据问题?",
"xpack.infra.assetDetails.datePicker.tooltip.autoRefresh.troubleshoot": "请尝试增加刷新时间间隔,缩短日期范围或关闭自动刷新。",
"xpack.infra.assetDetails.flyout.AlertsPageLinkLabel": "全部显示",
"xpack.infra.assetDetails.header.return": "返回",
"xpack.infra.assetDetails.metadata.tooltip.documentationLink": "host.name",
"xpack.infra.assetDetails.metadata.tooltip.metadata": "元数据",
@ -21418,7 +21417,6 @@
"xpack.infra.hostsViewPage.tabs.metricsCharts.title": "指标",
"xpack.infra.hostsViewPage.tooltip.whatAreTheseMetricsLink": "这些指标是什么?",
"xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "为什么我看到的是虚线?",
"xpack.infra.infra.assetDetails.alerts.createAlertLink": "创建规则",
"xpack.infra.infra.nodeDetails.openAsPage": "以页面形式打开",
"xpack.infra.inventory.alerting.groupActionVariableDescription": "报告数据的组名称",
"xpack.infra.inventoryId.host.ipCodeLabel": "host.ip",