[Uptime]Make uptime ping histogram bar clickable to improve filtering (#79054)

This commit is contained in:
Shahzad 2020-10-05 21:03:28 +02:00 committed by GitHub
parent d5b8a95694
commit 8120e7e7bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 108 additions and 29 deletions

View file

@ -66,6 +66,7 @@ export const MonitorSummaryType = t.intersection([
}),
t.partial({
histogram: HistogramType,
minInterval: t.number,
}),
]);

View file

@ -26,7 +26,7 @@ export interface GetPingHistogramParams {
export interface HistogramResult {
histogram: HistogramDataPoint[];
interval: string;
minInterval: number;
}
export interface HistogramQueryResult {

View file

@ -131,6 +131,7 @@ exports[`MonitorBarSeries component shallow renders a series when there are down
},
]
}
minInterval={10}
/>
</ContextProvider>
</ContextProvider>

View file

@ -273,7 +273,7 @@ exports[`PingHistogram component shallow renders the component without errors 1`
"y": 1,
},
],
"interval": "1s",
"minInterval": 60,
}
}
/>

View file

@ -31,6 +31,7 @@ describe('MonitorBarSeries component', () => {
up: 0,
},
],
minInterval: 10,
};
histogramSeries = [
{ timestamp: 1580387868000, up: 0, down: 5 },
@ -192,14 +193,16 @@ describe('MonitorBarSeries component', () => {
});
it('shallow renders nothing if the data series is null', () => {
const component = shallowWithRouter(<MonitorBarSeries histogramSeries={null} />);
const component = shallowWithRouter(
<MonitorBarSeries histogramSeries={null} minInterval={5} />
);
expect(component).toEqual({});
});
it('renders if the data series is present', () => {
const component = renderWithRouter(
<MountWithReduxProvider>
<MonitorBarSeries histogramSeries={histogramSeries} />
<MonitorBarSeries histogramSeries={histogramSeries} minInterval={5} />
</MountWithReduxProvider>
);
expect(component).toMatchSnapshot();

View file

@ -44,7 +44,7 @@ describe('PingHistogram component', () => {
{ x: 1581068989000, downCount: 3, upCount: 36, y: 1 },
{ x: 1581069019000, downCount: 1, upCount: 11, y: 1 },
],
interval: '1s',
minInterval: 60,
},
};

View file

@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { getDateRangeFromChartElement } from '../utils';
import { XYChartElementEvent } from '@elastic/charts';
describe('Chart utils', () => {
it('get date range from chart element should add 100 miliseconds', () => {
const elementData = [{ x: 1548697920000, y: 4 }];
const dr = getDateRangeFromChartElement(elementData as XYChartElementEvent, 1000);
expect(dr).toStrictEqual({
dateRangeStart: '2019-01-28T17:52:00.000Z',
dateRangeEnd: '2019-01-28T17:52:01.000Z',
});
});
});

View file

@ -13,6 +13,8 @@ import {
Position,
timeFormatter,
BrushEndListener,
XYChartElementEvent,
ElementClickListener,
} from '@elastic/charts';
import { i18n } from '@kbn/i18n';
import React, { useContext } from 'react';
@ -23,12 +25,15 @@ import { HistogramPoint } from '../../../../common/runtime_types';
import { getChartDateLabel, seriesHasDownValues } from '../../../lib/helper';
import { useUrlParams } from '../../../hooks';
import { UptimeThemeContext } from '../../../contexts';
import { getDateRangeFromChartElement } from './utils';
export interface MonitorBarSeriesProps {
/**
* The timeseries data to display.
*/
histogramSeries: HistogramPoint[] | null;
minInterval: number;
}
/**
@ -36,7 +41,7 @@ export interface MonitorBarSeriesProps {
* so we will only render the series component if there are down counts for the selected monitor.
* @param props - the values for the monitor this chart visualizes
*/
export const MonitorBarSeries = ({ histogramSeries }: MonitorBarSeriesProps) => {
export const MonitorBarSeries = ({ histogramSeries, minInterval }: MonitorBarSeriesProps) => {
const {
colors: { danger },
chartTheme,
@ -55,14 +60,23 @@ export const MonitorBarSeries = ({ histogramSeries }: MonitorBarSeriesProps) =>
});
};
const onBarClicked: ElementClickListener = ([elementData]) => {
updateUrlParams(getDateRangeFromChartElement(elementData as XYChartElementEvent, minInterval));
};
const id = 'downSeries';
return seriesHasDownValues(histogramSeries) ? (
<div style={{ height: 50, width: '100%', maxWidth: '1200px', marginRight: 15 }}>
<Chart>
<Settings
xDomain={{ min: absoluteDateRangeStart, max: absoluteDateRangeEnd }}
xDomain={{
minInterval,
min: absoluteDateRangeStart,
max: absoluteDateRangeEnd,
}}
onBrushEnd={onBrushEnd}
onElementClick={onBarClicked}
{...chartTheme}
/>
<Axis

View file

@ -12,6 +12,8 @@ import {
Settings,
timeFormatter,
BrushEndListener,
XYChartElementEvent,
ElementClickListener,
} from '@elastic/charts';
import { EuiTitle, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@ -25,6 +27,7 @@ import { UptimeThemeContext } from '../../../contexts';
import { HistogramResult } from '../../../../common/runtime_types';
import { useUrlParams } from '../../../hooks';
import { ChartEmptyState } from './chart_empty_state';
import { getDateRangeFromChartElement } from './utils';
export interface PingHistogramComponentProps {
/**
@ -79,7 +82,7 @@ export const PingHistogramComponent: React.FC<PingHistogramComponentProps> = ({
/>
);
} else {
const { histogram } = data;
const { histogram, minInterval } = data;
const downSpecId = i18n.translate('xpack.uptime.snapshotHistogram.series.downLabel', {
defaultMessage: 'Down',
@ -100,6 +103,12 @@ export const PingHistogramComponent: React.FC<PingHistogramComponentProps> = ({
});
};
const onBarClicked: ElementClickListener = ([elementData]) => {
updateUrlParams(
getDateRangeFromChartElement(elementData as XYChartElementEvent, minInterval)
);
};
const barData: BarPoint[] = [];
histogram.forEach(({ x, upCount, downCount }) => {
@ -125,11 +134,13 @@ export const PingHistogramComponent: React.FC<PingHistogramComponentProps> = ({
<Chart>
<Settings
xDomain={{
minInterval,
min: absoluteStartDate,
max: absoluteEndDate,
}}
showLegend={false}
onBrushEnd={onBrushEnd}
onElementClick={onBarClicked}
{...chartTheme}
/>
<Axis

View file

@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { XYChartElementEvent } from '@elastic/charts';
import moment from 'moment';
export const getDateRangeFromChartElement = (
elementData: XYChartElementEvent,
minInterval: number
) => {
const startRange = (elementData as XYChartElementEvent)[0].x;
return {
dateRangeStart: moment(startRange).toISOString(),
dateRangeEnd: moment(startRange).add(minInterval, 'ms').toISOString(),
};
};

View file

@ -139,8 +139,8 @@ export const MonitorListComponent: ({
mobileOptions: {
show: false,
},
render: (histogramSeries: HistogramPoint[] | null) => (
<MonitorBarSeries histogramSeries={histogramSeries} />
render: (histogramSeries: HistogramPoint[] | null, summary: MonitorSummary) => (
<MonitorBarSeries histogramSeries={histogramSeries} minInterval={summary.minInterval!} />
),
},
{

View file

@ -22,7 +22,7 @@ Object {
"y": 1,
},
],
"interval": "1m",
"minInterval": 36000,
}
`;
@ -48,7 +48,7 @@ Object {
"y": 1,
},
],
"interval": "1h",
"minInterval": 36000,
}
`;
@ -62,7 +62,7 @@ Object {
"y": 1,
},
],
"interval": "10s",
"minInterval": 36000,
}
`;
@ -82,6 +82,6 @@ Object {
"y": 1,
},
],
"interval": "1m",
"minInterval": 36000,
}
`;

View file

@ -140,8 +140,8 @@ describe('getPingHistogram', () => {
const result = await getPingHistogram({
callES: mockEsClient,
dynamicSettings: DYNAMIC_SETTINGS_DEFAULTS,
from: '1234',
to: '5678',
from: 'now-15m',
to: 'now',
filters: JSON.stringify(searchFilter),
monitorId: undefined,
});

View file

@ -68,13 +68,21 @@ export const getMonitorStates: UMElasticsearchQueryFn<
const iterator = new MonitorSummaryIterator(queryContext);
const page = await iterator.nextPage(size);
const minInterval = getHistogramInterval(
queryContext.dateRangeStart,
queryContext.dateRangeEnd,
12
);
const histograms = await getHistogramForMonitors(
queryContext,
page.monitorSummaries.map((s) => s.monitor_id)
page.monitorSummaries.map((s) => s.monitor_id),
minInterval
);
page.monitorSummaries.forEach((s) => {
s.histogram = histograms[s.monitor_id];
s.minInterval = minInterval;
});
return {
@ -86,7 +94,8 @@ export const getMonitorStates: UMElasticsearchQueryFn<
export const getHistogramForMonitors = async (
queryContext: QueryContext,
monitorIds: string[]
monitorIds: string[],
minInterval: number
): Promise<{ [key: string]: Histogram }> => {
const params = {
index: queryContext.heartbeatIndices,
@ -122,9 +131,7 @@ export const getHistogramForMonitors = async (
field: '@timestamp',
// 12 seems to be a good size for performance given
// long monitor lists of up to 100 on the overview page
fixed_interval:
getHistogramInterval(queryContext.dateRangeStart, queryContext.dateRangeEnd, 12) +
'ms',
fixed_interval: minInterval + 'ms',
missing: 0,
},
aggs: {

View file

@ -37,6 +37,8 @@ export const getPingHistogram: UMElasticsearchQueryFn<
}
const filter = getFilterClause(from, to, additionalFilters);
const minInterval = getHistogramInterval(from, to, QUERY.DEFAULT_BUCKET_COUNT);
const params = {
index: dynamicSettings.heartbeatIndices,
body: {
@ -50,8 +52,7 @@ export const getPingHistogram: UMElasticsearchQueryFn<
timeseries: {
date_histogram: {
field: '@timestamp',
fixed_interval:
bucketSize || getHistogramInterval(from, to, QUERY.DEFAULT_BUCKET_COUNT) + 'ms',
fixed_interval: bucketSize || minInterval + 'ms',
missing: 0,
},
aggs: {
@ -76,7 +77,6 @@ export const getPingHistogram: UMElasticsearchQueryFn<
};
const result = await callES('search', params);
const interval = result.aggregations?.timeseries?.interval;
const buckets: HistogramQueryResult[] = result?.aggregations?.timeseries?.buckets ?? [];
const histogram = buckets.map((bucket) => {
const x: number = bucket.key;
@ -91,6 +91,6 @@ export const getPingHistogram: UMElasticsearchQueryFn<
});
return {
histogram,
interval,
minInterval,
};
};

View file

@ -54,4 +54,4 @@
},
"docId": "h5toHm0B0I9WX_CznN_V",
"timestamp": "2019-09-11T03:40:34.371Z"
}
}

View file

@ -156,5 +156,6 @@
"upCount": 93,
"y": 1
}
]
],
"minInterval": 22801
}

View file

@ -156,5 +156,6 @@
"upCount": 93,
"y": 1
}
]
],
"minInterval": 22801
}

View file

@ -156,5 +156,6 @@
"upCount": 1,
"y": 1
}
]
],
"minInterval": 22801
}