diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap
index 72381f6e62d63..af8a5b3d8e0a9 100644
--- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap
+++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap
@@ -3,37 +3,61 @@
exports[`AlertDetailsAppSection should render annotations 1`] = `
Array [
Object {
+ "additionalFilters": undefined,
"annotations": Array [
- ,
- ,
+ Object {
+ "color": "#BD271E",
+ "icon": "alert",
+ "id": "metric_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": "metric_threshold_active_alert_range_annotation",
+ "key": Object {
+ "endTimestamp": "2024-06-13T07:00:33.381Z",
+ "timestamp": "2023-03-28T13:40:00.000Z",
+ "type": "range",
+ },
+ "label": "Active alert",
+ "type": "manual",
+ },
],
- "chartType": "line",
- "expression": Object {
- "aggType": "count",
+ "chartOptions": Object {
+ "seriesType": "bar_stacked",
+ },
+ "dataView": "index",
+ "groupBy": Array [
+ "host.hostname",
+ ],
+ "metricExpression": Object {
"comparator": ">",
+ "metrics": Array [
+ Object {
+ "aggType": "count",
+ "field": "",
+ "name": "A",
+ },
+ ],
"threshold": Array [
2000,
],
"timeSize": 15,
"timeUnit": "m",
+ "warningComparator": undefined,
+ "warningThreshold": undefined,
+ },
+ "searchConfiguration": Object {
+ "query": Object {
+ "language": "",
+ "query": "",
+ },
},
- "filterQuery": undefined,
- "groupBy": Array [
- "host.hostname",
- ],
- "groupInstance": Array [
- "host-1",
- ],
- "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_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx
index eaf077684cf4d..5f8b99629eeb8 100644
--- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx
@@ -16,15 +16,35 @@ import {
buildMetricThresholdRule,
} from '../mocks/metric_threshold_rule';
import { AlertDetailsAppSection } from './alert_details_app_section';
-import { ExpressionChart } from './expression_chart';
+import { RuleConditionChart } from '@kbn/observability-plugin/public';
+import { lensPluginMock } from '@kbn/lens-plugin/public/mocks';
const mockedChartStartContract = chartPluginMock.createStartContract();
+const mockedLensStartContract = lensPluginMock.createStartContract();
+
+Date.now = jest.fn(() => new Date('2024-06-13T07:00:33.381Z').getTime());
+
+jest.mock('../../../containers/metrics_source', () => ({
+ useMetricsDataViewContext: () => ({
+ metricsView: { dataViewReference: 'index' },
+ }),
+ withSourceProvider:
+ (Component: React.FC) =>
+ () => {
+ return function ComponentWithSourceProvider(props: ComponentProps) {
+ return ;
+ };
+ },
+}));
jest.mock('@kbn/observability-alert-details', () => ({
AlertAnnotation: () => {},
AlertActiveTimeRangeAnnotation: () => {},
}));
-
+jest.mock('@kbn/observability-alert-details', () => ({
+ AlertAnnotation: () => {},
+ AlertActiveTimeRangeAnnotation: () => {},
+}));
jest.mock('@kbn/observability-get-padded-alert-time-range-util', () => ({
getPaddedAlertTimeRange: () => ({
from: '2023-03-28T10:43:13.802Z',
@@ -32,8 +52,9 @@ jest.mock('@kbn/observability-get-padded-alert-time-range-util', () => ({
}),
}));
-jest.mock('./expression_chart', () => ({
- ExpressionChart: jest.fn(() => ),
+jest.mock('@kbn/observability-plugin/public', () => ({
+ RuleConditionChart: jest.fn(() => ),
+ getGroupFilters: jest.fn(),
}));
jest.mock('../../../hooks/use_kibana', () => ({
@@ -41,6 +62,7 @@ jest.mock('../../../hooks/use_kibana', () => ({
services: {
...mockCoreMock.createStart(),
charts: mockedChartStartContract,
+ lens: mockedLensStartContract,
},
}),
}));
@@ -74,11 +96,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);
renderComponent();
- expect(mockedExpressionChart).toHaveBeenCalledTimes(3);
- expect(mockedExpressionChart.mock.calls[0]).toMatchSnapshot();
+ expect(mockedRuleConditionChart).toHaveBeenCalledTimes(3);
+ expect(mockedRuleConditionChart.mock.calls[0]).toMatchSnapshot();
});
});
diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx
index bee0035f210c1..78d908d85ad8c 100644
--- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx
@@ -15,29 +15,32 @@ import {
EuiPanel,
EuiSpacer,
EuiTitle,
+ transparentize,
useEuiTheme,
} from '@elastic/eui';
-import { AlertSummaryField, TopAlert } from '@kbn/observability-plugin/public';
+import chroma from 'chroma-js';
+
+import { AlertSummaryField, RuleConditionChart, TopAlert } from '@kbn/observability-plugin/public';
import { ALERT_END, ALERT_START, ALERT_EVALUATION_VALUES, ALERT_GROUP } from '@kbn/rule-data-utils';
-import { Rule } from '@kbn/alerting-plugin/common';
-import { AlertAnnotation, AlertActiveTimeRangeAnnotation } from '@kbn/observability-alert-details';
+import { Rule, RuleTypeParams } from '@kbn/alerting-plugin/common';
import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util';
+import type {
+ EventAnnotationConfig,
+ PointInTimeEventAnnotationConfig,
+ RangeEventAnnotationConfig,
+} from '@kbn/event-annotation-common';
+
+import { getGroupFilters } from '@kbn/observability-plugin/public';
+import type { GenericAggType } from '@kbn/observability-plugin/public';
import { metricValueFormatter } from '../../../../common/alerting/metrics/metric_value_formatter';
import { Threshold } from '../../common/components/threshold';
-import { withSourceProvider } from '../../../containers/metrics_source';
+import { useMetricsDataViewContext, withSourceProvider } from '../../../containers/metrics_source';
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';
+import { AlertParams } from '../types';
// 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 MetricThresholdRule = Rule;
interface Group {
field: string;
@@ -51,41 +54,49 @@ interface MetricThresholdAlertField {
export type MetricThresholdAlert = 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: MetricThresholdAlert;
rule: MetricThresholdRule;
setAlertSummaryFields: React.Dispatch>;
}
-export function AlertDetailsAppSection({ alert, rule }: AppSectionProps) {
- const { uiSettings, charts } = useKibanaContextForPlugin().services;
+export function AlertDetailsAppSection({ alert, rule, setAlertSummaryFields }: AppSectionProps) {
+ const { charts } = useKibanaContextForPlugin().services;
const { euiTheme } = useEuiTheme();
- const groupInstance = alert.fields[ALERT_GROUP]?.map((group: Group) => group.value);
-
+ const groups = alert.fields[ALERT_GROUP];
+ const { metricsView } = useMetricsDataViewContext();
const chartProps = {
baseTheme: charts.theme.useChartsBaseTheme(),
};
- const alertEnd = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).valueOf() : undefined;
- const annotations = [
- ,
- ,
- ];
+ const alertEnd = alert.fields[ALERT_END];
+ const alertStart = alert.fields[ALERT_START];
+
+ const alertStartAnnotation: PointInTimeEventAnnotationConfig = {
+ label: 'Alert',
+ type: 'manual',
+ key: {
+ type: 'point_in_time',
+ timestamp: alertStart!,
+ },
+ color: euiTheme.colors.danger,
+ icon: 'alert',
+ id: 'metric_threshold_alert_start_annotation',
+ };
+
+ const alertRangeAnnotation: RangeEventAnnotationConfig = {
+ label: `${alertEnd ? 'Alert duration' : 'Active alert'}`,
+ type: 'manual',
+ key: {
+ type: 'range',
+ timestamp: alertStart!,
+ endTimestamp: alertEnd ?? moment().toISOString(),
+ },
+ color: chroma(transparentize('#F04E981A', 0.2)).hex().toUpperCase(),
+ id: `metric_threshold_${alertEnd ? 'recovered' : 'active'}_alert_range_annotation`,
+ };
+
+ const annotations: EventAnnotationConfig[] = [];
+ annotations.push(alertStartAnnotation, alertRangeAnnotation);
return !!rule.params.criteria ? (
@@ -94,10 +105,25 @@ export function AlertDetailsAppSection({ alert, rule }: AppSectionProps) {
alert.fields[ALERT_START]!,
alert.fields[ALERT_END],
{
- size: criterion.timeSize,
- unit: criterion.timeUnit,
+ size: criterion.timeSize!,
+ unit: criterion.timeUnit!,
}
);
+ let metricExpression = [
+ {
+ aggType: criterion.aggType as GenericAggType,
+ name: String.fromCharCode('A'.charCodeAt(0) + index),
+ field: criterion.metric || '',
+ },
+ ];
+ if (criterion.customMetrics) {
+ metricExpression = criterion.customMetrics.map((metric) => ({
+ name: metric.name,
+ aggType: metric.aggType as GenericAggType,
+ field: metric.field || '',
+ filter: metric.filter,
+ }));
+ }
return (
@@ -135,16 +161,30 @@ export function AlertDetailsAppSection({ alert, rule }: AppSectionProps) {
/>
-
+ {metricsView && (
+
+ )}
diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx
index 191c6ed8cd847..9eaf5e2bd7b45 100644
--- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx
@@ -28,6 +28,7 @@ import {
} from '@kbn/triggers-actions-ui-plugin/public';
import { TimeUnitChar } from '@kbn/observability-plugin/common/utils/formatters/duration';
import { COMPARATORS } from '@kbn/alerting-comparators';
+import { GenericAggType, RuleConditionChart } from '@kbn/observability-plugin/public';
import { Aggregators, QUERY_INVALID } from '../../../../common/alerting/metrics';
import {
useMetricsDataViewContext,
@@ -40,7 +41,6 @@ import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer
import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options';
import { convertKueryToElasticSearchQuery } from '../../../utils/kuery';
import { AlertContextMeta, AlertParams, MetricExpression } from '../types';
-import { ExpressionChart } from './expression_chart';
import { ExpressionRow } from './expression_row';
const FILTER_TYPING_DEBOUNCE_MS = 500;
@@ -69,7 +69,6 @@ export const Expressions: React.FC = (props) => {
const { docLinks } = useKibanaContextForPlugin().services;
const { source } = useSourceContext();
const { metricsView } = useMetricsDataViewContext();
-
const [timeSize, setTimeSize] = useState(1);
const [timeUnit, setTimeUnit] = useState('m');
@@ -304,8 +303,24 @@ export const Expressions: React.FC = (props) => {
- {ruleParams.criteria &&
+ {metricsView &&
ruleParams.criteria.map((e, idx) => {
+ let metricExpression = [
+ {
+ aggType: e.aggType as GenericAggType,
+ // RuleConditionChart uses A,B,C etc in its parser to identify multiple conditions
+ name: String.fromCharCode('A'.charCodeAt(0) + idx),
+ field: e.metric || '',
+ },
+ ];
+ if (e.customMetrics) {
+ metricExpression = e.customMetrics.map((metric) => ({
+ name: metric.name,
+ aggType: metric.aggType as GenericAggType,
+ field: metric.field || '',
+ filter: metric.filter,
+ }));
+ }
return (
1) || false}
@@ -317,9 +332,26 @@ export const Expressions: React.FC = (props) => {
errors={(errors[idx] as IErrorObject) || emptyError}
expression={e || {}}
>
-
diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts
index 0ef6478ff12d7..f7ec9022b4cad 100644
--- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts
+++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts
@@ -87,6 +87,7 @@ export const buildMetricThresholdRule = (
filterQuery:
'{"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'],
+ sourceId: 'sourceId',
},
monitoring: {
run: {
diff --git a/x-pack/plugins/observability_solution/infra/tsconfig.json b/x-pack/plugins/observability_solution/infra/tsconfig.json
index d1d1f0542da0a..f23c9f7b30c34 100644
--- a/x-pack/plugins/observability_solution/infra/tsconfig.json
+++ b/x-pack/plugins/observability_solution/infra/tsconfig.json
@@ -63,7 +63,6 @@
"@kbn/shared-ux-router",
"@kbn/shared-ux-link-redirect-app",
"@kbn/discover-plugin",
- "@kbn/observability-alert-details",
"@kbn/observability-shared-plugin",
"@kbn/observability-ai-assistant-plugin",
"@kbn/ui-theme",
@@ -105,7 +104,8 @@
"@kbn/react-kibana-context-theme",
"@kbn/presentation-publishing",
"@kbn/presentation-containers",
- "@kbn/deeplinks-observability"
+ "@kbn/deeplinks-observability",
+ "@kbn/event-annotation-common"
],
"exclude": [
"target/**/*"
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 35104fd199d3a..a98a519a1606a 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
@@ -18,7 +18,7 @@ import {
buildCustomThresholdRule,
} from '../../mocks/custom_threshold_rule';
import { CustomThresholdAlertFields } from '../../types';
-import { RuleConditionChart } from '../rule_condition_chart/rule_condition_chart';
+import { RuleConditionChart } from '../../../rule_condition_chart/rule_condition_chart';
import { CustomThresholdAlert } from '../types';
import AlertDetailsAppSection from './alert_details_app_section';
@@ -47,7 +47,7 @@ jest.mock('@kbn/observability-get-padded-alert-time-range-util', () => ({
}),
}));
-jest.mock('../rule_condition_chart/rule_condition_chart', () => ({
+jest.mock('../../../rule_condition_chart/rule_condition_chart', () => ({
RuleConditionChart: jest.fn(() => ),
}));
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 3a065d6e06f3e..83aa8cf2a35d3 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
@@ -31,16 +31,16 @@ import type {
import moment from 'moment';
import { LOGS_EXPLORER_LOCATOR_ID, LogsExplorerLocatorParams } from '@kbn/deeplinks-observability';
import { TimeRange } from '@kbn/es-query';
+import { getGroupFilters } from '../../../../../common/custom_threshold_rule/helpers/get_group';
import { useLicense } from '../../../../hooks/use_license';
import { useKibana } from '../../../../utils/kibana_react';
-import { getGroupFilters } from '../../../../../common/custom_threshold_rule/helpers/get_group';
import { metricValueFormatter } from '../../../../../common/custom_threshold_rule/metric_value_formatter';
import { AlertSummaryField } from '../../../..';
import { AlertParams } from '../../types';
import { Threshold } from '../custom_threshold';
import { CustomThresholdRule, CustomThresholdAlert } from '../types';
import { LogRateAnalysis } from './log_rate_analysis';
-import { RuleConditionChart } from '../rule_condition_chart/rule_condition_chart';
+import { RuleConditionChart } from '../../../rule_condition_chart/rule_condition_chart';
import { getViewInAppUrl } from '../../../../../common/custom_threshold_rule/get_view_in_app_url';
import { SearchConfigurationWithExtractedReferenceType } from '../../../../../common/custom_threshold_rule/types';
import { generateChartTitleAndTooltip } from './helpers/generate_chart_title_and_tooltip';
diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx
index 02c428ccf3698..62580b3a89f82 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx
@@ -21,7 +21,7 @@ import Expressions from './custom_threshold_rule_expression';
import { AlertParams, CustomThresholdPrefillOptions } from './types';
jest.mock('../../utils/kibana_react');
-jest.mock('./components/rule_condition_chart/rule_condition_chart', () => ({
+jest.mock('../rule_condition_chart/rule_condition_chart', () => ({
RuleConditionChart: jest.fn(() => ),
}));
diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx
index fab9568b080a9..0db9c2f048e11 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx
+++ b/x-pack/plugins/observability_solution/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 as PreviewChart } from './components/rule_condition_chart/rule_condition_chart';
+import { RuleConditionChart as PreviewChart } from '../rule_condition_chart/rule_condition_chart';
import { getSearchConfiguration } from './helpers/get_search_configuration';
const FILTER_TYPING_DEBOUNCE_MS = 500;
diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.test.ts b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.test.ts
similarity index 98%
rename from x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.test.ts
rename to x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.test.ts
index 4211907b5d4a0..044b57c64da28 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.test.ts
+++ b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.test.ts
@@ -7,7 +7,7 @@
import {
Aggregators,
CustomThresholdExpressionMetric,
-} from '../../../../../common/custom_threshold_rule/types';
+} from '../../../common/custom_threshold_rule/types';
import { getBufferThreshold, getLensOperationFromRuleMetric, lensFieldFormatter } from './helpers';
const useCases = [
[
diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.ts b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.ts
similarity index 85%
rename from x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.ts
rename to x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.ts
index 1875af6ceb93e..7cedf19d0b660 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/helpers.ts
+++ b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/helpers.ts
@@ -5,12 +5,10 @@
* 2.0.
*/
-import {
- Aggregators,
- CustomThresholdExpressionMetric,
-} from '../../../../../common/custom_threshold_rule/types';
+import { Aggregators } from '../../../common/custom_threshold_rule/types';
+import { GenericMetric } from './rule_condition_chart';
-export const getLensOperationFromRuleMetric = (metric: CustomThresholdExpressionMetric): string => {
+export const getLensOperationFromRuleMetric = (metric: GenericMetric): string => {
const { aggType, field, filter } = metric;
let operation: string = aggType;
const operationArgs: string[] = [];
@@ -56,7 +54,7 @@ export const LensFieldFormat = {
} as const;
export const lensFieldFormatter = (
- metrics: CustomThresholdExpressionMetric[]
+ metrics: GenericMetric[]
): typeof LensFieldFormat[keyof typeof LensFieldFormat] => {
if (metrics.length < 1 || !metrics[0].field) return LensFieldFormat.NUMBER;
const firstMetricField = metrics[0].field;
@@ -65,5 +63,5 @@ export const lensFieldFormatter = (
return LensFieldFormat.NUMBER;
};
-export const isRate = (metrics: CustomThresholdExpressionMetric[]): boolean =>
+export const isRate = (metrics: GenericMetric[]): boolean =>
Boolean(metrics.length > 0 && metrics[0].aggType === Aggregators.RATE);
diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/painless_tinymath_parser.test.ts b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.test.ts
similarity index 100%
rename from x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/painless_tinymath_parser.test.ts
rename to x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.test.ts
diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/painless_tinymath_parser.ts b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.ts
similarity index 100%
rename from x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/painless_tinymath_parser.ts
rename to x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/painless_tinymath_parser.ts
diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.test.tsx
similarity index 78%
rename from x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx
rename to x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.test.tsx
index d164e6670b4a7..ac0624265be0b 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.test.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.test.tsx
@@ -13,13 +13,12 @@ import { COMPARATORS } from '@kbn/alerting-comparators';
import {
Aggregators,
CustomThresholdSearchSourceFields,
-} from '../../../../../common/custom_threshold_rule/types';
-import { useKibana } from '../../../../utils/kibana_react';
-import { kibanaStartMock } from '../../../../utils/kibana_react.mock';
-import { MetricExpression } from '../../types';
-import { RuleConditionChart } from './rule_condition_chart';
+} from '../../../common/custom_threshold_rule/types';
+import { useKibana } from '../../utils/kibana_react';
+import { kibanaStartMock } from '../../utils/kibana_react.mock';
+import { RuleConditionChart, RuleConditionChartExpressions } from './rule_condition_chart';
-jest.mock('../../../../utils/kibana_react');
+jest.mock('../../utils/kibana_react');
const useKibanaMock = useKibana as jest.Mock;
@@ -34,7 +33,7 @@ describe('Rule condition chart', () => {
jest.clearAllMocks();
mockKibana();
});
- async function setup(expression: MetricExpression, dataView?: DataView) {
+ async function setup(expression: RuleConditionChartExpressions, dataView?: DataView) {
const wrapper = mountWithIntl(
{
}
it('should display no data message', async () => {
- const expression: MetricExpression = {
+ const expression: RuleConditionChartExpressions = {
metrics: [
{
name: 'A',
@@ -67,7 +66,6 @@ describe('Rule condition chart', () => {
],
timeSize: 1,
timeUnit: 'm',
- sourceId: 'default',
threshold: [1],
comparator: COMPARATORS.GREATER_THAN_OR_EQUALS,
};
diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.tsx
similarity index 71%
rename from x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx
rename to x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.tsx
index 1e326e3fa5f6d..a8710004876ff 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/components/rule_condition_chart/rule_condition_chart.tsx
@@ -26,10 +26,12 @@ import { i18n } from '@kbn/i18n';
import { TimeRange } from '@kbn/es-query';
import { EventAnnotationConfig } from '@kbn/event-annotation-common';
import { COMPARATORS } from '@kbn/alerting-comparators';
-import { EventsAsUnit } from '../../../../../common/constants';
-import { CustomThresholdSearchSourceFields } from '../../../../../common/custom_threshold_rule/types';
-import { useKibana } from '../../../../utils/kibana_react';
-import { MetricExpression } from '../../types';
+import { SerializedSearchSourceFields } from '@kbn/data-plugin/common';
+import { TimeUnitChar } from '../../../common';
+import { LEGACY_COMPARATORS } from '../../../common/utils/convert_legacy_outside_comparator';
+import { EventsAsUnit } from '../../../common/constants';
+import { Aggregators } from '../../../common/custom_threshold_rule/types';
+import { useKibana } from '../../utils/kibana_react';
import { AggMap, PainlessTinyMathParser } from './painless_tinymath_parser';
import {
lensFieldFormatter,
@@ -38,15 +40,38 @@ import {
isRate,
LensFieldFormat,
} from './helpers';
-
interface ChartOptions {
seriesType?: SeriesType;
interval?: string;
}
+interface GenericSearchSourceFields extends SerializedSearchSourceFields {
+ query?: Query;
+ filter?: Array>;
+}
+
+export type GenericAggType = Aggregators | 'custom';
+
+export interface GenericMetric {
+ aggType: GenericAggType;
+ name: string;
+ field?: string;
+ filter?: string;
+}
+
+export interface RuleConditionChartExpressions {
+ metrics: GenericMetric[];
+ threshold: number[];
+ comparator: COMPARATORS | LEGACY_COMPARATORS;
+ warningThreshold?: number[];
+ warningComparator?: COMPARATORS | LEGACY_COMPARATORS;
+ timeSize?: number;
+ timeUnit?: TimeUnitChar;
+ equation?: string;
+}
interface RuleConditionChartProps {
- metricExpression: MetricExpression;
- searchConfiguration: CustomThresholdSearchSourceFields;
+ metricExpression: RuleConditionChartExpressions;
+ searchConfiguration: GenericSearchSourceFields;
dataView?: DataView;
groupBy?: string | string[];
error?: IErrorObject;
@@ -76,11 +101,22 @@ export function RuleConditionChart({
services: { lens },
} = useKibana();
const { euiTheme } = useEuiTheme();
- const { metrics, timeSize, timeUnit, threshold, comparator, equation } = metricExpression;
+ const {
+ metrics,
+ timeSize,
+ timeUnit,
+ threshold,
+ comparator,
+ equation,
+ warningComparator,
+ warningThreshold,
+ } = metricExpression;
const [attributes, setAttributes] = useState();
const [aggMap, setAggMap] = useState();
const [formula, setFormula] = useState('');
const [thresholdReferenceLine, setThresholdReferenceLine] = useState();
+ const [warningThresholdReferenceLine, setWarningThresholdReferenceLine] =
+ useState();
const [alertAnnotation, setAlertAnnotation] = useState();
const [chartLoading, setChartLoading] = useState(false);
const filters = [...(searchConfiguration.filter || []), ...additionalFilters];
@@ -98,13 +134,13 @@ export function RuleConditionChart({
const paragraphElements = errorDiv.querySelectorAll('p');
if (!paragraphElements || paragraphElements.length < 2) return;
paragraphElements[0].innerText = i18n.translate(
- 'xpack.observability.customThreshold.rule..charts.error_equation.title',
+ 'xpack.observability.ruleCondition.chart.error_equation.title',
{
defaultMessage: 'An error occurred while rendering the chart',
}
);
paragraphElements[1].innerText = i18n.translate(
- 'xpack.observability.customThreshold.rule..charts.error_equation.description',
+ 'xpack.observability.ruleCondition.chart.error_equation.description',
{
defaultMessage: 'Check the rule equation.',
}
@@ -113,6 +149,77 @@ export function RuleConditionChart({
});
}, [chartLoading, attributes]);
+ // Build the warning threshold reference line
+ useEffect(() => {
+ if (!warningThreshold) {
+ if (warningThresholdReferenceLine?.length) {
+ setWarningThresholdReferenceLine([]);
+ }
+ return;
+ }
+ const refLayers = [];
+ if (
+ warningComparator === COMPARATORS.NOT_BETWEEN ||
+ (warningComparator === COMPARATORS.BETWEEN && warningThreshold.length === 2)
+ ) {
+ const refLineStart = new XYReferenceLinesLayer({
+ data: [
+ {
+ value: (warningThreshold[0] || 0).toString(),
+ color: euiTheme.colors.warning,
+ fill: warningComparator === COMPARATORS.NOT_BETWEEN ? 'below' : 'none',
+ },
+ ],
+ });
+ const refLineEnd = new XYReferenceLinesLayer({
+ data: [
+ {
+ value: (warningThreshold[1] || 0).toString(),
+ color: euiTheme.colors.warning,
+ fill: warningComparator === COMPARATORS.NOT_BETWEEN ? 'above' : 'none',
+ },
+ ],
+ });
+
+ refLayers.push(refLineStart, refLineEnd);
+ } else {
+ let fill: FillStyle = 'above';
+ if (
+ warningComparator === COMPARATORS.LESS_THAN ||
+ warningComparator === COMPARATORS.LESS_THAN_OR_EQUALS
+ ) {
+ fill = 'below';
+ }
+ const warningThresholdRefLine = new XYReferenceLinesLayer({
+ data: [
+ {
+ value: (warningThreshold[0] || 0).toString(),
+ color: euiTheme.colors.warning,
+ fill,
+ },
+ ],
+ });
+ // A transparent line to add extra buffer at the top of threshold
+ const bufferRefLine = new XYReferenceLinesLayer({
+ data: [
+ {
+ value: getBufferThreshold(warningThreshold[0]),
+ color: 'transparent',
+ fill,
+ },
+ ],
+ });
+ refLayers.push(warningThresholdRefLine, bufferRefLine);
+ }
+ setWarningThresholdReferenceLine(refLayers);
+ }, [
+ warningThreshold,
+ warningComparator,
+ euiTheme.colors.warning,
+ metrics,
+ warningThresholdReferenceLine?.length,
+ ]);
+
// Build the threshold reference line
useEffect(() => {
if (!threshold) return;
@@ -225,7 +332,7 @@ export function RuleConditionChart({
const baseLayer = {
type: 'formula',
value: formula,
- label: 'Custom Threshold',
+ label: formula,
groupBy,
format: {
id: formatId,
@@ -272,6 +379,9 @@ export function RuleConditionChart({
const layers: Array = [
xyDataLayer,
];
+ if (warningThresholdReferenceLine) {
+ layers.push(...warningThresholdReferenceLine);
+ }
if (thresholdReferenceLine) {
layers.push(...thresholdReferenceLine);
}
@@ -311,13 +421,14 @@ export function RuleConditionChart({
timeSize,
timeUnit,
seriesType,
+ warningThresholdReferenceLine,
]);
if (
!dataView ||
!attributes ||
error?.equation ||
- Object.keys(error?.metrics || {}).length !== 0 ||
+ Object.keys(error?.metrics || error?.metric || {}).length !== 0 ||
!timeSize ||
!timeRange
) {
@@ -329,7 +440,7 @@ export function RuleConditionChart({
data-test-subj="thresholdRuleNoChartData"
body={
);
}
-
return (