From 2913a4190618ed73a093930cbdd7dc26b7c825bf Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Thu, 25 Jan 2024 10:50:36 +0100 Subject: [PATCH 01/14] reuse lens chart, add annotations --- .../visualization_types/constants.ts | 1 + .../visualization_types/layers/index.ts | 5 +- .../layers/xy_annotation_layer.ts | 66 +++++++++++++++++++ packages/kbn-lens-embeddable-utils/index.ts | 1 + .../alert_details_app_section.tsx | 62 ++++++----------- .../painless_tinymath_parser.test.ts | 0 .../painless_tinymath_parser.ts | 0 .../rule_condition_chart.test.tsx} | 6 +- .../rule_condition_chart.tsx} | 61 +++++++++++++++-- .../custom_threshold_rule_expression.tsx | 5 +- 10 files changed, 151 insertions(+), 56 deletions(-) create mode 100644 packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_annotation_layer.ts rename x-pack/plugins/observability/public/components/custom_threshold/components/{preview_chart => rule_condition_chart}/painless_tinymath_parser.test.ts (100%) rename x-pack/plugins/observability/public/components/custom_threshold/components/{preview_chart => rule_condition_chart}/painless_tinymath_parser.ts (100%) rename x-pack/plugins/observability/public/components/custom_threshold/components/{preview_chart/preview_chart.test.tsx => rule_condition_chart/rule_condition_chart.test.tsx} (94%) rename x-pack/plugins/observability/public/components/custom_threshold/components/{preview_chart/preview_chart.tsx => rule_condition_chart/rule_condition_chart.tsx} (85%) diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/constants.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/constants.ts index f2d6a056c09db..0f71486ac65f2 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/constants.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/constants.ts @@ -11,4 +11,5 @@ export const METRIC_ID = 'lnsMetric'; export const METRIC_TREND_LINE_ID = 'metricTrendline'; export const XY_REFERENCE_LINE_ID = 'referenceLine'; +export const XY_ANNOTATIONS_ID = 'annotations'; export const XY_DATA_ID = 'data'; diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts index 2620d7639f5ff..c65baac8fab34 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts @@ -12,6 +12,9 @@ export { XYReferenceLinesLayer, type XYReferenceLinesLayerConfig, } from './xy_reference_lines_layer'; - +export { + XYByValueAnnotationsLayer, + type XYByValueAnnotationsLayerConfig, +} from './xy_annotation_layer'; export { FormulaColumn } from './columns/formula'; export { StaticColumn } from './columns/static'; diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_annotation_layer.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_annotation_layer.ts new file mode 100644 index 0000000000000..2407a3cb1da1b --- /dev/null +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_annotation_layer.ts @@ -0,0 +1,66 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { SavedObjectReference } from '@kbn/core/server'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import { PointInTimeEventAnnotationConfig } from '@kbn/event-annotation-common'; +import type { FormBasedPersistedState, PersistedIndexPatternLayer } from '@kbn/lens-plugin/public'; +import type { XYByValueAnnotationLayerConfig } from '@kbn/lens-plugin/public/visualizations/xy/types'; +import type { ChartLayer } from '../../types'; +import { getDefaultReferences } from '../../utils'; +import { XY_ANNOTATIONS_ID } from '../constants'; + +export interface XYByValueAnnotationsLayerConfig { + annotations: PointInTimeEventAnnotationConfig[]; + layerType?: typeof XY_ANNOTATIONS_ID; + /** + * It is possible to define a specific dataView for the layer. It will override the global chart one + **/ + dataView?: DataView; + ignoreGlobalFilters?: boolean; +} + +export class XYByValueAnnotationsLayer implements ChartLayer { + private layerConfig: XYByValueAnnotationsLayerConfig; + + constructor(layerConfig: XYByValueAnnotationsLayerConfig) { + this.layerConfig = { + ...layerConfig, + layerType: layerConfig.layerType ?? 'annotations', + }; + } + + getName(): string | undefined { + return this.layerConfig.annotations[0].label; + } + + getLayer(layerId: string): FormBasedPersistedState['layers'] { + const baseLayer = { columnOrder: [], columns: {} } as PersistedIndexPatternLayer; + return { + [`${layerId}_annotation`]: baseLayer, + }; + } + + getReference(layerId: string, chartDataView: DataView): SavedObjectReference[] { + return getDefaultReferences(this.layerConfig.dataView ?? chartDataView, `${layerId}_reference`); + } + + getLayerConfig(layerId: string): XYByValueAnnotationLayerConfig { + return { + layerId: `${layerId}_annotation`, + layerType: 'annotations', + annotations: this.layerConfig.annotations, + ignoreGlobalFilters: this.layerConfig.ignoreGlobalFilters || false, + indexPatternId: this.layerConfig.dataView?.id || '', + }; + } + + getDataView(): DataView | undefined { + return this.layerConfig.dataView; + } +} diff --git a/packages/kbn-lens-embeddable-utils/index.ts b/packages/kbn-lens-embeddable-utils/index.ts index ffe9d4e87d788..45d324c41fa2c 100644 --- a/packages/kbn-lens-embeddable-utils/index.ts +++ b/packages/kbn-lens-embeddable-utils/index.ts @@ -30,6 +30,7 @@ export { XYChart, XYDataLayer, XYReferenceLinesLayer, + XYByValueAnnotationsLayer, METRIC_ID, METRIC_TREND_LINE_ID, XY_ID, diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index 3369cb6ac63ae..7d4603174454f 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -5,8 +5,7 @@ * 2.0. */ -import moment from 'moment'; -import { DataViewBase, Query } from '@kbn/es-query'; +import { Query } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { useEffect, useMemo, useState } from 'react'; @@ -18,10 +17,8 @@ import { EuiSpacer, EuiText, EuiTitle, - useEuiTheme, } from '@elastic/eui'; import { Rule, RuleTypeParams } from '@kbn/alerting-plugin/common'; -import { AlertAnnotation, AlertActiveTimeRangeAnnotation } from '@kbn/observability-alert-details'; import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util'; import { ALERT_END, @@ -31,7 +28,6 @@ import { TAGS, } from '@kbn/rule-data-utils'; import { DataView } from '@kbn/data-views-plugin/common'; -import { MetricsExplorerChartType } from '../../../../../common/custom_threshold_rule/types'; import { useLicense } from '../../../../hooks/use_license'; import { useKibana } from '../../../../utils/kibana_react'; import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter'; @@ -41,21 +37,17 @@ import { CustomThresholdAlertFields, CustomThresholdRuleTypeParams, } from '../../types'; -import { ExpressionChart } from '../expression_chart'; import { TIME_LABELS } from '../criterion_preview_chart/criterion_preview_chart'; import { Threshold } from '../custom_threshold'; import { LogRateAnalysis } from './log_rate_analysis'; import { Groups } from './groups'; import { Tags } from './tags'; +import { RuleConditionChart } from '../rule_condition_chart/rule_condition_chart'; // TODO Use a generic props for app sections https://github.com/elastic/kibana/issues/152690 export type CustomThresholdRule = Rule; export type CustomThresholdAlert = TopAlert; -const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm'; -const ALERT_START_ANNOTATION_ID = 'alert_start_annotation'; -const ALERT_TIME_RANGE_ANNOTATION_ID = 'alert_time_range_annotation'; - interface AppSectionProps { alert: CustomThresholdAlert; rule: CustomThresholdRule; @@ -71,8 +63,7 @@ export default function AlertDetailsAppSection({ setAlertSummaryFields, }: AppSectionProps) { const services = useKibana().services; - const { uiSettings, charts, data } = services; - const { euiTheme } = useEuiTheme(); + const { charts, data } = services; const { hasAtLeast } = useLicense(); const hasLogRateAnalysisLicense = hasAtLeast('platinum'); const [dataView, setDataView] = useState(); @@ -82,23 +73,6 @@ export default function AlertDetailsAppSection({ baseTheme: charts.theme.useChartsBaseTheme(), }; const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]); - const alertEnd = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).valueOf() : undefined; - const annotations = [ - , - , - ]; useEffect(() => { const groups = alert.fields[ALERT_GROUP]; @@ -143,13 +117,14 @@ export default function AlertDetailsAppSection({ setAlertSummaryFields(alertSummaryFields); }, [alert, rule, ruleLink, setAlertSummaryFields]); - const derivedIndexPattern = useMemo( - () => ({ - fields: dataView?.fields || [], - title: dataView?.getIndexPattern() || 'unknown-index', - }), - [dataView] - ); + const filterQuery = useMemo(() => { + let query = `(${(ruleParams.searchConfiguration?.query as Query)?.query as string})`; + const groups = alert.fields[ALERT_GROUP] as Array<{ field: string; value: string }>; + groups.forEach(({ field, value }) => { + query += ` and ${field}: ${value}`; + }); + return query; + }, [ruleParams.searchConfiguration, alert.fields]); useEffect(() => { const initDataView = async () => { @@ -208,14 +183,15 @@ export default function AlertDetailsAppSection({ /> - diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/painless_tinymath_parser.test.ts b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/painless_tinymath_parser.test.ts similarity index 100% rename from x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/painless_tinymath_parser.test.ts rename to x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/painless_tinymath_parser.test.ts diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/painless_tinymath_parser.ts b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/painless_tinymath_parser.ts similarity index 100% rename from x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/painless_tinymath_parser.ts rename to x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/painless_tinymath_parser.ts diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/preview_chart.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx similarity index 94% rename from x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/preview_chart.test.tsx rename to x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx index bc478e54135ba..a2d93999014d3 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/preview_chart/preview_chart.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx @@ -13,7 +13,7 @@ import { Comparator, Aggregators } from '../../../../../common/custom_threshold_ import { useKibana } from '../../../../utils/kibana_react'; import { kibanaStartMock } from '../../../../utils/kibana_react.mock'; import { MetricExpression } from '../../types'; -import { getBufferThreshold, PreviewChart } from './preview_chart'; +import { getBufferThreshold, RuleConditionChart } from './rule_condition_chart'; jest.mock('../../../../utils/kibana_react'); @@ -25,14 +25,14 @@ const mockKibana = () => { }); }; -describe('Preview chart', () => { +describe('Rule condition chart', () => { beforeEach(() => { jest.clearAllMocks(); mockKibana(); }); async function setup(expression: MetricExpression, dataView?: DataView) { const wrapper = mountWithIntl( - { @@ -47,13 +57,15 @@ const getOperationTypeFromRuleAggType = (aggType: AggType): OperationType => { export const getBufferThreshold = (threshold?: number): string => (Math.ceil((threshold || 0) * 1.1 * 100) / 100).toFixed(2).toString(); -export function PreviewChart({ +export function RuleConditionChart({ metricExpression, dataView, filterQuery, groupBy, error, -}: PreviewChartPros) { + annotation, + timeRange, +}: RuleConditionChartPros) { const { services: { lens }, } = useKibana(); @@ -63,6 +75,7 @@ export function PreviewChart({ const [aggMap, setAggMap] = useState(); const [formula, setFormula] = useState(''); const [thresholdReferenceLine, setThresholdReferenceLine] = useState(); + const [alertAnnotation, setAlertAnnotation] = useState(); const [chartLoading, setChartLoading] = useState(false); const formulaAsync = useAsync(() => { return lens.stateHelperApi(); @@ -162,6 +175,33 @@ export function PreviewChart({ setThresholdReferenceLine(refLayers); }, [threshold, comparator, euiTheme.colors.danger, metrics]); + // Build alert annotation + useEffect(() => { + const annotationLayer = new XYByValueAnnotationsLayer({ + annotations: [ + { + label: `Alert`, + type: 'manual', + key: { + type: annotation?.endTimestamp ? 'range' : 'point_in_time', + timestamp: annotation?.timestamp, + endTimestamp: annotation?.endTimestamp, + }, + color: annotation?.endTimestamp + ? chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase() + : euiTheme.colors.danger, + icon: 'alert', + textVisibility: true, + id: uuidv4(), + } as PointInTimeEventAnnotationConfig, + ], + ignoreGlobalFilters: true, + dataView, + }); + + setAlertAnnotation(annotationLayer); + }, [euiTheme.colors.danger, dataView, annotation]); + // Build the aggregation map from the metrics useEffect(() => { if (!metrics || metrics.length === 0) { @@ -254,10 +294,15 @@ export function PreviewChart({ options: xYDataLayerOptions, }); - const layers: Array = [xyDataLayer]; + const layers: Array = [ + xyDataLayer, + ]; if (thresholdReferenceLine) { layers.push(...thresholdReferenceLine); } + if (alertAnnotation) { + layers.push(alertAnnotation); + } const attributesLens = new LensAttributesBuilder({ visualization: new XYChart({ visualOptions: { @@ -286,6 +331,7 @@ export function PreviewChart({ metrics, threshold, thresholdReferenceLine, + alertAnnotation, timeSize, timeUnit, ]); @@ -295,7 +341,8 @@ export function PreviewChart({ !attributes || error?.equation || Object.keys(error?.metrics || {}).length !== 0 || - !timeSize + !timeSize || + !timeRange ) { return (
@@ -327,7 +374,7 @@ export function PreviewChart({ onLoad={setChartLoading} id="customThresholdPreviewChart" style={{ height: 180 }} - timeRange={{ from: `now-${timeSize * 20}${timeUnit}`, to: 'now' }} + timeRange={timeRange} attributes={attributes} disableTriggers={true} query={{ diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx index 0df72511ce559..c82dcdaf2485f 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx @@ -42,7 +42,7 @@ import { TimeUnitChar } from '../../../common/utils/formatters/duration'; import { AlertContextMeta, AlertParams, MetricExpression } from './types'; import { ExpressionRow } from './components/expression_row'; import { MetricsExplorerFields, GroupBy } from './components/group_by'; -import { PreviewChart } from './components/preview_chart/preview_chart'; +import { RuleConditionChart } from './components/rule_condition_chart/rule_condition_chart'; const FILTER_TYPING_DEBOUNCE_MS = 500; @@ -456,12 +456,13 @@ export default function Expressions(props: Props) { ) } > -
From 5dbc95c7703ca899aecb50708301113f16ffceca Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Thu, 25 Jan 2024 11:00:20 +0100 Subject: [PATCH 02/14] resize actions menu --- .../public/pages/alert_details/components/header_actions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.tsx index fa6ae4c72758a..88418530eb757 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/header_actions.tsx @@ -139,7 +139,7 @@ export function HeaderActions({ alert, alertStatus, onUntrackAlert }: HeaderActi /> } > -
+
From 54385bcdf12af974199493a7684fabdf62d0ca2a Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Thu, 25 Jan 2024 12:58:52 +0100 Subject: [PATCH 03/14] fix filter query issue, error loading alert details page --- .../alert_details_app_section.tsx | 29 +++++++++++-------- .../pages/alert_details/alert_details.tsx | 4 +-- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index 7d4603174454f..4c343764ec2b5 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -8,7 +8,7 @@ import { Query } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, @@ -67,16 +67,17 @@ export default function AlertDetailsAppSection({ const { hasAtLeast } = useLicense(); const hasLogRateAnalysisLicense = hasAtLeast('platinum'); const [dataView, setDataView] = useState(); + const [filterQuery, setFilterQuery] = useState(''); const [, setDataViewError] = useState(); const ruleParams = rule.params as RuleTypeParams & AlertParams; const chartProps = { baseTheme: charts.theme.useChartsBaseTheme(), }; const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]); + const groups = alert.fields[ALERT_GROUP]; + const tags = alert.fields[TAGS]; useEffect(() => { - const groups = alert.fields[ALERT_GROUP]; - const tags = alert.fields[TAGS]; const alertSummaryFields = []; if (groups) { alertSummaryFields.push({ @@ -115,16 +116,20 @@ export default function AlertDetailsAppSection({ }); setAlertSummaryFields(alertSummaryFields); - }, [alert, rule, ruleLink, setAlertSummaryFields]); + }, [groups, tags, rule, ruleLink, setAlertSummaryFields]); - const filterQuery = useMemo(() => { - let query = `(${(ruleParams.searchConfiguration?.query as Query)?.query as string})`; - const groups = alert.fields[ALERT_GROUP] as Array<{ field: string; value: string }>; - groups.forEach(({ field, value }) => { - query += ` and ${field}: ${value}`; - }); - return query; - }, [ruleParams.searchConfiguration, alert.fields]); + useEffect(() => { + let query = `${(ruleParams.searchConfiguration?.query as Query)?.query as string}`; + if (query) { + query = `(${query})`; + } + if (groups) { + groups?.forEach(({ field, value }) => { + query += ` and ${field}: ${value}`; + }); + } + setFilterQuery(query); + }, [groups, ruleParams.searchConfiguration]); useEffect(() => { const initDataView = async () => { diff --git a/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx b/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx index feb7e06e0ae51..6ce338358c646 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/alert_details.tsx @@ -161,9 +161,9 @@ export function AlertDetails() { - {AlertDetailsAppSection && rule && ( + {AlertDetailsAppSection && rule && alertDetail?.formatted && ( Date: Thu, 25 Jan 2024 14:35:34 +0000 Subject: [PATCH 04/14] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/plugins/observability/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/observability/tsconfig.json b/x-pack/plugins/observability/tsconfig.json index 1f760c3799d90..8efdcececcbdd 100644 --- a/x-pack/plugins/observability/tsconfig.json +++ b/x-pack/plugins/observability/tsconfig.json @@ -105,7 +105,8 @@ "@kbn/core-saved-objects-api-server-mocks", "@kbn/core-ui-settings-browser-mocks", "@kbn/field-formats-plugin", - "@kbn/aiops-utils" + "@kbn/aiops-utils", + "@kbn/event-annotation-common" ], "exclude": [ "target/**/*" From a8dd3f9eaf115269f02f7bb45bf3ab4142cc361d Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Fri, 26 Jan 2024 13:53:54 +0100 Subject: [PATCH 05/14] fix tests, fix bug --- .../alert_details_app_section.test.tsx.snap | 32 ++++++------------- .../alert_details_app_section.test.tsx | 14 ++++---- .../alert_details_app_section.tsx | 8 ++--- .../custom_threshold_rule_expression.test.tsx | 4 +-- 4 files changed, 20 insertions(+), 38 deletions(-) diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/__snapshots__/alert_details_app_section.test.tsx.snap b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/__snapshots__/alert_details_app_section.test.tsx.snap index 0446f2fe1d4a2..9f0d1d50d9031 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/__snapshots__/alert_details_app_section.test.tsx.snap +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/__snapshots__/alert_details_app_section.test.tsx.snap @@ -3,25 +3,16 @@ exports[`AlertDetailsAppSection should render annotations 1`] = ` Array [ Object { - "annotations": Array [ - , - , - ], - "chartType": "line", - "derivedIndexPattern": Object { - "fields": Array [], - "title": "unknown-index", + "annotation": Object { + "endTimestamp": undefined, + "timestamp": "2023-03-28T13:40:00.000Z", }, - "expression": Object { + "dataView": undefined, + "filterQuery": "", + "groupBy": Array [ + "host.hostname", + ], + "metricExpression": Object { "comparator": ">", "metrics": Array [ Object { @@ -35,11 +26,6 @@ Array [ "timeSize": 15, "timeUnit": "m", }, - "filterQuery": "host.hostname: Users-System.local and service.type: system", - "groupBy": Array [ - "host.hostname", - ], - "hideTitle": true, "timeRange": Object { "from": "2023-03-28T10:43:13.802Z", "to": "2023-03-29T13:14:09.581Z", diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx index 1e7c91af675ba..50ed462113bfc 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx @@ -18,7 +18,7 @@ import { buildCustomThresholdRule, } from '../../mocks/custom_threshold_rule'; import { CustomThresholdAlertFields } from '../../types'; -import { ExpressionChart } from '../expression_chart'; +import { RuleConditionChart } from '../rule_condition_chart/rule_condition_chart'; import AlertDetailsAppSection, { CustomThresholdAlert } from './alert_details_app_section'; import { Groups } from './groups'; import { Tags } from './tags'; @@ -37,8 +37,8 @@ jest.mock('@kbn/observability-get-padded-alert-time-range-util', () => ({ }), })); -jest.mock('../expression_chart', () => ({ - ExpressionChart: jest.fn(() =>
), +jest.mock('../rule_condition_chart/rule_condition_chart', () => ({ + RuleConditionChart: jest.fn(() =>
), })); jest.mock('../../../../utils/kibana_react', () => ({ @@ -141,11 +141,11 @@ describe('AlertDetailsAppSection', () => { }); it('should render annotations', async () => { - const mockedExpressionChart = jest.fn(() =>
); - (ExpressionChart as jest.Mock).mockImplementation(mockedExpressionChart); + const mockedRuleConditionChart = jest.fn(() =>
); + (RuleConditionChart as jest.Mock).mockImplementation(mockedRuleConditionChart); const alertDetailsAppSectionComponent = renderComponent(); - expect(alertDetailsAppSectionComponent.getAllByTestId('ExpressionChart').length).toBe(3); - expect(mockedExpressionChart.mock.calls[0]).toMatchSnapshot(); + expect(alertDetailsAppSectionComponent.getAllByTestId('RuleConditionChart').length).toBe(3); + expect(mockedRuleConditionChart.mock.calls[0]).toMatchSnapshot(); }); }); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index 4c343764ec2b5..7259037659618 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -120,13 +120,9 @@ export default function AlertDetailsAppSection({ useEffect(() => { let query = `${(ruleParams.searchConfiguration?.query as Query)?.query as string}`; - if (query) { - query = `(${query})`; - } if (groups) { - groups?.forEach(({ field, value }) => { - query += ` and ${field}: ${value}`; - }); + const groupQueries = groups?.map(({ field, value }) => `${field}: ${value}`).join(' and '); + query = query ? `(${query}) and ${groupQueries}` : groupQueries; } setFilterQuery(query); }, [groups, ruleParams.searchConfiguration]); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx index a97de99ec8e78..8f569e1d80b17 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx @@ -20,8 +20,8 @@ import Expressions from './custom_threshold_rule_expression'; import { AlertParams, CustomThresholdPrefillOptions } from './types'; jest.mock('../../utils/kibana_react'); -jest.mock('./components/preview_chart/preview_chart', () => ({ - PreviewChart: jest.fn(() =>
), +jest.mock('./components/rule_condition_chart/rule_condition_chart', () => ({ + RuleConditionChart: jest.fn(() =>
), })); const useKibanaMock = useKibana as jest.Mock; From 2ef2081b122a1e8a43ad8f201200a9f48a87c8c0 Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Fri, 26 Jan 2024 14:49:35 +0100 Subject: [PATCH 06/14] refactoring --- .../visualization_types/layers/index.ts | 5 +- .../layers/xy_annotation_layer.ts | 16 ++--- packages/kbn-lens-embeddable-utils/index.ts | 2 +- .../rule_condition_chart.tsx | 62 +++++++++++++------ 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts index c65baac8fab34..1938a3ebbbcf5 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts @@ -12,9 +12,6 @@ export { XYReferenceLinesLayer, type XYReferenceLinesLayerConfig, } from './xy_reference_lines_layer'; -export { - XYByValueAnnotationsLayer, - type XYByValueAnnotationsLayerConfig, -} from './xy_annotation_layer'; +export { XYAnnotationsLayer, type XYAnnotationsLayerConfig } from './xy_annotation_layer'; export { FormulaColumn } from './columns/formula'; export { StaticColumn } from './columns/static'; diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_annotation_layer.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_annotation_layer.ts index 2407a3cb1da1b..9132484f0e3ce 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_annotation_layer.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_annotation_layer.ts @@ -8,15 +8,15 @@ import type { SavedObjectReference } from '@kbn/core/server'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { PointInTimeEventAnnotationConfig } from '@kbn/event-annotation-common'; +import { EventAnnotationConfig } from '@kbn/event-annotation-common'; import type { FormBasedPersistedState, PersistedIndexPatternLayer } from '@kbn/lens-plugin/public'; -import type { XYByValueAnnotationLayerConfig } from '@kbn/lens-plugin/public/visualizations/xy/types'; +import type { XYAnnotationLayerConfig } from '@kbn/lens-plugin/public/visualizations/xy/types'; import type { ChartLayer } from '../../types'; import { getDefaultReferences } from '../../utils'; import { XY_ANNOTATIONS_ID } from '../constants'; -export interface XYByValueAnnotationsLayerConfig { - annotations: PointInTimeEventAnnotationConfig[]; +export interface XYAnnotationsLayerConfig { + annotations: EventAnnotationConfig[]; layerType?: typeof XY_ANNOTATIONS_ID; /** * It is possible to define a specific dataView for the layer. It will override the global chart one @@ -25,10 +25,10 @@ export interface XYByValueAnnotationsLayerConfig { ignoreGlobalFilters?: boolean; } -export class XYByValueAnnotationsLayer implements ChartLayer { - private layerConfig: XYByValueAnnotationsLayerConfig; +export class XYAnnotationsLayer implements ChartLayer { + private layerConfig: XYAnnotationsLayerConfig; - constructor(layerConfig: XYByValueAnnotationsLayerConfig) { + constructor(layerConfig: XYAnnotationsLayerConfig) { this.layerConfig = { ...layerConfig, layerType: layerConfig.layerType ?? 'annotations', @@ -50,7 +50,7 @@ export class XYByValueAnnotationsLayer implements ChartLayer(); const [formula, setFormula] = useState(''); const [thresholdReferenceLine, setThresholdReferenceLine] = useState(); - const [alertAnnotation, setAlertAnnotation] = useState(); + const [alertAnnotation, setAlertAnnotation] = useState(); const [chartLoading, setChartLoading] = useState(false); const formulaAsync = useAsync(() => { return lens.stateHelperApi(); @@ -177,29 +178,56 @@ export function RuleConditionChart({ // Build alert annotation useEffect(() => { - const annotationLayer = new XYByValueAnnotationsLayer({ - annotations: [ + if (!annotation) return; + + const annotations: EventAnnotationConfig[] = []; + + if (annotation.endTimestamp) { + annotations.push({ + label: `Alert`, + type: 'manual', + key: { + type: 'range', + timestamp: annotation.timestamp, + endTimestamp: annotation.endTimestamp, + }, + color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), + id: uuidv4(), + } as EventAnnotationConfig); + } else { + annotations.push( { label: `Alert`, type: 'manual', key: { - type: annotation?.endTimestamp ? 'range' : 'point_in_time', - timestamp: annotation?.timestamp, - endTimestamp: annotation?.endTimestamp, + type: 'point_in_time', + timestamp: annotation.timestamp, }, - color: annotation?.endTimestamp - ? chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase() - : euiTheme.colors.danger, + color: euiTheme.colors.danger, icon: 'alert', - textVisibility: true, id: uuidv4(), - } as PointInTimeEventAnnotationConfig, - ], + } as EventAnnotationConfig, + { + label: `Active Alert`, + type: 'manual', + key: { + type: 'range', + timestamp: annotation.timestamp, + endTimestamp: moment().toISOString(), + }, + color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), + id: uuidv4(), + } as EventAnnotationConfig + ); + } + + const alertAnnotationLayer = new XYAnnotationsLayer({ + annotations, ignoreGlobalFilters: true, dataView, }); - setAlertAnnotation(annotationLayer); + setAlertAnnotation(alertAnnotationLayer); }, [euiTheme.colors.danger, dataView, annotation]); // Build the aggregation map from the metrics @@ -294,9 +322,7 @@ export function RuleConditionChart({ options: xYDataLayerOptions, }); - const layers: Array = [ - xyDataLayer, - ]; + const layers: Array = [xyDataLayer]; if (thresholdReferenceLine) { layers.push(...thresholdReferenceLine); } From f99f5fbb2d6b94af9323446031f16291df3a8bc3 Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Fri, 26 Jan 2024 15:02:17 +0100 Subject: [PATCH 07/14] make timerange non-optional --- .../rule_condition_chart/rule_condition_chart.test.tsx | 1 + .../components/rule_condition_chart/rule_condition_chart.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx index a2d93999014d3..320958a20c0e8 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx @@ -38,6 +38,7 @@ describe('Rule condition chart', () => { filterQuery={''} groupBy={[]} error={{}} + timeRange={{ from: 'now-15m', to: 'now' }} /> ); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx index 8c20ab5acfd02..c7ac025219b5d 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx @@ -42,7 +42,7 @@ interface RuleConditionChartPros { filterQuery?: string; groupBy?: string | string[]; error?: IErrorObject; - timeRange?: TimeRange; + timeRange: TimeRange; annotation?: { timestamp?: string; endTimestamp?: string; From fd364fabd79b393c2e5dd2119bed9c37e9425508 Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Tue, 30 Jan 2024 10:47:03 +0100 Subject: [PATCH 08/14] change xy_annotation_layer to xy_by_value_annotation_layer --- .../visualization_types/layers/index.ts | 5 ++++- ...tion_layer.ts => xy_by_value_annotation_layer.ts} | 12 ++++++------ packages/kbn-lens-embeddable-utils/index.ts | 2 +- .../rule_condition_chart/rule_condition_chart.tsx | 10 ++++++---- 4 files changed, 17 insertions(+), 12 deletions(-) rename packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/{xy_annotation_layer.ts => xy_by_value_annotation_layer.ts} (82%) diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts index 1938a3ebbbcf5..ed7dd2fd971c1 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/index.ts @@ -12,6 +12,9 @@ export { XYReferenceLinesLayer, type XYReferenceLinesLayerConfig, } from './xy_reference_lines_layer'; -export { XYAnnotationsLayer, type XYAnnotationsLayerConfig } from './xy_annotation_layer'; +export { + XYByValueAnnotationsLayer, + type XYByValueAnnotationsLayerConfig, +} from './xy_by_value_annotation_layer'; export { FormulaColumn } from './columns/formula'; export { StaticColumn } from './columns/static'; diff --git a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_annotation_layer.ts b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_by_value_annotation_layer.ts similarity index 82% rename from packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_annotation_layer.ts rename to packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_by_value_annotation_layer.ts index 9132484f0e3ce..41e62caab9a23 100644 --- a/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_annotation_layer.ts +++ b/packages/kbn-lens-embeddable-utils/attribute_builder/visualization_types/layers/xy_by_value_annotation_layer.ts @@ -10,12 +10,12 @@ import type { SavedObjectReference } from '@kbn/core/server'; import type { DataView } from '@kbn/data-views-plugin/common'; import { EventAnnotationConfig } from '@kbn/event-annotation-common'; import type { FormBasedPersistedState, PersistedIndexPatternLayer } from '@kbn/lens-plugin/public'; -import type { XYAnnotationLayerConfig } from '@kbn/lens-plugin/public/visualizations/xy/types'; +import type { XYByValueAnnotationLayerConfig } from '@kbn/lens-plugin/public/visualizations/xy/types'; import type { ChartLayer } from '../../types'; import { getDefaultReferences } from '../../utils'; import { XY_ANNOTATIONS_ID } from '../constants'; -export interface XYAnnotationsLayerConfig { +export interface XYByValueAnnotationsLayerConfig { annotations: EventAnnotationConfig[]; layerType?: typeof XY_ANNOTATIONS_ID; /** @@ -25,10 +25,10 @@ export interface XYAnnotationsLayerConfig { ignoreGlobalFilters?: boolean; } -export class XYAnnotationsLayer implements ChartLayer { - private layerConfig: XYAnnotationsLayerConfig; +export class XYByValueAnnotationsLayer implements ChartLayer { + private layerConfig: XYByValueAnnotationsLayerConfig; - constructor(layerConfig: XYAnnotationsLayerConfig) { + constructor(layerConfig: XYByValueAnnotationsLayerConfig) { this.layerConfig = { ...layerConfig, layerType: layerConfig.layerType ?? 'annotations', @@ -50,7 +50,7 @@ export class XYAnnotationsLayer implements ChartLayer { return getDefaultReferences(this.layerConfig.dataView ?? chartDataView, `${layerId}_reference`); } - getLayerConfig(layerId: string): XYAnnotationLayerConfig { + getLayerConfig(layerId: string): XYByValueAnnotationLayerConfig { return { layerId: `${layerId}_annotation`, layerType: 'annotations', diff --git a/packages/kbn-lens-embeddable-utils/index.ts b/packages/kbn-lens-embeddable-utils/index.ts index 6b9b8b0136416..45d324c41fa2c 100644 --- a/packages/kbn-lens-embeddable-utils/index.ts +++ b/packages/kbn-lens-embeddable-utils/index.ts @@ -30,7 +30,7 @@ export { XYChart, XYDataLayer, XYReferenceLinesLayer, - XYAnnotationsLayer, + XYByValueAnnotationsLayer, METRIC_ID, METRIC_TREND_LINE_ID, XY_ID, diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx index c7ac025219b5d..12cc1e15b18bd 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx @@ -17,7 +17,7 @@ import { XYDataLayer, XYLayerOptions, XYReferenceLinesLayer, - XYAnnotationsLayer, + XYByValueAnnotationsLayer, } from '@kbn/lens-embeddable-utils'; import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; @@ -76,7 +76,7 @@ export function RuleConditionChart({ const [aggMap, setAggMap] = useState(); const [formula, setFormula] = useState(''); const [thresholdReferenceLine, setThresholdReferenceLine] = useState(); - const [alertAnnotation, setAlertAnnotation] = useState(); + const [alertAnnotation, setAlertAnnotation] = useState(); const [chartLoading, setChartLoading] = useState(false); const formulaAsync = useAsync(() => { return lens.stateHelperApi(); @@ -221,7 +221,7 @@ export function RuleConditionChart({ ); } - const alertAnnotationLayer = new XYAnnotationsLayer({ + const alertAnnotationLayer = new XYByValueAnnotationsLayer({ annotations, ignoreGlobalFilters: true, dataView, @@ -322,7 +322,9 @@ export function RuleConditionChart({ options: xYDataLayerOptions, }); - const layers: Array = [xyDataLayer]; + const layers: Array = [ + xyDataLayer, + ]; if (thresholdReferenceLine) { layers.push(...thresholdReferenceLine); } From a6fb1a7e8b3a9d5e81dfe15a53b9745b0894196b Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Wed, 31 Jan 2024 12:51:27 +0100 Subject: [PATCH 09/14] PR feedback --- .../rule_condition_chart.tsx | 90 ++++++++++--------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx index 12cc1e15b18bd..9da991df0c902 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx @@ -24,7 +24,11 @@ import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; import { i18n } from '@kbn/i18n'; import { v4 as uuidv4 } from 'uuid'; import chroma from 'chroma-js'; -import type { EventAnnotationConfig } from '@kbn/event-annotation-common'; +import type { + EventAnnotationConfig, + PointInTimeEventAnnotationConfig, + RangeEventAnnotationConfig, +} from '@kbn/event-annotation-common'; import { TimeRange } from '@kbn/es-query'; import moment from 'moment'; import { @@ -36,7 +40,7 @@ import { useKibana } from '../../../../utils/kibana_react'; import { MetricExpression } from '../../types'; import { AggMap, PainlessTinyMathParser } from './painless_tinymath_parser'; -interface RuleConditionChartPros { +interface RuleConditionChartProps { metricExpression: MetricExpression; dataView?: DataView; filterQuery?: string; @@ -44,7 +48,7 @@ interface RuleConditionChartPros { error?: IErrorObject; timeRange: TimeRange; annotation?: { - timestamp?: string; + timestamp: string; endTimestamp?: string; }; } @@ -66,7 +70,7 @@ export function RuleConditionChart({ error, annotation, timeRange, -}: RuleConditionChartPros) { +}: RuleConditionChartProps) { const { services: { lens }, } = useKibana(); @@ -182,44 +186,46 @@ export function RuleConditionChart({ const annotations: EventAnnotationConfig[] = []; - if (annotation.endTimestamp) { - annotations.push({ - label: `Alert`, - type: 'manual', - key: { - type: 'range', - timestamp: annotation.timestamp, - endTimestamp: annotation.endTimestamp, - }, - color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), - id: uuidv4(), - } as EventAnnotationConfig); - } else { - annotations.push( - { - label: `Alert`, - type: 'manual', - key: { - type: 'point_in_time', - timestamp: annotation.timestamp, - }, - color: euiTheme.colors.danger, - icon: 'alert', - id: uuidv4(), - } as EventAnnotationConfig, - { - label: `Active Alert`, - type: 'manual', - key: { - type: 'range', - timestamp: annotation.timestamp, - endTimestamp: moment().toISOString(), - }, - color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), - id: uuidv4(), - } as EventAnnotationConfig - ); - } + const alertStartAnnotation: PointInTimeEventAnnotationConfig = { + label: 'Alert', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: annotation.timestamp, + }, + color: euiTheme.colors.danger, + icon: 'alert', + id: uuidv4(), + }; + + const activeAlertRangeAnnotation: RangeEventAnnotationConfig = { + label: 'Active Alert', + type: 'manual', + key: { + type: 'range', + timestamp: annotation.timestamp, + endTimestamp: moment().toISOString(), + }, + color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), + id: uuidv4(), + }; + + const recoveredAlertRangeAnnotation: RangeEventAnnotationConfig = { + label: 'Alert duration', + type: 'manual', + key: { + type: 'range', + timestamp: annotation.timestamp, + endTimestamp: annotation.endTimestamp ?? '', + }, + color: chroma(transparentize('#F04E981A', 1)).hex().toUpperCase(), + id: uuidv4(), + }; + + annotations.push(alertStartAnnotation); + annotations.push( + annotation.endTimestamp ? recoveredAlertRangeAnnotation : activeAlertRangeAnnotation + ); const alertAnnotationLayer = new XYByValueAnnotationsLayer({ annotations, From 68b92cb17f74a248d48260ea206323ca38b8b59e Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Fri, 2 Feb 2024 11:38:16 +0100 Subject: [PATCH 10/14] fix annotation issue, refactoring --- .../alert_details_app_section.tsx | 61 ++++++++++++++-- .../rule_condition_chart.tsx | 72 +++---------------- 2 files changed, 67 insertions(+), 66 deletions(-) diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index 7259037659618..3bf3c7e2df673 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -28,6 +28,15 @@ import { TAGS, } from '@kbn/rule-data-utils'; import { DataView } from '@kbn/data-views-plugin/common'; +import { v4 as uuidv4 } from 'uuid'; +import chroma from 'chroma-js'; +import type { + EventAnnotationConfig, + PointInTimeEventAnnotationConfig, + RangeEventAnnotationConfig, +} from '@kbn/event-annotation-common'; +import moment from 'moment'; +import { transparentize, useEuiTheme } from '@elastic/eui'; import { useLicense } from '../../../../hooks/use_license'; import { useKibana } from '../../../../utils/kibana_react'; import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter'; @@ -65,6 +74,7 @@ export default function AlertDetailsAppSection({ const services = useKibana().services; const { charts, data } = services; const { hasAtLeast } = useLicense(); + const { euiTheme } = useEuiTheme(); const hasLogRateAnalysisLicense = hasAtLeast('platinum'); const [dataView, setDataView] = useState(); const [filterQuery, setFilterQuery] = useState(''); @@ -73,10 +83,52 @@ export default function AlertDetailsAppSection({ const chartProps = { baseTheme: charts.theme.useChartsBaseTheme(), }; - const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]); + const alertStart = alert.fields[ALERT_START]; + const alertEnd = alert.fields[ALERT_END]; + const timeRange = getPaddedAlertTimeRange(alertStart!, alertEnd); const groups = alert.fields[ALERT_GROUP]; const tags = alert.fields[TAGS]; + const alertStartAnnotation: PointInTimeEventAnnotationConfig = { + label: 'Alert', + type: 'manual', + key: { + type: 'point_in_time', + timestamp: alertStart!, + }, + color: euiTheme.colors.danger, + icon: 'alert', + id: uuidv4(), + }; + + const activeAlertRangeAnnotation: RangeEventAnnotationConfig = { + label: 'Active Alert', + type: 'manual', + key: { + type: 'range', + timestamp: alertStart!, + endTimestamp: moment().toISOString(), + }, + color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), + id: uuidv4(), + }; + + const recoveredAlertRangeAnnotation: RangeEventAnnotationConfig = { + label: 'Alert duration', + type: 'manual', + key: { + type: 'range', + timestamp: alertStart!, + endTimestamp: alertEnd ?? '', + }, + color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), + id: uuidv4(), + }; + + const annotations: EventAnnotationConfig[] = []; + annotations.push(alertStartAnnotation); + annotations.push(alertEnd ? recoveredAlertRangeAnnotation : activeAlertRangeAnnotation); + useEffect(() => { const alertSummaryFields = []; if (groups) { @@ -189,11 +241,10 @@ export default function AlertDetailsAppSection({ dataView={dataView} filterQuery={filterQuery} groupBy={ruleParams.groupBy} - annotation={{ - timestamp: alert.fields[ALERT_START], - endTimestamp: alert.fields[ALERT_END], - }} + annotations={annotations} timeRange={timeRange} + // For alert details page, the series type needs to be changed to 'bar_stacked' due to https://github.com/elastic/elastic-charts/issues/2323 + seriesType={'bar_stacked'} /> diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx index 9da991df0c902..bc2701eb79489 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx @@ -5,8 +5,8 @@ * 2.0. */ import React, { useState, useEffect } from 'react'; -import { EuiEmptyPrompt, transparentize, useEuiTheme } from '@elastic/eui'; -import { FillStyle, OperationType } from '@kbn/lens-plugin/public'; +import { EuiEmptyPrompt, useEuiTheme } from '@elastic/eui'; +import { FillStyle, OperationType, SeriesType } from '@kbn/lens-plugin/public'; import { DataView } from '@kbn/data-views-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; import useAsync from 'react-use/lib/useAsync'; @@ -22,15 +22,8 @@ import { import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; import { i18n } from '@kbn/i18n'; -import { v4 as uuidv4 } from 'uuid'; -import chroma from 'chroma-js'; -import type { - EventAnnotationConfig, - PointInTimeEventAnnotationConfig, - RangeEventAnnotationConfig, -} from '@kbn/event-annotation-common'; import { TimeRange } from '@kbn/es-query'; -import moment from 'moment'; +import { EventAnnotationConfig } from '@kbn/event-annotation-common'; import { Aggregators, Comparator, @@ -47,10 +40,8 @@ interface RuleConditionChartProps { groupBy?: string | string[]; error?: IErrorObject; timeRange: TimeRange; - annotation?: { - timestamp: string; - endTimestamp?: string; - }; + annotations?: EventAnnotationConfig[]; + seriesType?: SeriesType; } const getOperationTypeFromRuleAggType = (aggType: AggType): OperationType => { @@ -68,8 +59,9 @@ export function RuleConditionChart({ filterQuery, groupBy, error, - annotation, + annotations, timeRange, + seriesType, }: RuleConditionChartProps) { const { services: { lens }, @@ -182,50 +174,7 @@ export function RuleConditionChart({ // Build alert annotation useEffect(() => { - if (!annotation) return; - - const annotations: EventAnnotationConfig[] = []; - - const alertStartAnnotation: PointInTimeEventAnnotationConfig = { - label: 'Alert', - type: 'manual', - key: { - type: 'point_in_time', - timestamp: annotation.timestamp, - }, - color: euiTheme.colors.danger, - icon: 'alert', - id: uuidv4(), - }; - - const activeAlertRangeAnnotation: RangeEventAnnotationConfig = { - label: 'Active Alert', - type: 'manual', - key: { - type: 'range', - timestamp: annotation.timestamp, - endTimestamp: moment().toISOString(), - }, - color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), - id: uuidv4(), - }; - - const recoveredAlertRangeAnnotation: RangeEventAnnotationConfig = { - label: 'Alert duration', - type: 'manual', - key: { - type: 'range', - timestamp: annotation.timestamp, - endTimestamp: annotation.endTimestamp ?? '', - }, - color: chroma(transparentize('#F04E981A', 1)).hex().toUpperCase(), - id: uuidv4(), - }; - - annotations.push(alertStartAnnotation); - annotations.push( - annotation.endTimestamp ? recoveredAlertRangeAnnotation : activeAlertRangeAnnotation - ); + if (!annotations) return; const alertAnnotationLayer = new XYByValueAnnotationsLayer({ annotations, @@ -234,7 +183,7 @@ export function RuleConditionChart({ }); setAlertAnnotation(alertAnnotationLayer); - }, [euiTheme.colors.danger, dataView, annotation]); + }, [euiTheme.colors.danger, dataView, annotations]); // Build the aggregation map from the metrics useEffect(() => { @@ -303,7 +252,7 @@ export function RuleConditionChart({ interval: `${timeSize}${timeUnit}`, }, }, - seriesType: 'bar', + seriesType: seriesType ? seriesType : 'bar', }; if (groupBy && groupBy?.length) { @@ -368,6 +317,7 @@ export function RuleConditionChart({ alertAnnotation, timeSize, timeUnit, + seriesType, ]); if ( From 51eadcc1475aed95174d4169253e9b5ebb48b1cf Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Fri, 2 Feb 2024 12:48:16 +0100 Subject: [PATCH 11/14] refactoring --- .../alert_details_app_section.tsx | 9 ++--- .../helpers/get_filter_query.test.ts | 39 +++++++++++++++++++ .../helpers/get_filter_query.ts | 21 ++++++++++ 3 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.test.ts create mode 100644 x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.ts diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index 3bf3c7e2df673..a9f28823f2f5f 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -52,6 +52,7 @@ import { LogRateAnalysis } from './log_rate_analysis'; import { Groups } from './groups'; import { Tags } from './tags'; import { RuleConditionChart } from '../rule_condition_chart/rule_condition_chart'; +import { getFilterQuery } from './helpers/get_filter_query'; // TODO Use a generic props for app sections https://github.com/elastic/kibana/issues/152690 export type CustomThresholdRule = Rule; @@ -171,12 +172,8 @@ export default function AlertDetailsAppSection({ }, [groups, tags, rule, ruleLink, setAlertSummaryFields]); useEffect(() => { - let query = `${(ruleParams.searchConfiguration?.query as Query)?.query as string}`; - if (groups) { - const groupQueries = groups?.map(({ field, value }) => `${field}: ${value}`).join(' and '); - query = query ? `(${query}) and ${groupQueries}` : groupQueries; - } - setFilterQuery(query); + const query = `${(ruleParams.searchConfiguration?.query as Query)?.query as string}`; + setFilterQuery(getFilterQuery(query, groups)); }, [groups, ruleParams.searchConfiguration]); useEffect(() => { diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.test.ts b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.test.ts new file mode 100644 index 0000000000000..574a9dcd4402b --- /dev/null +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.test.ts @@ -0,0 +1,39 @@ +/* + * 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 { getFilterQuery } from './get_filter_query'; + +describe('getFilterQuery', () => { + it('should generate correct filter query when original query is not empty', () => { + const query = 'container.id: container-1'; + const groups = [ + { field: 'container.id', value: 'container-0' }, + { field: 'host.name', value: 'host-0' }, + ]; + + expect(getFilterQuery(query, groups)).toBe( + '(container.id: container-1) and container.id: container-0 and host.name: host-0' + ); + }); + + it('should generate correct filter query when original query is empty', () => { + const query = ''; + const groups = [ + { field: 'container.id', value: 'container-0' }, + { field: 'host.name', value: 'host-0' }, + ]; + + expect(getFilterQuery(query, groups)).toBe('container.id: container-0 and host.name: host-0'); + }); + + it('should generate correct filter query when original query and groups both are empty', () => { + const query = ''; + const groups = undefined; + + expect(getFilterQuery(query as unknown as string, groups)).toBe(''); + }); +}); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.ts b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.ts new file mode 100644 index 0000000000000..2e1dd2ee0e543 --- /dev/null +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.ts @@ -0,0 +1,21 @@ +/* + * 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. + */ + +export const getFilterQuery = ( + filter: string, + groups?: Array<{ + field: string; + value: string; + }> +) => { + let query = filter; + if (groups) { + const groupQueries = groups?.map(({ field, value }) => `${field}: ${value}`).join(' and '); + query = query ? `(${query}) and ${groupQueries}` : groupQueries; + } + return query; +}; From 2de9e0e0ab5e70e9fa662f008d75161be5fee554 Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Fri, 2 Feb 2024 12:58:14 +0100 Subject: [PATCH 12/14] typo --- .../alert_details_app_section/helpers/get_filter_query.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.test.ts b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.test.ts index 574a9dcd4402b..91cfa4f7abc01 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.test.ts +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/get_filter_query.test.ts @@ -34,6 +34,6 @@ describe('getFilterQuery', () => { const query = ''; const groups = undefined; - expect(getFilterQuery(query as unknown as string, groups)).toBe(''); + expect(getFilterQuery(query, groups)).toBe(''); }); }); From b1d8c91386f2a96513eef3f40f61e779df4a73a5 Mon Sep 17 00:00:00 2001 From: Bena Kansara Date: Fri, 2 Feb 2024 21:58:29 +0100 Subject: [PATCH 13/14] fix tests --- .../alert_details_app_section.test.tsx.snap | 29 ++++++++++++++++--- .../alert_details_app_section.test.tsx | 5 +++- .../alert_details_app_section.tsx | 7 ++--- .../custom_threshold_rule_expression.tsx | 4 +-- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/__snapshots__/alert_details_app_section.test.tsx.snap b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/__snapshots__/alert_details_app_section.test.tsx.snap index 9f0d1d50d9031..b963137281b70 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/__snapshots__/alert_details_app_section.test.tsx.snap +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/__snapshots__/alert_details_app_section.test.tsx.snap @@ -3,10 +3,30 @@ exports[`AlertDetailsAppSection should render annotations 1`] = ` Array [ Object { - "annotation": Object { - "endTimestamp": undefined, - "timestamp": "2023-03-28T13:40:00.000Z", - }, + "annotations": Array [ + Object { + "color": "#BD271E", + "icon": "alert", + "id": "custom_threshold_alert_start_annotation", + "key": Object { + "timestamp": "2023-03-28T13:40:00.000Z", + "type": "point_in_time", + }, + "label": "Alert", + "type": "manual", + }, + Object { + "color": "#F04E9833", + "id": "custom_threshold_recovered_alert_range_annotation", + "key": Object { + "endTimestamp": "2023-03-28T14:40:00.000Z", + "timestamp": "2023-03-28T13:40:00.000Z", + "type": "range", + }, + "label": "Alert duration", + "type": "manual", + }, + ], "dataView": undefined, "filterQuery": "", "groupBy": Array [ @@ -26,6 +46,7 @@ Array [ "timeSize": 15, "timeUnit": "m", }, + "seriesType": "bar_stacked", "timeRange": Object { "from": "2023-03-28T10:43:13.802Z", "to": "2023-03-29T13:14:09.581Z", diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx index 50ed462113bfc..80d7feb20a4e6 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.test.tsx @@ -143,7 +143,10 @@ describe('AlertDetailsAppSection', () => { it('should render annotations', async () => { const mockedRuleConditionChart = jest.fn(() =>
); (RuleConditionChart as jest.Mock).mockImplementation(mockedRuleConditionChart); - const alertDetailsAppSectionComponent = renderComponent(); + const alertDetailsAppSectionComponent = renderComponent( + {}, + { ['kibana.alert.end']: '2023-03-28T14:40:00.000Z' } + ); expect(alertDetailsAppSectionComponent.getAllByTestId('RuleConditionChart').length).toBe(3); expect(mockedRuleConditionChart.mock.calls[0]).toMatchSnapshot(); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index a9f28823f2f5f..3e77371c63e0f 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -28,7 +28,6 @@ import { TAGS, } from '@kbn/rule-data-utils'; import { DataView } from '@kbn/data-views-plugin/common'; -import { v4 as uuidv4 } from 'uuid'; import chroma from 'chroma-js'; import type { EventAnnotationConfig, @@ -99,7 +98,7 @@ export default function AlertDetailsAppSection({ }, color: euiTheme.colors.danger, icon: 'alert', - id: uuidv4(), + id: 'custom_threshold_alert_start_annotation', }; const activeAlertRangeAnnotation: RangeEventAnnotationConfig = { @@ -111,7 +110,7 @@ export default function AlertDetailsAppSection({ endTimestamp: moment().toISOString(), }, color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), - id: uuidv4(), + id: 'custom_threshold_active_alert_range_annotation', }; const recoveredAlertRangeAnnotation: RangeEventAnnotationConfig = { @@ -123,7 +122,7 @@ export default function AlertDetailsAppSection({ endTimestamp: alertEnd ?? '', }, color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), - id: uuidv4(), + id: 'custom_threshold_recovered_alert_range_annotation', }; const annotations: EventAnnotationConfig[] = []; diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx index 576449185b8bf..cd93a5e134c2b 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx @@ -42,7 +42,7 @@ import { TimeUnitChar } from '../../../common/utils/formatters/duration'; import { AlertContextMeta, AlertParams, MetricExpression } from './types'; import { ExpressionRow } from './components/expression_row'; import { MetricsExplorerFields, GroupBy } from './components/group_by'; -import { RuleConditionChart } from './components/rule_condition_chart/rule_condition_chart'; +import { RuleConditionChart as PreviewChart } from './components/rule_condition_chart/rule_condition_chart'; const FILTER_TYPING_DEBOUNCE_MS = 500; @@ -451,7 +451,7 @@ export default function Expressions(props: Props) { ) } > - Date: Fri, 2 Feb 2024 22:10:25 +0100 Subject: [PATCH 14/14] pr feedback --- .../alert_details_app_section.tsx | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx index 3e77371c63e0f..9d2aebddb43ee 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section/alert_details_app_section.tsx @@ -101,33 +101,20 @@ export default function AlertDetailsAppSection({ id: 'custom_threshold_alert_start_annotation', }; - const activeAlertRangeAnnotation: RangeEventAnnotationConfig = { - label: 'Active Alert', + const alertRangeAnnotation: RangeEventAnnotationConfig = { + label: `${alertEnd ? 'Alert duration' : 'Active alert'}`, type: 'manual', key: { type: 'range', timestamp: alertStart!, - endTimestamp: moment().toISOString(), + endTimestamp: alertEnd ?? moment().toISOString(), }, color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), - id: 'custom_threshold_active_alert_range_annotation', - }; - - const recoveredAlertRangeAnnotation: RangeEventAnnotationConfig = { - label: 'Alert duration', - type: 'manual', - key: { - type: 'range', - timestamp: alertStart!, - endTimestamp: alertEnd ?? '', - }, - color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(), - id: 'custom_threshold_recovered_alert_range_annotation', + id: `custom_threshold_${alertEnd ? 'recovered' : 'active'}_alert_range_annotation`, }; const annotations: EventAnnotationConfig[] = []; - annotations.push(alertStartAnnotation); - annotations.push(alertEnd ? recoveredAlertRangeAnnotation : activeAlertRangeAnnotation); + annotations.push(alertStartAnnotation, alertRangeAnnotation); useEffect(() => { const alertSummaryFields = [];