[Metrics UI] Replace uses of any
introduced by Lodash 4 (#75507)
Co-authored-by: Felix Stürmer <weltenwort@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
8671db1559
commit
b98e2e4f3d
|
@ -85,7 +85,7 @@ interface Props {
|
||||||
nodeType: InventoryItemType;
|
nodeType: InventoryItemType;
|
||||||
filterQuery?: string;
|
filterQuery?: string;
|
||||||
filterQueryText?: string;
|
filterQueryText?: string;
|
||||||
sourceId?: string;
|
sourceId: string;
|
||||||
alertOnNoData?: boolean;
|
alertOnNoData?: boolean;
|
||||||
};
|
};
|
||||||
alertInterval: string;
|
alertInterval: string;
|
||||||
|
@ -379,7 +379,7 @@ export const Expressions: React.FC<Props> = (props) => {
|
||||||
<AlertPreview
|
<AlertPreview
|
||||||
alertInterval={alertInterval}
|
alertInterval={alertInterval}
|
||||||
alertType={METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID}
|
alertType={METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID}
|
||||||
alertParams={pick(alertParams as any, 'criteria', 'nodeType', 'sourceId', 'filterQuery')}
|
alertParams={pick(alertParams, 'criteria', 'nodeType', 'sourceId', 'filterQuery')}
|
||||||
validate={validateMetricThreshold}
|
validate={validateMetricThreshold}
|
||||||
fetch={alertsContext.http.fetch}
|
fetch={alertsContext.http.fetch}
|
||||||
groupByDisplayName={alertParams.nodeType}
|
groupByDisplayName={alertParams.nodeType}
|
||||||
|
|
|
@ -34,6 +34,7 @@ describe('Expression', () => {
|
||||||
criteria: [],
|
criteria: [],
|
||||||
groupBy: undefined,
|
groupBy: undefined,
|
||||||
filterQueryText: '',
|
filterQueryText: '',
|
||||||
|
sourceId: 'default',
|
||||||
};
|
};
|
||||||
|
|
||||||
const mocks = coreMock.createSetup();
|
const mocks = coreMock.createSetup();
|
||||||
|
|
|
@ -400,7 +400,7 @@ export const Expressions: React.FC<Props> = (props) => {
|
||||||
<AlertPreview
|
<AlertPreview
|
||||||
alertInterval={alertInterval}
|
alertInterval={alertInterval}
|
||||||
alertType={METRIC_THRESHOLD_ALERT_TYPE_ID}
|
alertType={METRIC_THRESHOLD_ALERT_TYPE_ID}
|
||||||
alertParams={pick(alertParams as any, 'criteria', 'groupBy', 'filterQuery', 'sourceId')}
|
alertParams={pick(alertParams, 'criteria', 'groupBy', 'filterQuery', 'sourceId')}
|
||||||
showNoDataResults={alertParams.alertOnNoData}
|
showNoDataResults={alertParams.alertOnNoData}
|
||||||
validate={validateMetricThreshold}
|
validate={validateMetricThreshold}
|
||||||
fetch={alertsContext.http.fetch}
|
fetch={alertsContext.http.fetch}
|
||||||
|
|
|
@ -84,14 +84,16 @@ export const ExpressionChart: React.FC<Props> = ({
|
||||||
};
|
};
|
||||||
const isDarkMode = context.uiSettings?.get('theme:darkMode') || false;
|
const isDarkMode = context.uiSettings?.get('theme:darkMode') || false;
|
||||||
const dateFormatter = useMemo(() => {
|
const dateFormatter = useMemo(() => {
|
||||||
const firstSeries = data ? first(data.series) : null;
|
const firstSeries = first(data?.series);
|
||||||
return firstSeries && firstSeries.rows.length > 0
|
const firstTimestamp = first(firstSeries?.rows)?.timestamp;
|
||||||
? niceTimeFormatter([
|
const lastTimestamp = last(firstSeries?.rows)?.timestamp;
|
||||||
(first(firstSeries.rows) as any).timestamp,
|
|
||||||
(last(firstSeries.rows) as any).timestamp,
|
if (firstTimestamp == null || lastTimestamp == null) {
|
||||||
])
|
return (value: number) => `${value}`;
|
||||||
: (value: number) => `${value}`;
|
}
|
||||||
}, [data]);
|
|
||||||
|
return niceTimeFormatter([firstTimestamp, lastTimestamp]);
|
||||||
|
}, [data?.series]);
|
||||||
|
|
||||||
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
||||||
const yAxisFormater = useCallback(createFormatterForMetric(metric), [expression]);
|
const yAxisFormater = useCallback(createFormatterForMetric(metric), [expression]);
|
||||||
|
@ -138,8 +140,8 @@ export const ExpressionChart: React.FC<Props> = ({
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const firstTimestamp = (first(firstSeries.rows) as any).timestamp;
|
const firstTimestamp = first(firstSeries.rows)!.timestamp;
|
||||||
const lastTimestamp = (last(firstSeries.rows) as any).timestamp;
|
const lastTimestamp = last(firstSeries.rows)!.timestamp;
|
||||||
const dataDomain = calculateDomain(series, [metric], false);
|
const dataDomain = calculateDomain(series, [metric], false);
|
||||||
const domain = {
|
const domain = {
|
||||||
max: Math.max(dataDomain.max, last(thresholds) || dataDomain.max) * 1.1, // add 10% headroom.
|
max: Math.max(dataDomain.max, last(thresholds) || dataDomain.max) * 1.1, // add 10% headroom.
|
||||||
|
|
|
@ -13,9 +13,9 @@ export const transformMetricsExplorerData = (
|
||||||
data: MetricsExplorerResponse | null
|
data: MetricsExplorerResponse | null
|
||||||
) => {
|
) => {
|
||||||
const { criteria } = params;
|
const { criteria } = params;
|
||||||
if (criteria && data) {
|
const firstSeries = first(data?.series);
|
||||||
const firstSeries = first(data.series) as any;
|
if (criteria && firstSeries) {
|
||||||
const series = firstSeries.rows.reduce((acc: any, row: any) => {
|
const series = firstSeries.rows.reduce((acc, row) => {
|
||||||
const { timestamp } = row;
|
const { timestamp } = row;
|
||||||
criteria.forEach((item, index) => {
|
criteria.forEach((item, index) => {
|
||||||
if (!acc[index]) {
|
if (!acc[index]) {
|
||||||
|
|
|
@ -55,7 +55,7 @@ export interface AlertParams {
|
||||||
criteria: MetricExpression[];
|
criteria: MetricExpression[];
|
||||||
groupBy?: string[];
|
groupBy?: string[];
|
||||||
filterQuery?: string;
|
filterQuery?: string;
|
||||||
sourceId?: string;
|
sourceId: string;
|
||||||
filterQueryText?: string;
|
filterQueryText?: string;
|
||||||
alertOnNoData?: boolean;
|
alertOnNoData?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,7 +266,7 @@ export const LegendControls = ({
|
||||||
fullWidth
|
fullWidth
|
||||||
label={
|
label={
|
||||||
<SwatchLabel
|
<SwatchLabel
|
||||||
color={first(paletteColors) as any}
|
color={first(paletteColors)!}
|
||||||
label={i18n.translate('xpack.infra.legendControls.minLabel', {
|
label={i18n.translate('xpack.infra.legendControls.minLabel', {
|
||||||
defaultMessage: 'Minimum',
|
defaultMessage: 'Minimum',
|
||||||
})}
|
})}
|
||||||
|
@ -294,7 +294,7 @@ export const LegendControls = ({
|
||||||
display="columnCompressed"
|
display="columnCompressed"
|
||||||
label={
|
label={
|
||||||
<SwatchLabel
|
<SwatchLabel
|
||||||
color={last(paletteColors) as any}
|
color={last(paletteColors)!}
|
||||||
label={i18n.translate('xpack.infra.legendControls.maxLabel', {
|
label={i18n.translate('xpack.infra.legendControls.maxLabel', {
|
||||||
defaultMessage: 'Maxium',
|
defaultMessage: 'Maxium',
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -68,8 +68,9 @@ export const calculateSteppedGradientColor = (
|
||||||
|
|
||||||
// Since the stepped legend matches a range we need to ensure anything outside
|
// Since the stepped legend matches a range we need to ensure anything outside
|
||||||
// the max bounds get's the maximum color.
|
// the max bounds get's the maximum color.
|
||||||
if (gte(normalizedValue, (last(rules) as any).value)) {
|
const lastRule = last(rules);
|
||||||
return (last(rules) as any).color;
|
if (lastRule && gte(normalizedValue, lastRule.value)) {
|
||||||
|
return lastRule.color;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rules.reduce((color: string, rule) => {
|
return rules.reduce((color: string, rule) => {
|
||||||
|
@ -79,7 +80,7 @@ export const calculateSteppedGradientColor = (
|
||||||
return rule.color;
|
return rule.color;
|
||||||
}
|
}
|
||||||
return color;
|
return color;
|
||||||
}, (first(rules) as any).color || defaultColor);
|
}, first(rules)?.color ?? defaultColor);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const calculateStepColor = (
|
export const calculateStepColor = (
|
||||||
|
@ -106,7 +107,7 @@ export const calculateGradientColor = (
|
||||||
return defaultColor;
|
return defaultColor;
|
||||||
}
|
}
|
||||||
if (rules.length === 1) {
|
if (rules.length === 1) {
|
||||||
return (last(rules) as any).color;
|
return last(rules)!.color;
|
||||||
}
|
}
|
||||||
const { min, max } = bounds;
|
const { min, max } = bounds;
|
||||||
const sortedRules = sortBy(rules, 'value');
|
const sortedRules = sortBy(rules, 'value');
|
||||||
|
@ -116,10 +117,8 @@ export const calculateGradientColor = (
|
||||||
return rule;
|
return rule;
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, first(sortedRules)) as any;
|
}, first(sortedRules))!;
|
||||||
const endRule = sortedRules
|
const endRule = sortedRules.filter((r) => r !== startRule).find((r) => r.value >= normValue);
|
||||||
.filter((r) => r !== startRule)
|
|
||||||
.find((r) => r.value >= normValue) as any;
|
|
||||||
if (!endRule) {
|
if (!endRule) {
|
||||||
return startRule.color;
|
return startRule.color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,9 @@ function findOrCreateGroupWithNodes(
|
||||||
* look for the full id. Otherwise we need to find the parent group and
|
* look for the full id. Otherwise we need to find the parent group and
|
||||||
* then look for the group in it's sub groups.
|
* then look for the group in it's sub groups.
|
||||||
*/
|
*/
|
||||||
if (path.length === 2) {
|
const firstPath = first(path);
|
||||||
const parentId = (first(path) as any).value;
|
if (path.length === 2 && firstPath) {
|
||||||
|
const parentId = firstPath.value;
|
||||||
const existingParentGroup = groups.find((g) => g.id === parentId);
|
const existingParentGroup = groups.find((g) => g.id === parentId);
|
||||||
if (isWaffleMapGroupWithGroups(existingParentGroup)) {
|
if (isWaffleMapGroupWithGroups(existingParentGroup)) {
|
||||||
const existingSubGroup = existingParentGroup.groups.find((g) => g.id === id);
|
const existingSubGroup = existingParentGroup.groups.find((g) => g.id === id);
|
||||||
|
|
|
@ -74,16 +74,13 @@ export const MetricsExplorerChart = ({
|
||||||
const [from, to] = x;
|
const [from, to] = x;
|
||||||
onTimeChange(moment(from).toISOString(), moment(to).toISOString());
|
onTimeChange(moment(from).toISOString(), moment(to).toISOString());
|
||||||
};
|
};
|
||||||
const dateFormatter = useMemo(
|
const dateFormatter = useMemo(() => {
|
||||||
() =>
|
const firstRow = first(series.rows);
|
||||||
series.rows.length > 0
|
const lastRow = last(series.rows);
|
||||||
? niceTimeFormatter([
|
return firstRow && lastRow
|
||||||
(first(series.rows) as any).timestamp,
|
? niceTimeFormatter([firstRow.timestamp, lastRow.timestamp])
|
||||||
(last(series.rows) as any).timestamp,
|
: (value: number) => `${value}`;
|
||||||
])
|
}, [series.rows]);
|
||||||
: (value: number) => `${value}`,
|
|
||||||
[series.rows]
|
|
||||||
);
|
|
||||||
const tooltipProps = {
|
const tooltipProps = {
|
||||||
headerFormatter: useCallback(
|
headerFormatter: useCallback(
|
||||||
(data: TooltipValue) => moment(data.value).format(dateFormat || 'Y-MM-DD HH:mm:ss.SSS'),
|
(data: TooltipValue) => moment(data.value).format(dateFormat || 'Y-MM-DD HH:mm:ss.SSS'),
|
||||||
|
|
|
@ -122,14 +122,14 @@ const getData = async (
|
||||||
if (!nodes.length) return { [UNGROUPED_FACTORY_KEY]: null }; // No Data state
|
if (!nodes.length) return { [UNGROUPED_FACTORY_KEY]: null }; // No Data state
|
||||||
|
|
||||||
return nodes.reduce((acc, n) => {
|
return nodes.reduce((acc, n) => {
|
||||||
const nodePathItem = last(n.path) as any;
|
const { name: nodeName } = n;
|
||||||
const m = first(n.metrics);
|
const m = first(n.metrics);
|
||||||
if (m && m.value && m.timeseries) {
|
if (m && m.value && m.timeseries) {
|
||||||
const { timeseries } = m;
|
const { timeseries } = m;
|
||||||
const values = timeseries.rows.map((row) => row.metric_0) as Array<number | null>;
|
const values = timeseries.rows.map((row) => row.metric_0) as Array<number | null>;
|
||||||
acc[nodePathItem.label] = values;
|
acc[nodeName] = values;
|
||||||
} else {
|
} else {
|
||||||
acc[nodePathItem.label] = m && m.value;
|
acc[nodeName] = m && m.value;
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, number | Array<number | string | null | undefined> | undefined | null>);
|
}, {} as Record<string, number | Array<number | string | null | undefined> | undefined | null>);
|
||||||
|
|
|
@ -42,6 +42,8 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
|
||||||
alertOnNoData,
|
alertOnNoData,
|
||||||
} = params as InventoryMetricThresholdParams;
|
} = params as InventoryMetricThresholdParams;
|
||||||
|
|
||||||
|
if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions');
|
||||||
|
|
||||||
const source = await libs.sources.getSourceConfiguration(
|
const source = await libs.sources.getSourceConfiguration(
|
||||||
services.savedObjectsClient,
|
services.savedObjectsClient,
|
||||||
sourceId || 'default'
|
sourceId || 'default'
|
||||||
|
@ -53,7 +55,7 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) =
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const inventoryItems = Object.keys(first(results) as any);
|
const inventoryItems = Object.keys(first(results)!);
|
||||||
for (const item of inventoryItems) {
|
for (const item of inventoryItems) {
|
||||||
const alertInstance = services.alertInstanceFactory(`${item}`);
|
const alertInstance = services.alertInstanceFactory(`${item}`);
|
||||||
// AND logic; all criteria must be across the threshold
|
// AND logic; all criteria must be across the threshold
|
||||||
|
|
|
@ -40,6 +40,8 @@ export const previewInventoryMetricThresholdAlert = async ({
|
||||||
}: PreviewInventoryMetricThresholdAlertParams) => {
|
}: PreviewInventoryMetricThresholdAlertParams) => {
|
||||||
const { criteria, filterQuery, nodeType } = params as InventoryMetricThresholdParams;
|
const { criteria, filterQuery, nodeType } = params as InventoryMetricThresholdParams;
|
||||||
|
|
||||||
|
if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions');
|
||||||
|
|
||||||
const { timeSize, timeUnit } = criteria[0];
|
const { timeSize, timeUnit } = criteria[0];
|
||||||
const bucketInterval = `${timeSize}${timeUnit}`;
|
const bucketInterval = `${timeSize}${timeUnit}`;
|
||||||
const bucketIntervalInSeconds = getIntervalInSeconds(bucketInterval);
|
const bucketIntervalInSeconds = getIntervalInSeconds(bucketInterval);
|
||||||
|
@ -57,7 +59,7 @@ export const previewInventoryMetricThresholdAlert = async ({
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const inventoryItems = Object.keys(first(results) as any);
|
const inventoryItems = Object.keys(first(results)!);
|
||||||
const previewResults = inventoryItems.map((item) => {
|
const previewResults = inventoryItems.map((item) => {
|
||||||
const numberOfResultBuckets = lookbackSize;
|
const numberOfResultBuckets = lookbackSize;
|
||||||
const numberOfExecutionBuckets = Math.floor(numberOfResultBuckets / alertResultsPerExecution);
|
const numberOfExecutionBuckets = Math.floor(numberOfResultBuckets / alertResultsPerExecution);
|
||||||
|
|
|
@ -22,6 +22,8 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) =>
|
||||||
async function (options: AlertExecutorOptions) {
|
async function (options: AlertExecutorOptions) {
|
||||||
const { services, params } = options;
|
const { services, params } = options;
|
||||||
const { criteria } = params;
|
const { criteria } = params;
|
||||||
|
if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions');
|
||||||
|
|
||||||
const { sourceId, alertOnNoData } = params as {
|
const { sourceId, alertOnNoData } = params as {
|
||||||
sourceId?: string;
|
sourceId?: string;
|
||||||
alertOnNoData: boolean;
|
alertOnNoData: boolean;
|
||||||
|
@ -34,8 +36,8 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) =>
|
||||||
const config = source.configuration;
|
const config = source.configuration;
|
||||||
const alertResults = await evaluateAlert(services.callCluster, params, config);
|
const alertResults = await evaluateAlert(services.callCluster, params, config);
|
||||||
|
|
||||||
// Because each alert result has the same group definitions, just grap the groups from the first one.
|
// Because each alert result has the same group definitions, just grab the groups from the first one.
|
||||||
const groups = Object.keys(first(alertResults) as any);
|
const groups = Object.keys(first(alertResults)!);
|
||||||
for (const group of groups) {
|
for (const group of groups) {
|
||||||
const alertInstance = services.alertInstanceFactory(`${group}`);
|
const alertInstance = services.alertInstanceFactory(`${group}`);
|
||||||
|
|
||||||
|
@ -60,7 +62,7 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) =>
|
||||||
let reason;
|
let reason;
|
||||||
if (nextState === AlertStates.ALERT) {
|
if (nextState === AlertStates.ALERT) {
|
||||||
reason = alertResults
|
reason = alertResults
|
||||||
.map((result) => buildFiredAlertReason(formatAlertResult(result[group]) as any))
|
.map((result) => buildFiredAlertReason(formatAlertResult(result[group])))
|
||||||
.join('\n');
|
.join('\n');
|
||||||
}
|
}
|
||||||
if (alertOnNoData) {
|
if (alertOnNoData) {
|
||||||
|
@ -121,11 +123,13 @@ const mapToConditionsLookup = (
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
const formatAlertResult = (alertResult: {
|
const formatAlertResult = <AlertResult>(
|
||||||
metric: string;
|
alertResult: {
|
||||||
currentValue: number;
|
metric: string;
|
||||||
threshold: number[];
|
currentValue: number;
|
||||||
}) => {
|
threshold: number[];
|
||||||
|
} & AlertResult
|
||||||
|
) => {
|
||||||
const { metric, currentValue, threshold } = alertResult;
|
const { metric, currentValue, threshold } = alertResult;
|
||||||
if (!metric.endsWith('.pct')) return alertResult;
|
if (!metric.endsWith('.pct')) return alertResult;
|
||||||
const formatter = createFormatter('percent');
|
const formatter = createFormatter('percent');
|
||||||
|
|
|
@ -49,6 +49,8 @@ export const previewMetricThresholdAlert: (
|
||||||
iterations = 0,
|
iterations = 0,
|
||||||
precalculatedNumberOfGroups
|
precalculatedNumberOfGroups
|
||||||
) => {
|
) => {
|
||||||
|
if (params.criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions');
|
||||||
|
|
||||||
// There are three different "intervals" we're dealing with here, so to disambiguate:
|
// There are three different "intervals" we're dealing with here, so to disambiguate:
|
||||||
// - The lookback interval, which is how long of a period of time we want to examine to count
|
// - The lookback interval, which is how long of a period of time we want to examine to count
|
||||||
// how many times the alert fired
|
// how many times the alert fired
|
||||||
|
@ -70,7 +72,7 @@ export const previewMetricThresholdAlert: (
|
||||||
// Get a date histogram using the bucket interval and the lookback interval
|
// Get a date histogram using the bucket interval and the lookback interval
|
||||||
try {
|
try {
|
||||||
const alertResults = await evaluateAlert(callCluster, params, config, timeframe);
|
const alertResults = await evaluateAlert(callCluster, params, config, timeframe);
|
||||||
const groups = Object.keys(first(alertResults) as any);
|
const groups = Object.keys(first(alertResults)!);
|
||||||
|
|
||||||
// Now determine how to interpolate this histogram based on the alert interval
|
// Now determine how to interpolate this histogram based on the alert interval
|
||||||
const alertIntervalInSeconds = getIntervalInSeconds(alertInterval);
|
const alertIntervalInSeconds = getIntervalInSeconds(alertInterval);
|
||||||
|
@ -81,7 +83,7 @@ export const previewMetricThresholdAlert: (
|
||||||
// buckets would have fired the alert. If the alert interval and bucket interval are the same,
|
// buckets would have fired the alert. If the alert interval and bucket interval are the same,
|
||||||
// this will be a 1:1 evaluation of the alert results. If these are different, the interpolation
|
// this will be a 1:1 evaluation of the alert results. If these are different, the interpolation
|
||||||
// will skip some buckets or read some buckets more than once, depending on the differential
|
// will skip some buckets or read some buckets more than once, depending on the differential
|
||||||
const numberOfResultBuckets = (first(alertResults) as any)[group].shouldFire.length;
|
const numberOfResultBuckets = first(alertResults)![group].shouldFire.length;
|
||||||
const numberOfExecutionBuckets = Math.floor(
|
const numberOfExecutionBuckets = Math.floor(
|
||||||
numberOfResultBuckets / alertResultsPerExecution
|
numberOfResultBuckets / alertResultsPerExecution
|
||||||
);
|
);
|
||||||
|
@ -120,8 +122,7 @@ export const previewMetricThresholdAlert: (
|
||||||
? await evaluateAlert(callCluster, params, config)
|
? await evaluateAlert(callCluster, params, config)
|
||||||
: [];
|
: [];
|
||||||
const numberOfGroups =
|
const numberOfGroups =
|
||||||
precalculatedNumberOfGroups ??
|
precalculatedNumberOfGroups ?? Math.max(Object.keys(first(currentAlertResults)!).length, 1);
|
||||||
Math.max(Object.keys(first(currentAlertResults) as any).length, 1);
|
|
||||||
const estimatedTotalBuckets =
|
const estimatedTotalBuckets =
|
||||||
(lookbackIntervalInSeconds / bucketIntervalInSeconds) * numberOfGroups;
|
(lookbackIntervalInSeconds / bucketIntervalInSeconds) * numberOfGroups;
|
||||||
// The minimum number of slices is 2. In case we underestimate the total number of buckets
|
// The minimum number of slices is 2. In case we underestimate the total number of buckets
|
||||||
|
@ -152,14 +153,16 @@ export const previewMetricThresholdAlert: (
|
||||||
// `undefined` values occur if there is no data at all in a certain slice, and that slice
|
// `undefined` values occur if there is no data at all in a certain slice, and that slice
|
||||||
// returns an empty array. This is different from an error or no data state,
|
// returns an empty array. This is different from an error or no data state,
|
||||||
// so filter these results out entirely and only regard the resultA portion
|
// so filter these results out entirely and only regard the resultA portion
|
||||||
.filter((value) => typeof value !== 'undefined')
|
.filter(
|
||||||
|
<Value>(value: Value): value is NonNullable<Value> => typeof value !== 'undefined'
|
||||||
|
)
|
||||||
.reduce((a, b) => {
|
.reduce((a, b) => {
|
||||||
if (!a) return b;
|
if (!a) return b;
|
||||||
if (!b) return a;
|
if (!b) return a;
|
||||||
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
return zippedResult as any;
|
return zippedResult;
|
||||||
} else throw e;
|
} else throw e;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -127,7 +127,8 @@ export const getNodeMetrics = (
|
||||||
avg: null,
|
avg: null,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
const lastBucket = findLastFullBucket(nodeBuckets, options) as any;
|
const lastBucket = findLastFullBucket(nodeBuckets, options);
|
||||||
|
if (!lastBucket) return [];
|
||||||
return options.metrics.map((metric, index) => {
|
return options.metrics.map((metric, index) => {
|
||||||
const metricResult: SnapshotNodeMetric = {
|
const metricResult: SnapshotNodeMetric = {
|
||||||
name: metric.type,
|
name: metric.type,
|
||||||
|
|
|
@ -27,6 +27,8 @@ import { InfraSnapshotRequestOptions } from './types';
|
||||||
import { createTimeRangeWithInterval } from './create_timerange_with_interval';
|
import { createTimeRangeWithInterval } from './create_timerange_with_interval';
|
||||||
import { SnapshotNode } from '../../../common/http_api/snapshot_api';
|
import { SnapshotNode } from '../../../common/http_api/snapshot_api';
|
||||||
|
|
||||||
|
type NamedSnapshotNode = SnapshotNode & { name: string };
|
||||||
|
|
||||||
export type ESSearchClient = <Hit = {}, Aggregation = undefined>(
|
export type ESSearchClient = <Hit = {}, Aggregation = undefined>(
|
||||||
options: CallWithRequestParams
|
options: CallWithRequestParams
|
||||||
) => Promise<InfraDatabaseSearchResponse<Hit, Aggregation>>;
|
) => Promise<InfraDatabaseSearchResponse<Hit, Aggregation>>;
|
||||||
|
@ -34,7 +36,7 @@ export class InfraSnapshot {
|
||||||
public async getNodes(
|
public async getNodes(
|
||||||
client: ESSearchClient,
|
client: ESSearchClient,
|
||||||
options: InfraSnapshotRequestOptions
|
options: InfraSnapshotRequestOptions
|
||||||
): Promise<{ nodes: SnapshotNode[]; interval: string }> {
|
): Promise<{ nodes: NamedSnapshotNode[]; interval: string }> {
|
||||||
// Both requestGroupedNodes and requestNodeMetrics may send several requests to elasticsearch
|
// Both requestGroupedNodes and requestNodeMetrics may send several requests to elasticsearch
|
||||||
// in order to page through the results of their respective composite aggregations.
|
// in order to page through the results of their respective composite aggregations.
|
||||||
// Both chains of requests are supposed to run in parallel, and their results be merged
|
// Both chains of requests are supposed to run in parallel, and their results be merged
|
||||||
|
@ -184,11 +186,12 @@ const mergeNodeBuckets = (
|
||||||
nodeGroupByBuckets: InfraSnapshotNodeGroupByBucket[],
|
nodeGroupByBuckets: InfraSnapshotNodeGroupByBucket[],
|
||||||
nodeMetricsBuckets: InfraSnapshotNodeMetricsBucket[],
|
nodeMetricsBuckets: InfraSnapshotNodeMetricsBucket[],
|
||||||
options: InfraSnapshotRequestOptions
|
options: InfraSnapshotRequestOptions
|
||||||
): SnapshotNode[] => {
|
): NamedSnapshotNode[] => {
|
||||||
const nodeMetricsForLookup = getNodeMetricsForLookup(nodeMetricsBuckets);
|
const nodeMetricsForLookup = getNodeMetricsForLookup(nodeMetricsBuckets);
|
||||||
|
|
||||||
return nodeGroupByBuckets.map((node) => {
|
return nodeGroupByBuckets.map((node) => {
|
||||||
return {
|
return {
|
||||||
|
name: node.key.name || node.key.id, // For type safety; name can be derived from getNodePath but not in a TS-friendly way
|
||||||
path: getNodePath(node, options),
|
path: getNodePath(node, options),
|
||||||
metrics: getNodeMetrics(nodeMetricsForLookup[node.key.id], options),
|
metrics: getNodeMetrics(nodeMetricsForLookup[node.key.id], options),
|
||||||
};
|
};
|
||||||
|
|
|
@ -48,7 +48,7 @@ export const initIpToHostName = ({ framework }: InfraBackendLibs) => {
|
||||||
body: { message: 'Host with matching IP address not found.' },
|
body: { message: 'Host with matching IP address not found.' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const hostDoc = first(hits.hits) as any;
|
const hostDoc = first(hits.hits)!;
|
||||||
return response.ok({ body: { host: hostDoc._source.host.name } });
|
return response.ok({ body: { host: hostDoc._source.host.name } });
|
||||||
} catch ({ statusCode = 500, message = 'Unknown error occurred' }) {
|
} catch ({ statusCode = 500, message = 'Unknown error occurred' }) {
|
||||||
return response.customError({
|
return response.customError({
|
||||||
|
|
|
@ -10,11 +10,14 @@ import { InfraDatabaseSearchResponse } from '../lib/adapters/framework';
|
||||||
export const createAfterKeyHandler = (
|
export const createAfterKeyHandler = (
|
||||||
optionsAfterKeyPath: string | string[],
|
optionsAfterKeyPath: string | string[],
|
||||||
afterKeySelector: (input: InfraDatabaseSearchResponse<any, any>) => any
|
afterKeySelector: (input: InfraDatabaseSearchResponse<any, any>) => any
|
||||||
) => <Options>(options: Options, response: InfraDatabaseSearchResponse<any, any>): Options => {
|
) => <Options extends object>(
|
||||||
|
options: Options,
|
||||||
|
response: InfraDatabaseSearchResponse<any, any>
|
||||||
|
): Options => {
|
||||||
if (!response.aggregations) {
|
if (!response.aggregations) {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
const newOptions = { ...options } as any;
|
const newOptions = { ...options };
|
||||||
const afterKey = afterKeySelector(response);
|
const afterKey = afterKeySelector(response);
|
||||||
set(newOptions, optionsAfterKeyPath, afterKey);
|
set(newOptions, optionsAfterKeyPath, afterKey);
|
||||||
return newOptions;
|
return newOptions;
|
||||||
|
|
Loading…
Reference in a new issue