From 6cf5c4c934669621eaa8cc51b7b51585e32b0f6d Mon Sep 17 00:00:00 2001 From: Maryam Saeidi Date: Tue, 22 Oct 2024 09:22:29 +0200 Subject: [PATCH] [Alert details page] Use alert rule params instead of rule params in the custom threshold app section (#197023) Related to #181828 ## Summary This PR refactors the custom threshold app section to rely on the alert rule params instead of rule params. ### How to test - Create a custom threshold rule and verify that the alert details page works as before - Also, check the log rate analysis component and ensure it works as before (cherry picked from commit c0393ae6588a1f024f16bcddd52be0bf9da45f7d) --- .../alert_details_app_section.test.tsx | 7 +- .../alert_details_app_section.tsx | 19 +++-- .../helpers/log_rate_analysis_query.test.ts | 84 +++++++++++-------- .../helpers/log_rate_analysis_query.ts | 18 ++-- .../log_rate_analysis.tsx | 27 +++--- .../mocks/custom_threshold_rule.ts | 12 ++- .../components/custom_threshold/types.ts | 7 +- 7 files changed, 94 insertions(+), 80 deletions(-) diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx index f45a353be9a61..87aee8649300a 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { coreMock as mockCoreMock } from '@kbn/core/public/mocks'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; import { render } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; @@ -85,8 +86,10 @@ describe('AlertDetailsAppSection', () => { diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index b474f246988b6..cc2f8364ce5b6 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -20,9 +20,14 @@ import { useEuiTheme, transparentize, } from '@elastic/eui'; -import { RuleTypeParams } from '@kbn/alerting-plugin/common'; import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util'; -import { ALERT_END, ALERT_START, ALERT_EVALUATION_VALUES, ALERT_GROUP } from '@kbn/rule-data-utils'; +import { + ALERT_END, + ALERT_START, + ALERT_EVALUATION_VALUES, + ALERT_GROUP, + ALERT_RULE_PARAMETERS, +} from '@kbn/rule-data-utils'; import { DataView } from '@kbn/data-views-plugin/common'; import type { EventAnnotationConfig, @@ -36,9 +41,8 @@ import { getGroupFilters } from '../../../../../common/custom_threshold_rule/hel import { useLicense } from '../../../../hooks/use_license'; import { useKibana } from '../../../../utils/kibana_react'; import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter'; -import { AlertParams } from '../../types'; import { Threshold } from '../threshold'; -import { CustomThresholdRule, CustomThresholdAlert } from '../types'; +import { CustomThresholdAlert } from '../types'; import { LogRateAnalysis } from './log_rate_analysis'; import { RuleConditionChart } from '../../../rule_condition_chart/rule_condition_chart'; import { getViewInAppUrl } from '../../../../../common/custom_threshold_rule/get_view_in_app_url'; @@ -47,11 +51,10 @@ import { generateChartTitleAndTooltip } from './helpers/generate_chart_title_and interface AppSectionProps { alert: CustomThresholdAlert; - rule: CustomThresholdRule; } // eslint-disable-next-line import/no-default-export -export default function AlertDetailsAppSection({ alert, rule }: AppSectionProps) { +export default function AlertDetailsAppSection({ alert }: AppSectionProps) { const services = useKibana().services; const { charts, @@ -66,10 +69,10 @@ export default function AlertDetailsAppSection({ alert, rule }: AppSectionProps) const [dataView, setDataView] = useState(); const [, setDataViewError] = useState(); const [timeRange, setTimeRange] = useState({ from: 'now-15m', to: 'now' }); - const ruleParams = rule.params as RuleTypeParams & AlertParams; const chartProps = { baseTheme: charts.theme.useChartsBaseTheme(), }; + const ruleParams = alert.fields[ALERT_RULE_PARAMETERS]; const alertStart = alert.fields[ALERT_START]; const alertEnd = alert.fields[ALERT_END]; const groups = alert.fields[ALERT_GROUP]; @@ -213,7 +216,7 @@ export default function AlertDetailsAppSection({ alert, rule }: AppSectionProps) ); })} {hasLogRateAnalysisLicense && ( - + )} ); diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts index 05a8c3eb67a35..996030cf890cf 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts @@ -6,6 +6,7 @@ */ import { COMPARATORS } from '@kbn/alerting-comparators'; +import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; import { Aggregators } from '../../../../../../common/custom_threshold_rule/types'; import { CustomThresholdRuleTypeParams } from '../../../types'; import { getLogRateAnalysisEQQuery } from './log_rate_analysis_query'; @@ -50,74 +51,83 @@ describe('buildEsQuery', () => { }; const testData: Array<{ title: string; - params: CustomThresholdRuleTypeParams; alert: any; }> = [ { title: 'rule with optional filer, count filter and group by', - params: mockedParams, alert: { fields: { 'kibana.alert.group': [mockedAlertWithMultipleGroups.fields['kibana.alert.group'][0]], + [ALERT_RULE_PARAMETERS]: mockedParams, }, }, }, { title: 'rule with optional filer, count filter and multiple group by', - params: mockedParams, - alert: mockedAlertWithMultipleGroups, + alert: { + fields: { + ...mockedAlertWithMultipleGroups.fields, + [ALERT_RULE_PARAMETERS]: mockedParams, + }, + }, }, { title: 'rule with optional filer, count filter and WITHOUT group by', - params: mockedParams, - alert: {}, + alert: { + fields: { + [ALERT_RULE_PARAMETERS]: mockedParams, + }, + }, }, { title: 'rule without filter and with group by', - params: { - groupBy: ['host.hostname'], - searchConfiguration: { - index, - query: { query: '', language: 'kuery' }, - }, - criteria: [ - { - metrics: [{ name: 'A', aggType: Aggregators.COUNT }], - timeSize: 1, - timeUnit: 'm', - threshold: [90], - comparator: COMPARATORS.GREATER_THAN, - }, - ], - }, alert: { fields: { 'kibana.alert.group': [mockedAlertWithMultipleGroups.fields['kibana.alert.group'][0]], + [ALERT_RULE_PARAMETERS]: { + groupBy: ['host.hostname'], + searchConfiguration: { + index, + query: { query: '', language: 'kuery' }, + }, + criteria: [ + { + metrics: [{ name: 'A', aggType: Aggregators.COUNT }], + timeSize: 1, + timeUnit: 'm', + threshold: [90], + comparator: COMPARATORS.GREATER_THAN, + }, + ], + }, }, }, }, { title: 'rule with multiple metrics', - params: { - ...mockedParams, - criteria: [ - { - metrics: [ - { name: 'A', aggType: Aggregators.COUNT, filter: 'host.name: host-1' }, - { name: 'B', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, + alert: { + fields: { + [ALERT_RULE_PARAMETERS]: { + ...mockedParams, + criteria: [ + { + metrics: [ + { name: 'A', aggType: Aggregators.COUNT, filter: 'host.name: host-1' }, + { name: 'B', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, + ], + timeSize: 1, + timeUnit: 'm', + threshold: [90], + comparator: COMPARATORS.GREATER_THAN, + }, ], - timeSize: 1, - timeUnit: 'm', - threshold: [90], - comparator: COMPARATORS.GREATER_THAN, }, - ], + }, }, - alert: {}, }, ]; - test.each(testData)('should generate correct es query for $title', ({ alert, params }) => { - expect(getLogRateAnalysisEQQuery(alert, params)).toMatchSnapshot(); + test.each(testData)('should generate correct es query for $title', ({ alert }) => { + expect(getLogRateAnalysisEQQuery(alert)).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.ts index 4bd0b16212e11..bea80bfb5ab5e 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.ts @@ -7,12 +7,12 @@ import { get } from 'lodash'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; +import { CustomThresholdAlert } from '../../types'; import { getGroupFilters } from '../../../../../../common/custom_threshold_rule/helpers/get_group'; import { Aggregators } from '../../../../../../common/custom_threshold_rule/types'; import { buildEsQuery } from '../../../../../utils/build_es_query'; import type { CustomThresholdExpressionMetric } from '../../../../../../common/custom_threshold_rule/types'; -import type { TopAlert } from '../../../../../typings/alerts'; -import type { CustomThresholdRuleTypeParams } from '../../../types'; import { Group } from '../../../../../../common/typings'; const getKuery = (metrics: CustomThresholdExpressionMetric[], filter?: string) => { @@ -32,23 +32,23 @@ const getKuery = (metrics: CustomThresholdExpressionMetric[], filter?: string) = }; export const getLogRateAnalysisEQQuery = ( - alert: TopAlert>, - params: CustomThresholdRuleTypeParams + alert: CustomThresholdAlert ): QueryDslQueryContainer | undefined => { + const ruleParams = alert.fields[ALERT_RULE_PARAMETERS]; // We only show log rate analysis for one condition with one count aggregation if ( - params.criteria.length !== 1 || - params.criteria[0].metrics.length !== 1 || - params.criteria[0].metrics[0].aggType !== Aggregators.COUNT + ruleParams.criteria.length !== 1 || + ruleParams.criteria[0].metrics.length !== 1 || + ruleParams.criteria[0].metrics[0].aggType !== Aggregators.COUNT ) { return; } const group = get(alert, 'fields["kibana.alert.group"]') as Group[] | undefined; - const optionalFilter = get(params.searchConfiguration, 'query.query') as string | undefined; + const optionalFilter = get(ruleParams.searchConfiguration, 'query.query') as string | undefined; const groupByFilters = getGroupFilters(group); const boolQuery = buildEsQuery({ - kuery: getKuery(params.criteria[0].metrics, optionalFilter), + kuery: getKuery(ruleParams.criteria[0].metrics, optionalFilter), filters: groupByFilters, }); diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/log_rate_analysis.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/log_rate_analysis.tsx index f2285b3529f65..89e8cc5e2aa6a 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/log_rate_analysis.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/log_rate_analysis.tsx @@ -19,17 +19,14 @@ import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { Message } from '@kbn/observability-ai-assistant-plugin/public'; -import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; -import { ALERT_END } from '@kbn/rule-data-utils'; -import { CustomThresholdRuleTypeParams } from '../../types'; -import { TopAlert } from '../../../..'; +import { ALERT_END, ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; +import { CustomThresholdAlert } from '../types'; import { Color, colorTransformer } from '../../../../../common/custom_threshold_rule/color_palette'; import { getLogRateAnalysisEQQuery } from './helpers/log_rate_analysis_query'; export interface AlertDetailsLogRateAnalysisProps { - alert: TopAlert>; + alert: CustomThresholdAlert; dataView: any; - rule: Rule; services: any; } @@ -40,12 +37,7 @@ interface SignificantFieldValue { pValue: number | null; } -export function LogRateAnalysis({ - alert, - dataView, - rule, - services, -}: AlertDetailsLogRateAnalysisProps) { +export function LogRateAnalysis({ alert, dataView, services }: AlertDetailsLogRateAnalysisProps) { const { observabilityAIAssistant: { ObservabilityAIAssistantContextualInsight, @@ -57,22 +49,23 @@ export function LogRateAnalysis({ | { logRateAnalysisType: LogRateAnalysisType; significantFieldValues: SignificantFieldValue[] } | undefined >(); + const ruleParams = alert.fields[ALERT_RULE_PARAMETERS]; useEffect(() => { - const esSearchRequest = getLogRateAnalysisEQQuery(alert, rule.params); + const esSearchRequest = getLogRateAnalysisEQQuery(alert); if (esSearchRequest) { setEsSearchQuery(esSearchRequest); } - }, [alert, rule.params]); + }, [alert]); const { timeRange, windowParameters } = useMemo(() => { const alertStartedAt = moment(alert.start).toISOString(); const alertEndedAt = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).toISOString() : undefined; - const timeSize = rule.params.criteria[0]?.timeSize as number | undefined; - const timeUnit = rule.params.criteria[0]?.timeUnit as + const timeSize = ruleParams.criteria[0]?.timeSize as number | undefined; + const timeUnit = ruleParams.criteria[0]?.timeUnit as | moment.unitOfTime.DurationConstructor | undefined; @@ -82,7 +75,7 @@ export function LogRateAnalysis({ timeSize, timeUnit, }); - }, [alert, rule]); + }, [alert.fields, alert.start, ruleParams.criteria]); const logRateAnalysisTitle = i18n.translate( 'xpack.observability.customThreshold.alertDetails.logRateAnalysisTitle', diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts index c1db31d991c28..36f108b1db628 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts @@ -227,19 +227,23 @@ export const buildCustomThresholdAlert = ( { name: 'B', aggType: Aggregators.MAX, - metric: 'system.cpu.user.pct', + field: 'system.cpu.user.pct', }, ], threshold: [4], timeSize: 15, timeUnit: 'm', - warningComparator: COMPARATORS.GREATER_THAN, - warningThreshold: [2.2], }, ], - sourceId: 'default', alertOnNoData: true, alertOnGroupDisappear: true, + searchConfiguration: { + query: { + query: '', + language: 'kuery', + }, + index: 'b3eadf0e-1053-41d0-9672-dc1d7789dd68', + }, }, 'kibana.alert.evaluation.values': [2500, 5], 'kibana.alert.group': [{ field: 'host.name', value: 'host-1' }], diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/types.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/types.ts index 8d5b1260a809c..891661b6bc82a 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/types.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/types.ts @@ -8,7 +8,7 @@ import * as rt from 'io-ts'; import { CasesPublicStart } from '@kbn/cases-plugin/public'; import { ChartsPluginStart } from '@kbn/charts-plugin/public'; -import { DataPublicPluginStart, SerializedSearchSourceFields } from '@kbn/data-plugin/public'; +import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { DataView, DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { DiscoverStart } from '@kbn/discover-plugin/public'; import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; @@ -16,7 +16,7 @@ import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { LensPublicStart } from '@kbn/lens-plugin/public'; import { ObservabilitySharedPluginStart } from '@kbn/observability-shared-plugin/public'; import { OsqueryPluginStart } from '@kbn/osquery-plugin/public'; -import { ALERT_GROUP } from '@kbn/rule-data-utils'; +import { ALERT_GROUP, ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; import { SharePluginStart } from '@kbn/share-plugin/public'; import { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import { @@ -87,11 +87,12 @@ export type RendererFunction = (args: Rende export interface CustomThresholdRuleTypeParams extends RuleTypeParams { criteria: CustomMetricExpressionParams[]; - searchConfiguration: SerializedSearchSourceFields; + searchConfiguration: CustomThresholdSearchSourceFields; groupBy?: string | string[]; } export interface CustomThresholdAlertFields { [ALERT_GROUP]?: Array<{ field: string; value: string }>; + [ALERT_RULE_PARAMETERS]: CustomThresholdRuleTypeParams; } export const expressionTimestampsRT = rt.type({