mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Observability rules] Add baseline alert detail pages (#180256)
Closes https://github.com/elastic/kibana/issues/179338
Added baseline alert details page for:
- Inventory rule
- Metric threshold rule
- Error count threshold rule
- Failed transaction rate threshold rule
- APM Anomaly rule
- Elasticsearch query rule
- Anomaly detection rule
Example of Elasticsearch query alert details page:
<img width="1483" alt="Screenshot 2024-04-08 at 23 35 16"
src="acd1c9ba
-9711-4bfe-97e3-71aa56a2c57c">
This commit is contained in:
parent
3670d5eafc
commit
382be7ea3f
16 changed files with 181 additions and 159 deletions
|
@ -6,7 +6,14 @@
|
|||
*/
|
||||
|
||||
import React, { memo, useEffect, useMemo, useState } from 'react';
|
||||
import { EuiInMemoryTable } from '@elastic/eui';
|
||||
import {
|
||||
EuiTitle,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
EuiLink,
|
||||
EuiHorizontalRule,
|
||||
EuiInMemoryTable,
|
||||
} from '@elastic/eui';
|
||||
import {
|
||||
ALERT_CASE_IDS,
|
||||
ALERT_DURATION,
|
||||
|
@ -24,27 +31,38 @@ import { useUiSetting } from '@kbn/kibana-react-plugin/public';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util';
|
||||
|
||||
import { paths } from '../../../../common/locators/paths';
|
||||
import { TimeRange } from '../../../../common/custom_threshold_rule/types';
|
||||
import { TopAlert } from '../../../typings/alerts';
|
||||
import { useFetchBulkCases } from '../../../hooks/use_fetch_bulk_cases';
|
||||
import { useCaseViewNavigation } from '../../../hooks/use_case_view_navigation';
|
||||
import { useKibana } from '../../../utils/kibana_react';
|
||||
import { get } from 'lodash';
|
||||
import { paths } from '../../../common/locators/paths';
|
||||
import { TimeRange } from '../../../common/custom_threshold_rule/types';
|
||||
import { TopAlert } from '../../typings/alerts';
|
||||
import { useFetchBulkCases } from '../../hooks/use_fetch_bulk_cases';
|
||||
import { useCaseViewNavigation } from '../../hooks/use_case_view_navigation';
|
||||
import { useKibana } from '../../utils/kibana_react';
|
||||
import {
|
||||
FlyoutThresholdData,
|
||||
mapRuleParamsWithFlyout,
|
||||
} from './helpers/map_rules_params_with_flyout';
|
||||
import { ColumnIDs, overviewColumns } from './overview_columns';
|
||||
import { getSources } from './helpers/get_sources';
|
||||
import { RULE_DETAILS_PAGE_ID } from '../../pages/rule_details/constants';
|
||||
|
||||
export const Overview = memo(({ alert }: { alert: TopAlert }) => {
|
||||
const { http } = useKibana().services;
|
||||
export const AlertOverview = memo(({ alert, pageId }: { alert: TopAlert; pageId?: string }) => {
|
||||
const {
|
||||
http: {
|
||||
basePath: { prepend },
|
||||
},
|
||||
} = useKibana().services;
|
||||
const { cases, isLoading } = useFetchBulkCases({ ids: alert.fields[ALERT_CASE_IDS] || [] });
|
||||
const dateFormat = useUiSetting<string>('dateFormat');
|
||||
const [timeRange, setTimeRange] = useState<TimeRange>({ from: 'now-15m', to: 'now' });
|
||||
const [ruleCriteria, setRuleCriteria] = useState<FlyoutThresholdData[] | undefined>([]);
|
||||
const alertStart = alert.fields[ALERT_START];
|
||||
const alertEnd = alert.fields[ALERT_END];
|
||||
const ruleId = get(alert.fields, ALERT_RULE_UUID) ?? null;
|
||||
const linkToRule =
|
||||
pageId !== RULE_DETAILS_PAGE_ID && ruleId
|
||||
? prepend(paths.observability.ruleDetails(ruleId))
|
||||
: null;
|
||||
|
||||
useEffect(() => {
|
||||
const mappedRuleParams = mapRuleParamsWithFlyout(alert);
|
||||
|
@ -126,7 +144,7 @@ export const Overview = memo(({ alert }: { alert: TopAlert }) => {
|
|||
meta: {
|
||||
ruleLink:
|
||||
alert.fields[ALERT_RULE_UUID] &&
|
||||
http.basePath.prepend(paths.observability.ruleDetails(alert.fields[ALERT_RULE_UUID])),
|
||||
prepend(paths.observability.ruleDetails(alert.fields[ALERT_RULE_UUID])),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -154,11 +172,42 @@ export const Overview = memo(({ alert }: { alert: TopAlert }) => {
|
|||
alertEnd,
|
||||
cases,
|
||||
dateFormat,
|
||||
http.basePath,
|
||||
prepend,
|
||||
isLoading,
|
||||
navigateToCaseView,
|
||||
ruleCriteria,
|
||||
timeRange,
|
||||
]);
|
||||
return <EuiInMemoryTable width={'80%'} columns={overviewColumns} itemId="key" items={items} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiTitle size="xs">
|
||||
<h4>
|
||||
{i18n.translate('xpack.observability.alertsFlyout.reasonTitle', {
|
||||
defaultMessage: 'Reason',
|
||||
})}
|
||||
</h4>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size="s">{alert.reason}</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
{!!linkToRule && (
|
||||
<EuiLink href={linkToRule} data-test-subj="viewRuleDetailsFlyout">
|
||||
{i18n.translate('xpack.observability.alertsFlyout.viewRulesDetailsLinkText', {
|
||||
defaultMessage: 'View rule details',
|
||||
})}
|
||||
</EuiLink>
|
||||
)}
|
||||
<EuiHorizontalRule size="full" />
|
||||
<EuiTitle size="xs">
|
||||
<h4>
|
||||
{i18n.translate('xpack.observability.alertsFlyout.documentSummaryTitle', {
|
||||
defaultMessage: 'Document Summary',
|
||||
})}
|
||||
</h4>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiInMemoryTable width={'80%'} columns={overviewColumns} itemId="key" items={items} />
|
||||
</>
|
||||
);
|
||||
});
|
|
@ -9,8 +9,8 @@ import { ALERT_GROUP_FIELD, ALERT_GROUP_VALUE } from '@kbn/rule-data-utils';
|
|||
import {
|
||||
apmSources,
|
||||
infraSources,
|
||||
} from '../../../../../common/custom_threshold_rule/helpers/get_alert_source_links';
|
||||
import { TopAlert } from '../../../..';
|
||||
} from '../../../../common/custom_threshold_rule/helpers/get_alert_source_links';
|
||||
import { TopAlert } from '../../..';
|
||||
|
||||
interface AlertFields {
|
||||
[key: string]: any;
|
||||
|
@ -36,7 +36,7 @@ export const getSources = (alert: TopAlert) => {
|
|||
const fieldValue = alertFields[field];
|
||||
matchedSources.push({
|
||||
field: source,
|
||||
value: fieldValue[0],
|
||||
value: Array.isArray(fieldValue) ? fieldValue[0] : fieldValue,
|
||||
});
|
||||
}
|
||||
});
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { TopAlert } from '../../../../typings/alerts';
|
||||
import { TopAlert } from '../../../typings/alerts';
|
||||
import { mapRuleParamsWithFlyout } from './map_rules_params_with_flyout';
|
||||
|
||||
describe('Map rules params with flyout', () => {
|
||||
|
@ -149,7 +149,7 @@ describe('Map rules params with flyout', () => {
|
|||
observedValue: [4577],
|
||||
threshold: [100],
|
||||
comparator: 'more than',
|
||||
pctAboveThreshold: ' (4477.00% above the threshold)',
|
||||
pctAboveThreshold: ' (4477% above the threshold)',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -179,7 +179,7 @@ describe('Map rules params with flyout', () => {
|
|||
observedValue: '6%',
|
||||
threshold: '1%',
|
||||
comparator: '>',
|
||||
pctAboveThreshold: ' (500.00% above the threshold)',
|
||||
pctAboveThreshold: ' (500% above the threshold)',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -242,7 +242,7 @@ describe('Map rules params with flyout', () => {
|
|||
observedValue: [1],
|
||||
threshold: [1],
|
||||
comparator: '>',
|
||||
pctAboveThreshold: ' (0.00% above the threshold)',
|
||||
pctAboveThreshold: ' (0% above the threshold)',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -267,7 +267,7 @@ describe('Map rules params with flyout', () => {
|
|||
observedValue: ['23 s'],
|
||||
threshold: ['1.5 s'],
|
||||
comparator: '>',
|
||||
pctAboveThreshold: ' (1424.80% above the threshold)',
|
||||
pctAboveThreshold: ' (1424.8% above the threshold)',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -291,7 +291,7 @@ describe('Map rules params with flyout', () => {
|
|||
observedValue: ['25%'],
|
||||
threshold: ['1.0%'],
|
||||
comparator: '>',
|
||||
pctAboveThreshold: ' (2400.00% above the threshold)',
|
||||
pctAboveThreshold: ' (2400% above the threshold)',
|
||||
},
|
||||
],
|
||||
},
|
|
@ -19,16 +19,16 @@ import {
|
|||
} from '@kbn/rule-data-utils';
|
||||
import { EsQueryRuleParams } from '@kbn/stack-alerts-plugin/public/rule_types/es_query/types';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { asDuration, asPercent } from '../../../../../common';
|
||||
import { createFormatter } from '../../../../../common/custom_threshold_rule/formatters';
|
||||
import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter';
|
||||
import { METRIC_FORMATTERS } from '../../../../../common/custom_threshold_rule/formatters/snapshot_metric_formats';
|
||||
import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../../pages/alert_details/alert_details';
|
||||
import { asDuration, asPercent } from '../../../../common';
|
||||
import { createFormatter } from '../../../../common/custom_threshold_rule/formatters';
|
||||
import { metricValueFormatter } from '../../../../common/custom_threshold_rule/metric_value_formatter';
|
||||
import { METRIC_FORMATTERS } from '../../../../common/custom_threshold_rule/formatters/snapshot_metric_formats';
|
||||
import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../pages/alert_details/alert_details';
|
||||
import {
|
||||
BaseMetricExpressionParams,
|
||||
CustomMetricExpressionParams,
|
||||
} from '../../../../../common/custom_threshold_rule/types';
|
||||
import { TopAlert } from '../../../../typings/alerts';
|
||||
} from '../../../../common/custom_threshold_rule/types';
|
||||
import { TopAlert } from '../../../typings/alerts';
|
||||
import { isFieldsSameType } from './is_fields_same_type';
|
||||
export interface FlyoutThresholdData {
|
||||
observedValue: string;
|
||||
|
@ -42,7 +42,7 @@ const getPctAboveThreshold = (observedValue?: number, threshold?: number[]): str
|
|||
return i18n.translate('xpack.observability.alertFlyout.overview.aboveThresholdLabel', {
|
||||
defaultMessage: ' ({pctValue}% above the threshold)',
|
||||
values: {
|
||||
pctValue: (((observedValue - threshold[0]) * 100) / threshold[0]).toFixed(2),
|
||||
pctValue: parseFloat((((observedValue - threshold[0]) * 100) / threshold[0]).toFixed(2)),
|
||||
},
|
||||
});
|
||||
};
|
|
@ -13,9 +13,9 @@ import { AlertStatus } from '@kbn/rule-data-utils';
|
|||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { Tooltip as CaseTooltip } from '@kbn/cases-components';
|
||||
import type { Group } from '../../../../common/custom_threshold_rule/types';
|
||||
import { NavigateToCaseView } from '../../../hooks/use_case_view_navigation';
|
||||
import { Groups } from '../../custom_threshold/components/alert_details_app_section/groups';
|
||||
import type { Group } from '../../../common/custom_threshold_rule/types';
|
||||
import { NavigateToCaseView } from '../../hooks/use_case_view_navigation';
|
||||
import { Groups } from '../custom_threshold/components/alert_details_app_section/groups';
|
||||
import { formatCase } from './helpers/format_cases';
|
||||
import { FlyoutThresholdData } from './helpers/map_rules_params_with_flyout';
|
||||
|
|
@ -5,27 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { get } from 'lodash';
|
||||
import {
|
||||
EuiHorizontalRule,
|
||||
EuiLink,
|
||||
EuiPanel,
|
||||
EuiSpacer,
|
||||
EuiTabbedContentTab,
|
||||
EuiText,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { ALERT_RULE_UUID } from '@kbn/rule-data-utils';
|
||||
import { EuiPanel, EuiTabbedContentTab } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { AlertFieldsTable, ScrollableFlyoutTabbedContent } from '@kbn/alerts-ui-shared';
|
||||
import { AlertsTableFlyoutBaseProps } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import { useKibana } from '../../utils/kibana_react';
|
||||
|
||||
import { paths } from '../../../common/locators/paths';
|
||||
|
||||
import { RULE_DETAILS_PAGE_ID } from '../../pages/rule_details/constants';
|
||||
import type { TopAlert } from '../../typings/alerts';
|
||||
import { Overview } from './alert_flyout_overview/alerts_flyout_overview';
|
||||
import { AlertOverview } from '../alert_overview/alert_overview';
|
||||
|
||||
interface FlyoutProps {
|
||||
rawAlert: AlertsTableFlyoutBaseProps['alert'];
|
||||
|
@ -36,18 +21,6 @@ interface FlyoutProps {
|
|||
type TabId = 'overview' | 'table';
|
||||
|
||||
export function AlertsFlyoutBody({ alert, rawAlert, id: pageId }: FlyoutProps) {
|
||||
const {
|
||||
http: {
|
||||
basePath: { prepend },
|
||||
},
|
||||
} = useKibana().services;
|
||||
|
||||
const ruleId = get(alert.fields, ALERT_RULE_UUID) ?? null;
|
||||
const linkToRule =
|
||||
pageId !== RULE_DETAILS_PAGE_ID && ruleId && prepend
|
||||
? prepend(paths.observability.ruleDetails(ruleId))
|
||||
: null;
|
||||
|
||||
const overviewTab = useMemo(() => {
|
||||
return {
|
||||
id: 'overview',
|
||||
|
@ -57,37 +30,11 @@ export function AlertsFlyoutBody({ alert, rawAlert, id: pageId }: FlyoutProps) {
|
|||
}),
|
||||
content: (
|
||||
<EuiPanel hasShadow={false} data-test-subj="overviewTabPanel">
|
||||
<EuiTitle size="xs">
|
||||
<h4>
|
||||
{i18n.translate('xpack.observability.alertsFlyout.reasonTitle', {
|
||||
defaultMessage: 'Reason',
|
||||
})}
|
||||
</h4>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size="s">{alert.reason}</EuiText>
|
||||
<EuiSpacer size="s" />
|
||||
{!!linkToRule && (
|
||||
<EuiLink href={linkToRule} data-test-subj="viewRuleDetailsFlyout">
|
||||
{i18n.translate('xpack.observability.alertsFlyout.viewRulesDetailsLinkText', {
|
||||
defaultMessage: 'View rule details',
|
||||
})}
|
||||
</EuiLink>
|
||||
)}
|
||||
<EuiHorizontalRule size="full" />
|
||||
<EuiTitle size="xs">
|
||||
<h4>
|
||||
{i18n.translate('xpack.observability.alertsFlyout.documentSummaryTitle', {
|
||||
defaultMessage: 'Document Summary',
|
||||
})}
|
||||
</h4>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="m" />
|
||||
<Overview alert={alert} />
|
||||
<AlertOverview alert={alert} pageId={pageId} />
|
||||
</EuiPanel>
|
||||
),
|
||||
};
|
||||
}, [alert, linkToRule]);
|
||||
}, [alert, pageId]);
|
||||
|
||||
const metadataTab = useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -9,8 +9,6 @@ import React, { useState, useEffect } from 'react';
|
|||
import { EuiFlyoutFooter, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibana } from '../../utils/kibana_react';
|
||||
import { usePluginContext } from '../../hooks/use_plugin_context';
|
||||
import { isAlertDetailsEnabledPerApp } from '../../utils/is_alert_details_enabled';
|
||||
import { paths } from '../../../common/locators/paths';
|
||||
import type { TopAlert } from '../../typings/alerts';
|
||||
|
||||
|
@ -25,7 +23,6 @@ export function AlertsFlyoutFooter({ alert, isInApp }: FlyoutProps & { isInApp:
|
|||
basePath: { prepend },
|
||||
},
|
||||
} = useKibana().services;
|
||||
const { config } = usePluginContext();
|
||||
const [viewInAppUrl, setViewInAppUrl] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -48,23 +45,20 @@ export function AlertsFlyoutFooter({ alert, isInApp }: FlyoutProps & { isInApp:
|
|||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
|
||||
{!isAlertDetailsEnabledPerApp(alert, config) ? null : (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
data-test-subj="alertsFlyoutAlertDetailsButton"
|
||||
fill
|
||||
href={
|
||||
prepend &&
|
||||
prepend(paths.observability.alertDetails(alert.fields['kibana.alert.uuid']))
|
||||
}
|
||||
>
|
||||
{i18n.translate('xpack.observability.alertsFlyout.alertsDetailsButtonText', {
|
||||
defaultMessage: 'Alert details',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
data-test-subj="alertsFlyoutAlertDetailsButton"
|
||||
fill
|
||||
href={
|
||||
prepend &&
|
||||
prepend(paths.observability.alertDetails(alert.fields['kibana.alert.uuid']))
|
||||
}
|
||||
>
|
||||
{i18n.translate('xpack.observability.alertsFlyout.alertsDetailsButtonText', {
|
||||
defaultMessage: 'Alert details',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
);
|
||||
|
|
|
@ -113,7 +113,7 @@ export const getRenderCellValue = ({
|
|||
return (
|
||||
<EuiLink
|
||||
data-test-subj="o11yGetRenderCellValueLink"
|
||||
css={{ display: 'contents' }}
|
||||
css={{ ':hover': { textDecoration: 'none' } }}
|
||||
onClick={() => setFlyoutAlert && setFlyoutAlert(alert.fields[ALERT_UUID])}
|
||||
>
|
||||
{alert.reason}
|
||||
|
|
|
@ -16,6 +16,7 @@ import { waitFor } from '@testing-library/react';
|
|||
import { Chance } from 'chance';
|
||||
import React, { Fragment } from 'react';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { from } from 'rxjs';
|
||||
import { useFetchAlertDetail } from '../../hooks/use_fetch_alert_detail';
|
||||
import { ConfigSchema } from '../../plugin';
|
||||
import { Subset } from '../../typings';
|
||||
|
@ -54,6 +55,7 @@ const mockKibana = () => {
|
|||
services: {
|
||||
...kibanaStartMock.startContract(),
|
||||
cases: casesPluginMock.createStartContract(),
|
||||
application: { currentAppId$: from('mockedApp') },
|
||||
http: {
|
||||
basePath: {
|
||||
prepend: jest.fn(),
|
||||
|
|
|
@ -38,12 +38,12 @@ import { PageTitle, pageTitleContent } from './components/page_title';
|
|||
import { HeaderActions } from './components/header_actions';
|
||||
import { AlertSummary, AlertSummaryField } from './components/alert_summary';
|
||||
import { CenterJustifiedSpinner } from '../../components/center_justified_spinner';
|
||||
import PageNotFound from '../404';
|
||||
import { getTimeZone } from '../../utils/get_time_zone';
|
||||
import { isAlertDetailsEnabledPerApp } from '../../utils/is_alert_details_enabled';
|
||||
import { observabilityFeatureId } from '../../../common';
|
||||
import { paths } from '../../../common/locators/paths';
|
||||
import { HeaderMenu } from '../overview/components/header_menu/header_menu';
|
||||
import { AlertOverview } from '../../components/alert_overview/alert_overview';
|
||||
import { AlertDetailContextualInsights } from './alert_details_contextual_insights';
|
||||
|
||||
interface AlertDetailsPathParams {
|
||||
|
@ -133,11 +133,6 @@ export function AlertDetails() {
|
|||
return <CenterJustifiedSpinner />;
|
||||
}
|
||||
|
||||
// Redirect to the 404 page when the user hit the page url directly in the browser while the feature flag is off.
|
||||
if (alertDetail && !isAlertDetailsEnabledPerApp(alertDetail.formatted, config)) {
|
||||
return <PageNotFound />;
|
||||
}
|
||||
|
||||
if (!isLoading && !alertDetail)
|
||||
return (
|
||||
<EuiPanel data-test-subj="alertDetailsError">
|
||||
|
@ -167,27 +162,43 @@ export function AlertDetails() {
|
|||
const OVERVIEW_TAB_ID = 'overview';
|
||||
const METADATA_TAB_ID = 'metadata';
|
||||
|
||||
const overviewTab = (
|
||||
<>
|
||||
<EuiSpacer size="l" />
|
||||
<AlertSummary alertSummaryFields={summaryFields} />
|
||||
|
||||
<AlertDetailContextualInsights alert={alertDetail} />
|
||||
<EuiSpacer size="l" />
|
||||
{AlertDetailsAppSection && rule && alertDetail?.formatted && (
|
||||
<AlertDetailsAppSection
|
||||
alert={alertDetail.formatted}
|
||||
rule={rule}
|
||||
timeZone={timeZone}
|
||||
setAlertSummaryFields={setSummaryFields}
|
||||
ruleLink={http.basePath.prepend(paths.observability.ruleDetails(rule.id))}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
const overviewTab = alertDetail ? (
|
||||
AlertDetailsAppSection &&
|
||||
/*
|
||||
when feature flag is enabled, show alert details page with customized overview tab,
|
||||
otherwise show default overview tab
|
||||
*/
|
||||
isAlertDetailsEnabledPerApp(alertDetail.formatted, config) ? (
|
||||
<>
|
||||
<EuiSpacer size="l" />
|
||||
<AlertSummary alertSummaryFields={summaryFields} />
|
||||
<AlertDetailContextualInsights alert={alertDetail} />
|
||||
<EuiSpacer size="l" />
|
||||
{rule && alertDetail.formatted && (
|
||||
<AlertDetailsAppSection
|
||||
alert={alertDetail.formatted}
|
||||
rule={rule}
|
||||
timeZone={timeZone}
|
||||
setAlertSummaryFields={setSummaryFields}
|
||||
ruleLink={http.basePath.prepend(paths.observability.ruleDetails(rule.id))}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<EuiPanel hasShadow={false} data-test-subj="overviewTabPanel" paddingSize="none">
|
||||
<EuiSpacer size="l" />
|
||||
<AlertDetailContextualInsights alert={alertDetail} />
|
||||
<EuiSpacer size="l" />
|
||||
<AlertOverview alert={alertDetail.formatted} />
|
||||
</EuiPanel>
|
||||
)
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
|
||||
const metadataTab = alertDetail?.raw && (
|
||||
<EuiPanel hasShadow={false} data-test-subj="metadataTabPanel">
|
||||
<EuiPanel hasShadow={false} data-test-subj="metadataTabPanel" paddingSize="none">
|
||||
<EuiSpacer size="l" />
|
||||
<AlertFieldsTable alert={alertDetail.raw} />
|
||||
</EuiPanel>
|
||||
);
|
||||
|
|
|
@ -147,8 +147,6 @@ describe('ObservabilityActions component', () => {
|
|||
wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(0);
|
||||
wrapper.update();
|
||||
expect(wrapper.find('[data-test-subj~="viewAlertDetailsFlyout"]').exists()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -157,7 +155,15 @@ describe('ObservabilityActions component', () => {
|
|||
wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(1);
|
||||
expect(wrapper.find('[data-test-subj~="viewAlertDetailsFlyout"]').hostNodes().length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('"View alert details" menu item should open alert details page', async () => {
|
||||
const wrapper = await setup('nothing');
|
||||
wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(wrapper.find('[data-test-subj~="viewAlertDetailsPage"]').hostNodes().length).toBe(1);
|
||||
expect(wrapper.find('[data-test-subj~="viewAlertDetailsFlyout"]').exists()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ import { useRouteMatch } from 'react-router-dom';
|
|||
import { SLO_ALERTS_TABLE_ID } from '@kbn/observability-shared-plugin/common';
|
||||
import { RULE_DETAILS_PAGE_ID } from '../../rule_details/constants';
|
||||
import { paths, SLO_DETAIL_PATH } from '../../../../common/locators/paths';
|
||||
import { isAlertDetailsEnabledPerApp } from '../../../utils/is_alert_details_enabled';
|
||||
import { useKibana } from '../../../utils/kibana_react';
|
||||
import { parseAlert } from '../helpers/parse_alert';
|
||||
import { observabilityFeatureId, ObservabilityRuleTypeRegistry } from '../../..';
|
||||
|
@ -147,7 +146,7 @@ export function AlertActions({
|
|||
triggersActionsUi.getAlertsTableDefaultAlertActions({
|
||||
key: 'defaultRowActions',
|
||||
onActionExecuted: closeActionsPopover,
|
||||
isAlertDetailsEnabled: isAlertDetailsEnabledPerApp(observabilityAlert, config),
|
||||
isAlertDetailsEnabled: true,
|
||||
resolveRulePagePath: (ruleId, currentPageId) =>
|
||||
currentPageId !== RULE_DETAILS_PAGE_ID ? paths.observability.ruleDetails(ruleId) : null,
|
||||
resolveAlertPagePath: (alertId, currentPageId) =>
|
||||
|
@ -156,7 +155,7 @@ export function AlertActions({
|
|||
: null,
|
||||
...customActionsProps,
|
||||
}),
|
||||
[config, customActionsProps, observabilityAlert, triggersActionsUi]
|
||||
[customActionsProps, triggersActionsUi]
|
||||
);
|
||||
|
||||
const actionsMenuItems = [
|
||||
|
|
|
@ -159,9 +159,16 @@ export function ObservabilityAlertsCommonProvider({
|
|||
};
|
||||
|
||||
// Flyout
|
||||
const getReasonMessageLinkByIndex = async (index: number) => {
|
||||
const reasonMessageLinks = await find.allByCssSelector(
|
||||
'[data-test-subj="o11yGetRenderCellValueLink"]'
|
||||
);
|
||||
return reasonMessageLinks[index] || null;
|
||||
};
|
||||
|
||||
const openAlertsFlyout = retryOnStale.wrap(async (index: number = 0) => {
|
||||
await openActionsMenuForRow(index);
|
||||
await testSubjects.click('viewAlertDetailsFlyout');
|
||||
const reasonMessageLink = await getReasonMessageLinkByIndex(index);
|
||||
await reasonMessageLink.click();
|
||||
await retry.waitFor(
|
||||
'flyout open',
|
||||
async () => await testSubjects.exists(ALERTS_FLYOUT_SELECTOR, { timeout: 2500 })
|
||||
|
|
|
@ -13,7 +13,7 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
const observability = getService('observability');
|
||||
const retry = getService('retry');
|
||||
|
||||
describe('Observability Alert Details page - Feature flag', function () {
|
||||
describe('Observability Alert Details page', function () {
|
||||
this.tags('includeFirefox');
|
||||
|
||||
before(async () => {
|
||||
|
@ -27,29 +27,36 @@ export default ({ getService }: FtrProviderContext) => {
|
|||
await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs');
|
||||
});
|
||||
|
||||
it('should show 404 page when the feature flag is disabled but the alert exists', async () => {
|
||||
await observability.alerts.common.navigateToAlertDetails(
|
||||
'4c87bd11-ff31-4a05-8a04-833e2da94858'
|
||||
);
|
||||
it('should show error when the alert does not exist', async () => {
|
||||
await observability.alerts.common.navigateToAlertDetails('deleted-alert-Id');
|
||||
await retry.waitFor(
|
||||
'The 404 - Not found page to be visible',
|
||||
async () => await testSubjects.exists('pageNotFound')
|
||||
'Error message to be visible',
|
||||
async () => await testSubjects.exists('alertDetailsError')
|
||||
);
|
||||
});
|
||||
// This test is will be removed after removing the feature flag.
|
||||
// FLAKY for the same reason: https://github.com/elastic/kibana/issues/133799
|
||||
describe.skip('Alert Detail / Alert Flyout', () => {
|
||||
|
||||
describe('Alert components', () => {
|
||||
before(async () => {
|
||||
await observability.alerts.common.navigateToTimeWithData();
|
||||
});
|
||||
it('should open the flyout instead of the alerts details page when clicking on "View alert details" from the... (3 dots) button when the feature flag is disabled', async () => {
|
||||
await observability.alerts.common.openAlertsFlyout();
|
||||
await observability.alerts.common.getAlertsFlyoutOrFail();
|
||||
|
||||
it('should show tabbed view', async () => {
|
||||
await observability.alerts.common.navigateToAlertDetails(
|
||||
'4c87bd11-ff31-4a05-8a04-833e2da94858'
|
||||
);
|
||||
|
||||
await retry.waitFor(
|
||||
'Overview tab to be visible',
|
||||
async () => await testSubjects.exists('overviewTab')
|
||||
);
|
||||
|
||||
await retry.waitFor(
|
||||
'Metadata tab to be visible',
|
||||
async () => await testSubjects.exists('metadataTab')
|
||||
);
|
||||
});
|
||||
/* TODO: Add more test cases regarding the feature flag for:
|
||||
- alert details URL from the Action variable
|
||||
- alert details button from the alert flyout.
|
||||
*/
|
||||
|
||||
/* TODO: Add more test cases */
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue