mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Actionable Observability] - Add latency alert history chart on the Alert details page for APM (#148011)
## Summary Closes #147932 by adding a new latency chart that covers the last 30 days of alerts for a given rule. And it adds annotations with the number of alerts for a given day besides the time to recover. <img width="1196" alt="Screenshot 2023-01-18 at 16 22 08" src="https://user-images.githubusercontent.com/6838659/213211368-1416e620-e96c-4a98-9552-9397fc37ee1e.png">
This commit is contained in:
parent
ecc048cb40
commit
ff3d413a6e
11 changed files with 496 additions and 42 deletions
|
@ -15,13 +15,11 @@ import { EuiIconTip } from '@elastic/eui';
|
|||
import {
|
||||
ALERT_DURATION,
|
||||
ALERT_END,
|
||||
ALERT_RULE_UUID,
|
||||
ALERT_EVALUATION_THRESHOLD,
|
||||
ALERT_RULE_TYPE_ID,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import moment from 'moment';
|
||||
import { getTransactionType } from '../../../../context/apm_service/apm_service_context';
|
||||
import { useServiceAgentFetcher } from '../../../../context/apm_service/use_service_agent_fetcher';
|
||||
import { useServiceTransactionTypesFetcher } from '../../../../context/apm_service/use_service_transaction_types_fetcher';
|
||||
import { asPercent } from '../../../../../common/utils/formatters';
|
||||
import { APIReturnType } from '../../../../services/rest/create_call_apm_api';
|
||||
import { getDurationFormatter } from '../../../../../common/utils/formatters/duration';
|
||||
|
@ -48,6 +46,7 @@ import {
|
|||
import { getAggsTypeFromRule, isLatencyThresholdRuleType } from './helpers';
|
||||
import { filterNil } from '../../../shared/charts/latency_chart';
|
||||
import { errorRateI18n } from '../../../shared/charts/failed_transaction_rate_chart';
|
||||
import { LatencyAlertsHistoryChart } from './latency_alerts_history_chart';
|
||||
import {
|
||||
AlertActiveRect,
|
||||
AlertAnnotation,
|
||||
|
@ -100,23 +99,7 @@ export function AlertDetailsAppSection({
|
|||
.toISOString();
|
||||
|
||||
const { start, end } = useTimeRange({ rangeFrom, rangeTo });
|
||||
const { agentName } = useServiceAgentFetcher({
|
||||
serviceName,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
const transactionTypes = useServiceTransactionTypesFetcher({
|
||||
serviceName,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
|
||||
const transactionType = getTransactionType({
|
||||
transactionType: alert.fields[TRANSACTION_TYPE],
|
||||
transactionTypes,
|
||||
agentName,
|
||||
});
|
||||
|
||||
const transactionType = alert.fields[TRANSACTION_TYPE];
|
||||
const comparisonChartTheme = getComparisonChartTheme();
|
||||
const INITIAL_STATE = {
|
||||
currentPeriod: [],
|
||||
|
@ -443,6 +426,18 @@ export function AlertDetailsAppSection({
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<LatencyAlertsHistoryChart
|
||||
ruleId={alert.fields[ALERT_RULE_UUID]}
|
||||
serviceName={serviceName}
|
||||
start={start}
|
||||
end={end}
|
||||
transactionType={transactionType}
|
||||
latencyAggregationType={latencyAggregationType}
|
||||
environment={environment}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</ChartPointerEventContextProvider>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -5,3 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
export const DEFAULT_DATE_FORMAT = 'HH:mm:ss';
|
||||
export const CHART_ANNOTATION_RED_COLOR = '#BD271E';
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* 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, { useMemo } from 'react';
|
||||
import moment from 'moment';
|
||||
import { EuiPanel, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiText } from '@elastic/eui';
|
||||
import {
|
||||
AnnotationDomainType,
|
||||
LineAnnotation,
|
||||
Position,
|
||||
} from '@elastic/charts';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import { EuiBadge } from '@elastic/eui';
|
||||
import { convertTo } from '@kbn/observability-plugin/public';
|
||||
import { useFetchTriggeredAlertsHistory } from '../../../../hooks/use_fetch_triggered_alert_history';
|
||||
import { getDurationFormatter } from '../../../../../common/utils/formatters';
|
||||
import { getLatencyChartSelector } from '../../../../selectors/latency_chart_selectors';
|
||||
import { LatencyAggregationType } from '../../../../../common/latency_aggregation_types';
|
||||
import { useFetcher } from '../../../../hooks/use_fetcher';
|
||||
import { TimeseriesChart } from '../../../shared/charts/timeseries_chart';
|
||||
import { filterNil } from '../../../shared/charts/latency_chart';
|
||||
import {
|
||||
getMaxY,
|
||||
getResponseTimeTickFormatter,
|
||||
} from '../../../shared/charts/transaction_charts/helper';
|
||||
import { CHART_ANNOTATION_RED_COLOR } from './constants';
|
||||
|
||||
interface LatencyAlertsHistoryChartProps {
|
||||
serviceName: string;
|
||||
start: string;
|
||||
end: string;
|
||||
transactionType?: string;
|
||||
latencyAggregationType: LatencyAggregationType;
|
||||
environment: string;
|
||||
timeZone: string;
|
||||
ruleId: string;
|
||||
}
|
||||
export function LatencyAlertsHistoryChart({
|
||||
serviceName,
|
||||
start,
|
||||
end,
|
||||
transactionType,
|
||||
latencyAggregationType,
|
||||
environment,
|
||||
timeZone,
|
||||
ruleId,
|
||||
}: LatencyAlertsHistoryChartProps) {
|
||||
const { data, status } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (
|
||||
serviceName &&
|
||||
start &&
|
||||
end &&
|
||||
transactionType &&
|
||||
latencyAggregationType
|
||||
) {
|
||||
return callApmApi(
|
||||
`GET /internal/apm/services/{serviceName}/transactions/charts/latency`,
|
||||
{
|
||||
params: {
|
||||
path: { serviceName },
|
||||
query: {
|
||||
environment,
|
||||
kuery: '',
|
||||
start: moment().subtract(30, 'days').toISOString(),
|
||||
end: moment().toISOString(),
|
||||
transactionType,
|
||||
transactionName: undefined,
|
||||
latencyAggregationType,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
[
|
||||
end,
|
||||
environment,
|
||||
latencyAggregationType,
|
||||
serviceName,
|
||||
start,
|
||||
transactionType,
|
||||
]
|
||||
);
|
||||
const memoizedData = useMemo(
|
||||
() =>
|
||||
getLatencyChartSelector({
|
||||
latencyChart: data,
|
||||
latencyAggregationType,
|
||||
previousPeriodLabel: '',
|
||||
}),
|
||||
// It should only update when the data has changed
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[data]
|
||||
);
|
||||
|
||||
const { currentPeriod, previousPeriod } = memoizedData;
|
||||
const timeseriesLatency = [currentPeriod, previousPeriod].filter(filterNil);
|
||||
const latencyMaxY = getMaxY(timeseriesLatency);
|
||||
const latencyFormatter = getDurationFormatter(latencyMaxY);
|
||||
const { triggeredAlertsData } = useFetchTriggeredAlertsHistory({
|
||||
features: 'apm',
|
||||
ruleId,
|
||||
});
|
||||
|
||||
return (
|
||||
<EuiPanel hasBorder={true}>
|
||||
<EuiFlexGroup direction="column" gutterSize="none" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h2>
|
||||
{serviceName}
|
||||
{i18n.translate('xpack.apm.latencyChartHistory.chartTitle', {
|
||||
defaultMessage: ' latency alerts history',
|
||||
})}
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate('xpack.apm.latencyChartHistory.last30days', {
|
||||
defaultMessage: 'Last 30 days',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<EuiFlexGroup gutterSize="l">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="xs" direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="danger">
|
||||
<EuiTitle size="s">
|
||||
<h3>{triggeredAlertsData?.totalTriggeredAlerts || '-'}</h3>
|
||||
</EuiTitle>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate(
|
||||
'xpack.apm.latencyChartHistory.alertsTriggered',
|
||||
{
|
||||
defaultMessage: 'Alerts triggered',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs" direction="column">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText>
|
||||
<EuiTitle size="s">
|
||||
<h3>
|
||||
{triggeredAlertsData?.avgTimeToRecoverUS
|
||||
? convertTo({
|
||||
unit: 'minutes',
|
||||
microseconds: triggeredAlertsData?.avgTimeToRecoverUS,
|
||||
extended: true,
|
||||
}).formatted
|
||||
: '-'}
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" color="subdued">
|
||||
{i18n.translate(
|
||||
'xpack.apm.latencyChartHistory.avgTimeToRecover',
|
||||
{
|
||||
defaultMessage: 'Avg time to recover',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="s" />
|
||||
|
||||
<TimeseriesChart
|
||||
id="latencyChart"
|
||||
annotations={[
|
||||
<LineAnnotation
|
||||
id="annotations"
|
||||
key={'annotationsAlertHistory'}
|
||||
domainType={AnnotationDomainType.XDomain}
|
||||
dataValues={
|
||||
triggeredAlertsData?.histogramTriggeredAlerts
|
||||
.filter((annotation) => annotation.doc_count > 0)
|
||||
.map((annotation) => {
|
||||
return {
|
||||
dataValue: annotation.key,
|
||||
header: String(annotation.doc_count),
|
||||
details: moment(annotation.key_as_string).format(
|
||||
'yyyy-MM-DD'
|
||||
),
|
||||
};
|
||||
}) || []
|
||||
}
|
||||
style={{
|
||||
line: {
|
||||
strokeWidth: 3,
|
||||
stroke: CHART_ANNOTATION_RED_COLOR,
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
marker={<EuiIcon type="alert" color={CHART_ANNOTATION_RED_COLOR} />}
|
||||
markerBody={(annotationData) => (
|
||||
<>
|
||||
<EuiBadge color={CHART_ANNOTATION_RED_COLOR}>
|
||||
<EuiText size="xs" color="white">
|
||||
{annotationData.header}
|
||||
</EuiText>
|
||||
</EuiBadge>
|
||||
<EuiSpacer size="xs" />
|
||||
</>
|
||||
)}
|
||||
markerPosition={Position.Top}
|
||||
/>,
|
||||
]}
|
||||
height={200}
|
||||
comparisonEnabled={false}
|
||||
offset={''}
|
||||
fetchStatus={status}
|
||||
timeseries={timeseriesLatency}
|
||||
yLabelFormat={getResponseTimeTickFormatter(latencyFormatter)}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
|
@ -14,7 +14,7 @@ import {
|
|||
import moment from 'moment';
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DEFAULT_DATE_FORMAT } from '../constants';
|
||||
import { CHART_ANNOTATION_RED_COLOR, DEFAULT_DATE_FORMAT } from '../constants';
|
||||
|
||||
export function AlertAnnotation({ alertStarted }: { alertStarted: number }) {
|
||||
return (
|
||||
|
@ -36,11 +36,11 @@ export function AlertAnnotation({ alertStarted }: { alertStarted: number }) {
|
|||
style={{
|
||||
line: {
|
||||
strokeWidth: 3,
|
||||
stroke: '#f00',
|
||||
stroke: CHART_ANNOTATION_RED_COLOR,
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
marker={<EuiIcon type="alert" color="red" />}
|
||||
marker={<EuiIcon type="alert" color={CHART_ANNOTATION_RED_COLOR} />}
|
||||
markerPosition={Position.Top}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { AnnotationDomainType, LineAnnotation } from '@elastic/charts';
|
||||
import { CHART_ANNOTATION_RED_COLOR } from '../constants';
|
||||
|
||||
export function AlertThresholdAnnotation({
|
||||
threshold,
|
||||
|
@ -29,7 +30,7 @@ export function AlertThresholdAnnotation({
|
|||
line: {
|
||||
opacity: 0.5,
|
||||
strokeWidth: 1,
|
||||
stroke: 'red',
|
||||
stroke: CHART_ANNOTATION_RED_COLOR,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* 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 { AsApiContract } from '@kbn/actions-plugin/common';
|
||||
import { HttpSetup } from '@kbn/core/public';
|
||||
import {
|
||||
ALERT_DURATION,
|
||||
ALERT_RULE_UUID,
|
||||
ALERT_START,
|
||||
ALERT_STATUS,
|
||||
ALERT_TIME_RANGE,
|
||||
} from '@kbn/rule-data-utils';
|
||||
import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
|
||||
interface UseFetchTriggeredAlertsHistoryProps {
|
||||
features: string;
|
||||
ruleId: string;
|
||||
}
|
||||
interface FetchTriggeredAlertsHistory {
|
||||
totalTriggeredAlerts: number;
|
||||
histogramTriggeredAlerts: Array<{
|
||||
key_as_string: string;
|
||||
key: number;
|
||||
doc_count: number;
|
||||
}>;
|
||||
error?: string;
|
||||
avgTimeToRecoverUS: number;
|
||||
}
|
||||
|
||||
interface TriggeredAlertsHistory {
|
||||
isLoadingTriggeredAlertHistory: boolean;
|
||||
errorTriggeredAlertHistory?: string;
|
||||
triggeredAlertsData?: FetchTriggeredAlertsHistory;
|
||||
}
|
||||
export function useFetchTriggeredAlertsHistory({
|
||||
features,
|
||||
ruleId,
|
||||
}: UseFetchTriggeredAlertsHistoryProps) {
|
||||
const { http } = useKibana().services;
|
||||
const [triggeredAlertsHistory, setTriggeredAlertsHistory] =
|
||||
useState<TriggeredAlertsHistory>({
|
||||
isLoadingTriggeredAlertHistory: true,
|
||||
});
|
||||
const isCancelledRef = useRef(false);
|
||||
const abortCtrlRef = useRef(new AbortController());
|
||||
const loadRuleAlertsAgg = useCallback(async () => {
|
||||
isCancelledRef.current = false;
|
||||
abortCtrlRef.current.abort();
|
||||
abortCtrlRef.current = new AbortController();
|
||||
|
||||
try {
|
||||
if (!http) throw new Error('No http client');
|
||||
if (!features) return;
|
||||
const { index } = await fetchIndexNameAPI({
|
||||
http,
|
||||
features,
|
||||
});
|
||||
|
||||
const {
|
||||
totalTriggeredAlerts,
|
||||
histogramTriggeredAlerts,
|
||||
error,
|
||||
avgTimeToRecoverUS,
|
||||
} = await fetchTriggeredAlertsHistory({
|
||||
http,
|
||||
index,
|
||||
ruleId,
|
||||
signal: abortCtrlRef.current.signal,
|
||||
});
|
||||
|
||||
if (error) throw error;
|
||||
if (!isCancelledRef.current) {
|
||||
setTriggeredAlertsHistory((oldState: TriggeredAlertsHistory) => ({
|
||||
...oldState,
|
||||
triggeredAlertsData: {
|
||||
totalTriggeredAlerts,
|
||||
histogramTriggeredAlerts,
|
||||
avgTimeToRecoverUS,
|
||||
},
|
||||
isLoadingRuleAlertsAggs: false,
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
if (!isCancelledRef.current) {
|
||||
if (error.name !== 'AbortError') {
|
||||
setTriggeredAlertsHistory((oldState: TriggeredAlertsHistory) => ({
|
||||
...oldState,
|
||||
isLoadingRuleAlertsAggs: false,
|
||||
errorTriggeredAlertHistory: error,
|
||||
triggeredAlertsData: undefined,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [features, http, ruleId]);
|
||||
useEffect(() => {
|
||||
loadRuleAlertsAgg();
|
||||
}, [loadRuleAlertsAgg]);
|
||||
|
||||
return triggeredAlertsHistory;
|
||||
}
|
||||
|
||||
interface IndexName {
|
||||
index: string;
|
||||
}
|
||||
|
||||
export async function fetchIndexNameAPI({
|
||||
http,
|
||||
features,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
features: string;
|
||||
}): Promise<IndexName> {
|
||||
const res = await http.get<{ index_name: string[] }>(
|
||||
`${BASE_RAC_ALERTS_API_PATH}/index`,
|
||||
{
|
||||
query: { features },
|
||||
}
|
||||
);
|
||||
return {
|
||||
index: res.index_name[0],
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchTriggeredAlertsHistory({
|
||||
http,
|
||||
index,
|
||||
ruleId,
|
||||
signal,
|
||||
}: {
|
||||
http: HttpSetup;
|
||||
index: string;
|
||||
ruleId: string;
|
||||
signal: AbortSignal;
|
||||
}): Promise<FetchTriggeredAlertsHistory> {
|
||||
try {
|
||||
const res = await http.post<AsApiContract<any>>(
|
||||
`${BASE_RAC_ALERTS_API_PATH}/find`,
|
||||
{
|
||||
signal,
|
||||
body: JSON.stringify({
|
||||
index,
|
||||
size: 0,
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
term: {
|
||||
[ALERT_RULE_UUID]: ruleId,
|
||||
},
|
||||
},
|
||||
{
|
||||
range: {
|
||||
[ALERT_TIME_RANGE]: {
|
||||
gte: 'now-30d',
|
||||
lt: 'now',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
histogramTriggeredAlerts: {
|
||||
date_histogram: {
|
||||
field: ALERT_START,
|
||||
fixed_interval: '1d',
|
||||
extended_bounds: {
|
||||
min: 'now-30d',
|
||||
max: 'now',
|
||||
},
|
||||
},
|
||||
},
|
||||
avgTimeToRecoverUS: {
|
||||
filter: {
|
||||
term: {
|
||||
[ALERT_STATUS]: 'recovered',
|
||||
},
|
||||
},
|
||||
aggs: {
|
||||
recoveryTime: {
|
||||
avg: {
|
||||
field: ALERT_DURATION,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
const totalTriggeredAlerts = res?.hits.total.value;
|
||||
const histogramTriggeredAlerts =
|
||||
res?.aggregations?.histogramTriggeredAlerts.buckets;
|
||||
const avgTimeToRecoverUS =
|
||||
res?.aggregations?.avgTimeToRecoverUS.recoveryTime.value;
|
||||
return {
|
||||
totalTriggeredAlerts,
|
||||
histogramTriggeredAlerts,
|
||||
avgTimeToRecoverUS,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return {
|
||||
error,
|
||||
totalTriggeredAlerts: 0,
|
||||
histogramTriggeredAlerts: [],
|
||||
avgTimeToRecoverUS: 0,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -109,7 +109,7 @@ function getUnitLabelAndConvertedValue(unitKey: DurationTimeUnit, value: number)
|
|||
/**
|
||||
* Converts a microseconds value into the unit defined.
|
||||
*/
|
||||
function convertTo({
|
||||
export function convertTo({
|
||||
unit,
|
||||
microseconds,
|
||||
defaultValue = NOT_AVAILABLE_LABEL,
|
||||
|
|
|
@ -22,7 +22,7 @@ export function useChartTheme(): PartialTheme[] {
|
|||
chartMargins: {
|
||||
left: 10,
|
||||
right: 10,
|
||||
top: 10,
|
||||
top: 35,
|
||||
bottom: 10,
|
||||
},
|
||||
background: {
|
||||
|
|
|
@ -121,3 +121,4 @@ export { ExploratoryViewContextProvider } from './components/shared/exploratory_
|
|||
export { fromQuery, toQuery } from './utils/url';
|
||||
|
||||
export type { NavigationSection } from './services/navigation_registry';
|
||||
export { convertTo } from '../common/utils/formatters/duration';
|
||||
|
|
|
@ -185,14 +185,6 @@ const bucketAggsTempsSchemas: t.Type<BucketAggsSchemas> = t.exact(
|
|||
})
|
||||
);
|
||||
|
||||
export const bucketAggsSchemas = t.intersection([
|
||||
bucketAggsTempsSchemas,
|
||||
t.partial({
|
||||
aggs: t.union([t.record(t.string, bucketAggsTempsSchemas), t.undefined]),
|
||||
aggregations: t.union([t.record(t.string, bucketAggsTempsSchemas), t.undefined]),
|
||||
}),
|
||||
]);
|
||||
|
||||
/**
|
||||
* Schemas for the metrics Aggregations
|
||||
*
|
||||
|
@ -287,11 +279,22 @@ export const metricsAggsSchemas = t.exact(
|
|||
}),
|
||||
})
|
||||
),
|
||||
aggs: t.undefined,
|
||||
aggregations: t.undefined,
|
||||
})
|
||||
);
|
||||
|
||||
export const bucketAggsSchemas = t.intersection([
|
||||
bucketAggsTempsSchemas,
|
||||
t.exact(
|
||||
t.partial({
|
||||
aggs: t.record(t.string, t.intersection([bucketAggsTempsSchemas, metricsAggsSchemas])),
|
||||
aggregations: t.record(
|
||||
t.string,
|
||||
t.intersection([bucketAggsTempsSchemas, metricsAggsSchemas])
|
||||
),
|
||||
})
|
||||
),
|
||||
]);
|
||||
|
||||
export type PutIndexTemplateRequest = estypes.IndicesPutIndexTemplateRequest & {
|
||||
body?: { composed_of?: string[] };
|
||||
};
|
||||
|
|
|
@ -26,11 +26,7 @@ export const findAlertsByQueryRoute = (router: IRouter<RacRequestHandlerContext>
|
|||
t.partial({
|
||||
index: t.string,
|
||||
query: t.object,
|
||||
aggs: t.union([
|
||||
t.record(t.string, bucketAggsSchemas),
|
||||
t.record(t.string, metricsAggsSchemas),
|
||||
t.undefined,
|
||||
]),
|
||||
aggs: t.record(t.string, t.intersection([metricsAggsSchemas, bucketAggsSchemas])),
|
||||
sort: t.union([t.array(t.object), t.undefined]),
|
||||
search_after: t.union([t.array(t.number), t.array(t.string), t.undefined]),
|
||||
size: t.union([PositiveInteger, t.undefined]),
|
||||
|
@ -49,7 +45,6 @@ export const findAlertsByQueryRoute = (router: IRouter<RacRequestHandlerContext>
|
|||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const { query, aggs, _source, track_total_hits, size, index, sort, search_after } =
|
||||
request.body;
|
||||
|
||||
const racContext = await context.rac;
|
||||
const alertsClient = await racContext.getAlertsClient();
|
||||
const alerts = await alertsClient.find({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue