[AO] Metric threshold alert details - custom time range and alert start annotation (#153954)

Closes #153202, closes #153850

## Summary

This PR adds alert start annotation and also uses a custom time range
for the alert details' charts depending on the alert duration. The logic
to calculate the time range was added in a separate package to be used
in other use cases as well.


![image](https://user-images.githubusercontent.com/12370520/228583927-bf90cc13-53d5-4824-9b3b-ed6e6ffd06f5.png)

## 🧪 How to test

Create a metric threshold alert and go to the related alert details
page, verify:
- Alert start annotation
- The time range of the charts should be before the alert was started
(1/8 of the duration was added to each side)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Maryam Saeidi 2023-04-04 18:08:27 +02:00 committed by GitHub
parent ee72588faa
commit 6350e146fa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 418 additions and 30 deletions

1
.github/CODEOWNERS vendored
View file

@ -471,6 +471,7 @@ src/plugins/newsfeed @elastic/kibana-core
test/common/plugins/newsfeed @elastic/kibana-core
x-pack/plugins/notifications @elastic/appex-sharedux
packages/kbn-object-versioning @elastic/appex-sharedux
x-pack/packages/observability/alert_details @elastic/actionable-observability
x-pack/test/cases_api_integration/common/plugins/observability @elastic/response-ops
x-pack/plugins/observability @elastic/actionable-observability
x-pack/test/security_api_integration/plugins/oidc_provider @elastic/kibana-security

View file

@ -18,6 +18,7 @@
"controls": "src/plugins/controls",
"data": "src/plugins/data",
"ecsDataQualityDashboard": "x-pack/packages/kbn-ecs-data-quality-dashboard",
"observabilityAlertDetails": "x-pack/packages/observability/alert_details",
"dataViews": "src/plugins/data_views",
"devTools": "src/plugins/dev_tools",
"discover": "src/plugins/discover",

View file

@ -486,6 +486,7 @@
"@kbn/newsfeed-test-plugin": "link:test/common/plugins/newsfeed",
"@kbn/notifications-plugin": "link:x-pack/plugins/notifications",
"@kbn/object-versioning": "link:packages/kbn-object-versioning",
"@kbn/observability-alert-details": "link:x-pack/packages/observability/alert_details",
"@kbn/observability-fixtures-plugin": "link:x-pack/test/cases_api_integration/common/plugins/observability",
"@kbn/observability-plugin": "link:x-pack/plugins/observability",
"@kbn/oidc-provider-plugin": "link:x-pack/test/security_api_integration/plugins/oidc_provider",

View file

@ -936,6 +936,8 @@
"@kbn/notifications-plugin/*": ["x-pack/plugins/notifications/*"],
"@kbn/object-versioning": ["packages/kbn-object-versioning"],
"@kbn/object-versioning/*": ["packages/kbn-object-versioning/*"],
"@kbn/observability-alert-details": ["x-pack/packages/observability/alert_details"],
"@kbn/observability-alert-details/*": ["x-pack/packages/observability/alert_details/*"],
"@kbn/observability-fixtures-plugin": ["x-pack/test/cases_api_integration/common/plugins/observability"],
"@kbn/observability-fixtures-plugin/*": ["x-pack/test/cases_api_integration/common/plugins/observability/*"],
"@kbn/observability-plugin": ["x-pack/plugins/observability"],

View file

@ -0,0 +1,3 @@
# @kbn/observability-alert-details
Provides alert details related helpers and components

View file

@ -0,0 +1,9 @@
/*
* 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 { AlertAnnotation } from './src/components/alert_annotation';
export { getAlertTimeRange } from './src/helpers/get_alert_time_range';

View file

@ -0,0 +1,12 @@
/*
* 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.
*/
module.exports = {
preset: '@kbn/test/jest_node',
rootDir: '../../../..',
roots: ['<rootDir>/x-pack/packages/observability/alert_details'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/observability-alert-details",
"owner": "@elastic/actionable-observability"
}

View file

@ -0,0 +1,8 @@
{
"name": "@kbn/observability-alert-details",
"descriptio": "Helper and components related to alert details",
"author": "Actionable Observability",
"private": true,
"version": "1.0.0",
"license": "Elastic License 2.0"
}

View file

@ -0,0 +1,48 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import moment from 'moment';
import { i18n } from '@kbn/i18n';
import { AnnotationDomainType, LineAnnotation, Position } from '@elastic/charts';
import { EuiIcon } from '@elastic/eui';
interface Props {
alertStart: number;
color: string;
dateFormat: string;
id: string;
}
const ANNOTATION_TITLE = i18n.translate('observabilityAlertDetails.alertAnnotation.title', {
defaultMessage: 'Alert started',
});
export function AlertAnnotation({ alertStart, color, dateFormat, id }: Props) {
return (
<LineAnnotation
id={id}
domainType={AnnotationDomainType.XDomain}
dataValues={[
{
dataValue: alertStart,
header: moment(alertStart).format(dateFormat),
details: ANNOTATION_TITLE,
},
]}
style={{
line: {
strokeWidth: 3,
stroke: color,
opacity: 1,
},
}}
marker={<EuiIcon type="warning" color={color} />}
markerPosition={Position.Top}
/>
);
}

View file

@ -0,0 +1,61 @@
/*
* 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 { getAlertTimeRange } from './get_alert_time_range';
describe('getAlertTimeRange', () => {
const mockedDate = '2023-03-28T09:22:32.660Z';
const mockDate = jest
.spyOn(global.Date, 'now')
.mockImplementation(() => new Date(mockedDate).valueOf());
afterAll(() => mockDate.mockRestore());
const testData: any[] = [
// Description, Start, End, Output
[
'Duration 4 hour, time range will be extended it with 30 minutes from each side',
'2023-03-28T04:15:32.660Z',
'2023-03-28T08:15:32.660Z',
{ from: '2023-03-28T03:45:32.660Z', to: '2023-03-28T08:45:32.660Z' },
],
[
'Duration 5 minutes, time range will be extended it with 20 minutes from each side',
'2023-03-28T08:22:33.660Z',
'2023-03-28T08:27:33.660Z',
{ from: '2023-03-28T08:02:33.660Z', to: '2023-03-28T08:47:33.660Z' },
],
];
it.each(testData)('%s', (_, start, end, output) => {
expect(getAlertTimeRange(start, end)).toEqual(output);
});
describe('active alert', () => {
it('without end time', () => {
// Duration 5 hours
const start = '2023-03-28T04:22:32.660Z';
const output = {
// Time range is from 37.5 minutes (duration/8) before start
from: '2023-03-28T03:45:02.660Z',
to: mockedDate,
};
expect(getAlertTimeRange(start)).toEqual(output);
});
it('with end time than 10 minutes before now', () => {
const start = '2023-03-28T05:17:32.660Z';
// 5 minutes before now, duration 4 hours
const end = '2023-03-28T09:17:32.660Z';
const output = {
// Time range is from 30 minutes (duration/8) before start
from: '2023-03-28T04:47:32.660Z',
to: mockedDate,
};
expect(getAlertTimeRange(start, end)).toEqual(output);
});
});
});

View file

@ -0,0 +1,34 @@
/*
* 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 moment from 'moment';
export interface TimeRange {
from?: string;
to?: string;
interval?: string;
}
export const getAlertTimeRange = (alertStart: string, alertEnd?: string): TimeRange => {
const alertDuration = moment.duration(moment(alertEnd).diff(moment(alertStart)));
const now = moment().toISOString();
const durationMs =
alertDuration.asMinutes() < 160
? moment.duration(20, 'minutes').asMilliseconds()
: alertDuration.asMilliseconds() / 8;
const from = moment(alertStart).subtract(durationMs, 'millisecond').toISOString();
const to =
alertEnd && moment(alertEnd).add(durationMs, 'millisecond').isBefore(now)
? moment(alertEnd).add(durationMs, 'millisecond').toISOString()
: now;
return {
from,
to,
};
};

View file

@ -0,0 +1,21 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": [
"@kbn/i18n"
]
}

View file

@ -0,0 +1,42 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AlertDetailsAppSection should render annotation 1`] = `
Array [
Object {
"annotations": Array [
<AlertAnnotation
alertStart={1678716383695}
color="#BD271E"
dateFormat="YYYY-MM-DD HH:mm"
id="annotation_alert_start"
/>,
],
"chartType": "line",
"derivedIndexPattern": Object {
"fields": Array [],
"title": "metricbeat-*",
},
"expression": Object {
"aggType": "count",
"comparator": ">",
"threshold": Array [
2000,
],
"timeSize": 15,
"timeUnit": "m",
},
"filterQuery": undefined,
"groupBy": Array [
"host.hostname",
],
"source": Object {
"id": "default",
},
"timeRange": Object {
"from": "2023-03-28T10:43:13.802Z",
"to": "2023-03-29T13:14:09.581Z",
},
},
Object {},
]
`;

View file

@ -5,13 +5,26 @@
* 2.0.
*/
import { coreMock as mockCoreMock } from '@kbn/core/public/mocks';
import React from 'react';
import { coreMock as mockCoreMock } from '@kbn/core/public/mocks';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { render } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { buildMetricThresholdRule } from '../mocks/metric_threshold_rule';
import {
buildMetricThresholdAlert,
buildMetricThresholdRule,
} from '../mocks/metric_threshold_rule';
import { AlertDetailsAppSection } from './alert_details_app_section';
import { ExpressionChart } from './expression_chart';
jest.mock('@kbn/observability-alert-details', () => ({
AlertAnnotation: () => {},
getAlertTimeRange: () => ({ from: '2023-03-28T10:43:13.802Z', to: '2023-03-29T13:14:09.581Z' }),
}));
jest.mock('./expression_chart', () => ({
ExpressionChart: jest.fn(() => <div data-test-subj="ExpressionChart" />),
}));
jest.mock('../../../hooks/use_kibana', () => ({
useKibanaContextForPlugin: () => ({
@ -33,15 +46,27 @@ describe('AlertDetailsAppSection', () => {
return render(
<IntlProvider locale="en">
<QueryClientProvider client={queryClient}>
<AlertDetailsAppSection rule={buildMetricThresholdRule()} />
<AlertDetailsAppSection
alert={buildMetricThresholdAlert()}
rule={buildMetricThresholdRule()}
/>
</QueryClientProvider>
</IntlProvider>
);
};
it('should render rule data correctly', async () => {
it('should render rule data', async () => {
const result = renderComponent();
expect((await result.findByTestId('metricThresholdAppSection')).children.length).toBe(3);
});
it('should render annotation', async () => {
const mockedExpressionChart = jest.fn(() => <div data-test-subj="ExpressionChart" />);
(ExpressionChart as jest.Mock).mockImplementation(mockedExpressionChart);
renderComponent();
expect(mockedExpressionChart).toHaveBeenCalledTimes(3);
expect(mockedExpressionChart.mock.calls[0]).toMatchSnapshot();
});
});

View file

@ -5,32 +5,55 @@
* 2.0.
*/
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
import React, { useMemo } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiPanel, useEuiTheme } from '@elastic/eui';
import { TopAlert } from '@kbn/observability-plugin/public';
import { ALERT_END, ALERT_START } from '@kbn/rule-data-utils';
import { Rule } from '@kbn/alerting-plugin/common';
import { AlertAnnotation, getAlertTimeRange } from '@kbn/observability-alert-details';
import { useSourceContext, withSourceProvider } from '../../../containers/metrics_source';
import { MetricThresholdRuleTypeParams } from '..';
import { generateUniqueKey } from '../lib/generate_unique_key';
import { MetricsExplorerChartType } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
import { MetricThresholdRuleTypeParams } from '..';
import { ExpressionChart } from './expression_chart';
// TODO Use a generic props for app sections https://github.com/elastic/kibana/issues/152690
export type MetricThresholdRule = Rule<
MetricThresholdRuleTypeParams & {
filterQueryText?: string;
groupBy?: string | string[];
}
>;
export type MetricThresholdAlert = TopAlert;
const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm';
const ALERT_START_ANNOTATION_ID = 'annotation_alert_start';
interface AppSectionProps {
rule: Rule<
MetricThresholdRuleTypeParams & {
filterQueryText?: string;
groupBy?: string | string[];
}
>;
rule: MetricThresholdRule;
alert: MetricThresholdAlert;
}
export function AlertDetailsAppSection({ rule }: AppSectionProps) {
export function AlertDetailsAppSection({ alert, rule }: AppSectionProps) {
const { uiSettings } = useKibanaContextForPlugin().services;
const { source, createDerivedIndexPattern } = useSourceContext();
const { euiTheme } = useEuiTheme();
const derivedIndexPattern = useMemo(
() => createDerivedIndexPattern(),
[createDerivedIndexPattern]
);
const timeRange = getAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]);
const annotations = [
<AlertAnnotation
key={ALERT_START_ANNOTATION_ID}
alertStart={alert.start}
color={euiTheme.colors.danger}
dateFormat={uiSettings.get('dateFormat') || DEFAULT_DATE_FORMAT}
id={ALERT_START_ANNOTATION_ID}
/>,
];
return !!rule.params.criteria ? (
<EuiFlexGroup direction="column" data-test-subj="metricThresholdAppSection">
@ -44,6 +67,8 @@ export function AlertDetailsAppSection({ rule }: AppSectionProps) {
filterQuery={rule.params.filterQueryText}
groupBy={rule.params.groupBy}
chartType={MetricsExplorerChartType.line}
timeRange={timeRange}
annotations={annotations}
/>
</EuiPanel>
</EuiFlexItem>

View file

@ -5,10 +5,11 @@
* 2.0.
*/
import React, { ReactElement } from 'react';
import { act } from 'react-dom/test-utils';
import { LineAnnotation, RectAnnotation } from '@elastic/charts';
import { DataViewBase } from '@kbn/es-query';
import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers';
import React from 'react';
import { act } from 'react-dom/test-utils';
// We are using this inside a `jest.mock` call. Jest requires dynamic dependencies to be prefixed with `mock`
import { coreMock as mockCoreMock } from '@kbn/core/public/mocks';
import { Aggregators, Comparator } from '../../../../common/alerting/metrics';
@ -38,7 +39,12 @@ jest.mock('../hooks/use_metrics_explorer_chart_data', () => ({
}));
describe('ExpressionChart', () => {
async function setup(expression: MetricExpression, filterQuery?: string, groupBy?: string) {
async function setup(
expression: MetricExpression,
filterQuery?: string,
groupBy?: string,
annotations?: Array<ReactElement<typeof RectAnnotation | typeof LineAnnotation>>
) {
const derivedIndexPattern: DataViewBase = {
title: 'metricbeat-*',
fields: [],
@ -64,6 +70,7 @@ describe('ExpressionChart', () => {
source={source}
filterQuery={filterQuery}
groupBy={groupBy}
annotations={annotations}
/>
);

View file

@ -5,8 +5,16 @@
* 2.0.
*/
import React from 'react';
import { Axis, Chart, niceTimeFormatter, Position, Settings } from '@elastic/charts';
import React, { ReactElement } from 'react';
import {
Axis,
Chart,
LineAnnotation,
niceTimeFormatter,
Position,
RectAnnotation,
Settings,
} from '@elastic/charts';
import { EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { DataViewBase } from '@kbn/es-query';
@ -16,7 +24,7 @@ import { MetricsSourceConfiguration } from '../../../../common/metrics_sources';
import { Color } from '../../../../common/color_palette';
import { MetricsExplorerRow, MetricsExplorerAggregation } from '../../../../common/http_api';
import { MetricExplorerSeriesChart } from '../../../pages/metrics/metrics_explorer/components/series_chart';
import { MetricExpression } from '../types';
import { MetricExpression, TimeRange } from '../types';
import {
MetricsExplorerChartType,
MetricsExplorerOptionsMetric,
@ -44,6 +52,8 @@ interface Props {
filterQuery?: string;
groupBy?: string | string[];
chartType?: MetricsExplorerChartType;
timeRange?: TimeRange;
annotations?: Array<ReactElement<typeof RectAnnotation | typeof LineAnnotation>>;
}
export const ExpressionChart: React.FC<Props> = ({
@ -53,6 +63,8 @@ export const ExpressionChart: React.FC<Props> = ({
filterQuery,
groupBy,
chartType = MetricsExplorerChartType.bar,
timeRange,
annotations,
}) => {
const { uiSettings } = useKibanaContextForPlugin().services;
@ -61,7 +73,8 @@ export const ExpressionChart: React.FC<Props> = ({
derivedIndexPattern,
source,
filterQuery,
groupBy
groupBy,
timeRange
);
if (isLoading) {
@ -158,6 +171,7 @@ export const ExpressionChart: React.FC<Props> = ({
domain={domain}
/>
)}
{annotations}
<Axis
id={'timestamp'}
position={Position.Bottom}

View file

@ -10,7 +10,7 @@ import { DataViewBase } from '@kbn/es-query';
import { useMemo } from 'react';
import { MetricExpressionCustomMetric } from '../../../../common/alerting/metrics';
import { MetricsSourceConfiguration } from '../../../../common/metrics_sources';
import { MetricExpression } from '../types';
import { MetricExpression, TimeRange } from '../types';
import {
MetricsExplorerOptions,
MetricsExplorerTimestampsRT,
@ -18,12 +18,15 @@ import {
import { useMetricsExplorerData } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data';
import { MetricExplorerCustomMetricAggregations } from '../../../../common/http_api/metrics_explorer';
const DEFAULT_TIME_RANGE = {};
export const useMetricsExplorerChartData = (
expression: MetricExpression,
derivedIndexPattern: DataViewBase,
source?: MetricsSourceConfiguration,
filterQuery?: string,
groupBy?: string | string[]
groupBy?: string | string[],
timeRange: TimeRange = DEFAULT_TIME_RANGE
) => {
const { timeSize, timeUnit } = expression || { timeSize: 1, timeUnit: 'm' };
@ -57,8 +60,8 @@ export const useMetricsExplorerChartData = (
]
);
const timestamps: MetricsExplorerTimestampsRT = useMemo(() => {
const from = `now-${(timeSize || 1) * 20}${timeUnit}`;
const to = 'now';
const from = timeRange.from ?? `now-${(timeSize || 1) * 20}${timeUnit}`;
const to = timeRange.to ?? 'now';
const fromTimestamp = DateMath.parse(from)!.valueOf();
const toTimestamp = DateMath.parse(to, { roundUp: true })!.valueOf();
return {
@ -66,7 +69,7 @@ export const useMetricsExplorerChartData = (
fromTimestamp,
toTimestamp,
};
}, [timeSize, timeUnit]);
}, [timeRange, timeSize, timeUnit]);
return useMetricsExplorerData(options, source?.configuration, derivedIndexPattern, timestamps);
};

View file

@ -5,14 +5,13 @@
* 2.0.
*/
import { Rule } from '@kbn/alerting-plugin/common';
import { v4 as uuidv4 } from 'uuid';
import { Aggregators, Comparator } from '../../../../common/alerting/metrics';
import { MetricThresholdRuleTypeParams } from '..';
import { MetricThresholdAlert, MetricThresholdRule } from '../components/alert_details_app_section';
export const buildMetricThresholdRule = (
rule: Partial<Rule<MetricThresholdRuleTypeParams>> = {}
): Rule<MetricThresholdRuleTypeParams> => {
rule: Partial<MetricThresholdRule> = {}
): MetricThresholdRule => {
return {
alertTypeId: 'metrics.alert.threshold',
createdBy: 'admin',
@ -85,7 +84,7 @@ export const buildMetricThresholdRule = (
},
],
filterQuery:
'{"bool":{"filter":[{"bool":{"should":[{"term":{"host.hostname":{"value":"Maryams-MacBook-Pro.local"}}}],"minimum_should_match":1}},{"bool":{"should":[{"term":{"service.type":{"value":"system"}}}],"minimum_should_match":1}}]}}',
'{"bool":{"filter":[{"bool":{"should":[{"term":{"host.hostname":{"value":"Users-System.local"}}}],"minimum_should_match":1}},{"bool":{"should":[{"term":{"service.type":{"value":"system"}}}],"minimum_should_match":1}}]}}',
groupBy: ['host.hostname'],
},
monitoring: {
@ -120,3 +119,60 @@ export const buildMetricThresholdRule = (
...rule,
};
};
export const buildMetricThresholdAlert = (
alert: Partial<MetricThresholdAlert> = {}
): MetricThresholdAlert => {
return {
link: '/app/metrics/explorer',
reason: 'system.cpu.user.pct reported no data in the last 1m for ',
fields: {
'kibana.alert.rule.parameters': {
criteria: [
{
aggType: 'avg',
comparator: '>',
threshold: [0.1],
timeSize: 1,
timeUnit: 'm',
metric: 'system.cpu.user.pct',
},
],
sourceId: 'default',
alertOnNoData: true,
alertOnGroupDisappear: true,
},
'kibana.alert.rule.category': 'Metric threshold',
'kibana.alert.rule.consumer': 'alerts',
'kibana.alert.rule.execution.uuid': '62dd07ef-ead9-4b1f-a415-7c83d03925f7',
'kibana.alert.rule.name': 'One condition',
'kibana.alert.rule.producer': 'infrastructure',
'kibana.alert.rule.rule_type_id': 'metrics.alert.threshold',
'kibana.alert.rule.uuid': '3a1ed8c0-c1a8-11ed-9249-ed6d75986bdc',
'kibana.space_ids': ['default'],
'kibana.alert.rule.tags': [],
'@timestamp': '2023-03-28T14:40:00.000Z',
'kibana.alert.reason': 'system.cpu.user.pct reported no data in the last 1m for ',
'kibana.alert.action_group': 'metrics.threshold.nodata',
tags: [],
'kibana.alert.duration.us': 248391946000,
'kibana.alert.time_range': {
gte: '2023-03-13T14:06:23.695Z',
},
'kibana.alert.instance.id': '*',
'kibana.alert.start': '2023-03-28T13:40:00.000Z',
'kibana.alert.uuid': '50faddcd-c0a0-4122-a068-c204f4a7ec87',
'kibana.alert.status': 'active',
'kibana.alert.workflow_status': 'open',
'event.kind': 'signal',
'event.action': 'active',
'kibana.version': '8.8.0',
'kibana.alert.flapping': false,
'kibana.alert.rule.revision': 1,
},
active: true,
start: 1678716383695,
lastUpdated: 1678964775641,
...alert,
};
};

View file

@ -58,6 +58,11 @@ export interface ExpressionChartRow {
export type ExpressionChartSeries = ExpressionChartRow[][];
export interface TimeRange {
from?: string;
to?: string;
}
export interface AlertParams {
criteria: MetricExpression[];
groupBy?: string | string[];

View file

@ -59,6 +59,7 @@
"@kbn/shared-ux-prompt-not-found",
"@kbn/shared-ux-router",
"@kbn/shared-ux-link-redirect-app",
"@kbn/observability-alert-details",
"@kbn/actions-plugin",
"@kbn/ui-theme"
],

View file

@ -4609,6 +4609,10 @@
version "0.0.0"
uid ""
"@kbn/observability-alert-details@link:x-pack/packages/observability/alert_details":
version "0.0.0"
uid ""
"@kbn/observability-fixtures-plugin@link:x-pack/test/cases_api_integration/common/plugins/observability":
version "0.0.0"
uid ""