[Uptime] In Ping histogram use auto date histogram (#55605) (#56370)

* update ping histogram API

* update test

* fix tests

* update test

* unused code

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Shahzad 2020-01-30 14:29:02 +01:00 committed by GitHub
parent 7da3097d89
commit 63e6c81b85
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 646 additions and 1182 deletions

View file

@ -4,14 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { HistogramDataPoint } from '../graphql/types';
export interface UMGqlRange {
dateRangeStart: string;
dateRangeEnd: string;
}
export interface HistogramResult {
histogram: HistogramDataPoint[];
interval: number;
}

View file

@ -166,65 +166,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "getSnapshotHistogram",
"description": "",
"args": [
{
"name": "dateRangeStart",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
},
"defaultValue": null
},
{
"name": "dateRangeEnd",
"description": "",
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "SCALAR", "name": "String", "ofType": null }
},
"defaultValue": null
},
{
"name": "filters",
"description": "",
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
"defaultValue": null
},
{
"name": "statusFilter",
"description": "",
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
"defaultValue": null
},
{
"name": "monitorId",
"description": "",
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
"defaultValue": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": { "kind": "OBJECT", "name": "HistogramDataPoint", "ofType": null }
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "getMonitorChartsData",
"description": "",
@ -2172,57 +2113,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "HistogramDataPoint",
"description": "",
"fields": [
{
"name": "upCount",
"description": "",
"args": [],
"type": { "kind": "SCALAR", "name": "Int", "ofType": null },
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "downCount",
"description": "",
"args": [],
"type": { "kind": "SCALAR", "name": "Int", "ofType": null },
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "x",
"description": "",
"args": [],
"type": { "kind": "SCALAR", "name": "UnsignedInteger", "ofType": null },
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "x0",
"description": "",
"args": [],
"type": { "kind": "SCALAR", "name": "UnsignedInteger", "ofType": null },
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "y",
"description": "",
"args": [],
"type": { "kind": "SCALAR", "name": "UnsignedInteger", "ofType": null },
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "MonitorChart",
@ -3944,33 +3834,6 @@
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "DataPoint",
"description": "",
"fields": [
{
"name": "x",
"description": "",
"args": [],
"type": { "kind": "SCALAR", "name": "UnsignedInteger", "ofType": null },
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "y",
"description": "",
"args": [],
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "MonitorDurationAreaPoint",

View file

@ -24,8 +24,6 @@ export interface Query {
getSnapshot?: Snapshot | null;
getSnapshotHistogram: HistogramDataPoint[];
getMonitorChartsData?: MonitorChart | null;
/** Fetch the most recent event data for a monitor ID, date range, location. */
getLatestMonitors: Ping[];
@ -419,17 +417,7 @@ export interface SnapshotCount {
total: number;
}
export interface HistogramDataPoint {
upCount?: number | null;
downCount?: number | null;
x?: UnsignedInteger | null;
x0?: UnsignedInteger | null;
y?: UnsignedInteger | null;
}
/** The data used to populate the monitor charts. */
export interface MonitorChart {
/** The average values for the monitor duration. */
@ -616,47 +604,6 @@ export interface StatesIndexStatus {
docCount?: DocCount | null;
}
export interface DataPoint {
x?: UnsignedInteger | null;
y?: number | null;
}
/** Represents a monitor's duration performance in microseconds at a point in time. */
export interface MonitorDurationAreaPoint {
/** The timeseries value for this point in time. */
x: UnsignedInteger;
/** The min duration value in microseconds at this time. */
yMin?: number | null;
/** The max duration value in microseconds at this point. */
yMax?: number | null;
}
export interface MonitorSummaryUrl {
domain?: string | null;
fragment?: string | null;
full?: string | null;
original?: string | null;
password?: string | null;
path?: string | null;
port?: number | null;
query?: string | null;
scheme?: string | null;
username?: string | null;
}
// ====================================================
// Arguments
// ====================================================
export interface AllPingsQueryArgs {
/** Optional: the direction to sort by. Accepts 'asc' and 'desc'. Defaults to 'desc'. */
sort?: string | null;
@ -673,35 +620,7 @@ export interface AllPingsQueryArgs {
/** Optional: agent location to filter by. */
location?: string | null;
}
export interface GetMonitorsQueryArgs {
dateRangeStart: string;
dateRangeEnd: string;
filters?: string | null;
statusFilter?: string | null;
}
export interface GetSnapshotQueryArgs {
dateRangeStart: string;
dateRangeEnd: string;
filters?: string | null;
statusFilter?: string | null;
}
export interface GetSnapshotHistogramQueryArgs {
dateRangeStart: string;
dateRangeEnd: string;
filters?: string | null;
statusFilter?: string | null;
monitorId?: string | null;
}
export interface GetMonitorChartsDataQueryArgs {
monitorId: string;
@ -711,11 +630,6 @@ export interface GetMonitorChartsDataQueryArgs {
location?: string | null;
}
export interface GetFilterBarQueryArgs {
dateRangeStart: string;
dateRangeEnd: string;
}
export interface GetMonitorStatesQueryArgs {
dateRangeStart: string;

View file

@ -0,0 +1,7 @@
/*
* 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.
*/
export * from './ping/histogram';

View file

@ -0,0 +1,32 @@
/*
* 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.
*/
export type UnsignedInteger = any;
export interface HistogramDataPoint {
upCount?: number | null;
downCount?: number | null;
x?: UnsignedInteger | null;
x0?: UnsignedInteger | null;
y?: UnsignedInteger | null;
}
export interface GetPingHistogramParams {
dateStart: string;
dateEnd: string;
filters?: string;
monitorId?: string;
statusFilter?: string;
}
export interface HistogramResult {
histogram: HistogramDataPoint[];
interval: string;
}

View file

@ -0,0 +1,76 @@
/*
* 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 React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { AppState } from '../../../state';
import {
PingHistogramComponent,
PingHistogramComponentProps,
} from '../../functional/charts/ping_histogram';
import { getPingHistogram } from '../../../state/actions';
import { selectPingHistogram } from '../../../state/selectors';
import { withResponsiveWrapper, ResponsiveWrapperProps } from '../../higher_order';
import { GetPingHistogramParams, HistogramResult } from '../../../../common/types';
type Props = GetPingHistogramParams &
ResponsiveWrapperProps &
PingHistogramComponentProps &
DispatchProps & { lastRefresh: number };
const PingHistogramContainer: React.FC<Props> = ({
data,
loadData,
statusFilter,
filters,
dateStart,
dateEnd,
absoluteStartDate,
absoluteEndDate,
monitorId,
lastRefresh,
...props
}) => {
useEffect(() => {
loadData({ monitorId, dateStart, dateEnd, statusFilter, filters });
}, [loadData, dateStart, dateEnd, monitorId, filters, statusFilter, lastRefresh]);
return (
<PingHistogramComponent
data={data}
absoluteStartDate={absoluteStartDate}
absoluteEndDate={absoluteEndDate}
{...props}
/>
);
};
interface StateProps {
data: HistogramResult | null;
loading: boolean;
lastRefresh: number;
}
interface DispatchProps {
loadData: typeof getPingHistogram;
}
const mapStateToProps = (state: AppState): StateProps => ({ ...selectPingHistogram(state) });
const mapDispatchToProps = (dispatch: any): DispatchProps => ({
loadData: (params: GetPingHistogramParams) => {
return dispatch(getPingHistogram(params));
},
});
export const PingHistogram = connect<
StateProps,
DispatchProps,
PingHistogramComponentProps,
AppState
>(
mapStateToProps,
mapDispatchToProps
)(withResponsiveWrapper(PingHistogramContainer));

View file

@ -0,0 +1,7 @@
/*
* 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.
*/
export { PingHistogram } from './charts/ping_histogram';

View file

@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ChartWrapper component renders the component with loading false 1`] = `
<Fragment>
<EuiErrorBoundary>
<div
intl={
Object {
@ -127,11 +127,11 @@ exports[`ChartWrapper component renders the component with loading false 1`] = `
width={144}
/>
</div>
</Fragment>
</EuiErrorBoundary>
`;
exports[`ChartWrapper component renders the component with loading true 1`] = `
<Fragment>
<EuiErrorBoundary>
<div
intl={
Object {
@ -276,5 +276,5 @@ exports[`ChartWrapper component renders the component with loading true 1`] = `
/>
</EuiFlexItem>
</EuiFlexGroup>
</Fragment>
</EuiErrorBoundary>
`;

View file

@ -0,0 +1,48 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PingHistogram component renders the component without errors 1`] = `
<Fragment>
<EuiTitle
size="xs"
>
<h5>
<FormattedMessage
defaultMessage="Pings over time"
id="xpack.uptime.snapshot.pingsOverTimeTitle"
values={Object {}}
/>
</h5>
</EuiTitle>
<EuiPanel
paddingSize="s"
style={
Object {
"height": 170,
}
}
>
<EuiEmptyPrompt
body={
<p>
<FormattedMessage
defaultMessage="Sorry, there is no data available for the histogram"
id="xpack.uptime.snapshot.noDataDescription"
values={Object {}}
/>
</p>
}
title={
<EuiTitle>
<h5>
<FormattedMessage
defaultMessage="No histogram data available"
id="xpack.uptime.snapshot.noDataTitle"
values={Object {}}
/>
</h5>
</EuiTitle>
}
/>
</EuiPanel>
</Fragment>
`;

View file

@ -1,7 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SnapshotHistogram component renders the component without errors 1`] = `
<ApolloConsumer>
<Component />
</ApolloConsumer>
`;

View file

@ -6,17 +6,16 @@
import React from 'react';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import { SnapshotHistogram, SnapshotHistogramProps } from '../snapshot_histogram';
import { PingHistogramComponent, PingHistogramComponentProps } from '../ping_histogram';
describe('SnapshotHistogram component', () => {
const props: SnapshotHistogramProps = {
describe('PingHistogram component', () => {
const props: PingHistogramComponentProps = {
absoluteStartDate: 1548697920000,
absoluteEndDate: 1548700920000,
isResponsive: false,
};
it('renders the component without errors', () => {
const component = shallowWithIntl(<SnapshotHistogram {...props} variables={{}} />);
const component = shallowWithIntl(<PingHistogramComponent {...props} />);
expect(component).toMatchSnapshot();
});
});

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FC, Fragment, HTMLAttributes } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiLoadingChart } from '@elastic/eui';
import React, { FC, HTMLAttributes } from 'react';
import { EuiErrorBoundary, EuiFlexGroup, EuiFlexItem, EuiLoadingChart } from '@elastic/eui';
interface Props {
/**
@ -31,7 +31,7 @@ export const ChartWrapper: FC<Props> = ({
const opacity = loading === true ? 0.3 : 1;
return (
<Fragment>
<EuiErrorBoundary>
<div
style={{
height,
@ -53,6 +53,6 @@ export const ChartWrapper: FC<Props> = ({
</EuiFlexItem>
</EuiFlexGroup>
)}
</Fragment>
</EuiErrorBoundary>
);
};

View file

@ -7,4 +7,4 @@
export { DonutChart } from './donut_chart';
export { DurationChart } from './duration_chart';
export { MonitorBarSeries } from './monitor_bar_series';
export { SnapshotHistogram } from './snapshot_histogram';
export { PingHistogramComponent } from './ping_histogram';

View file

@ -4,21 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Axis, BarSeries, Chart, Position, timeFormatter, Settings } from '@elastic/charts';
import { EuiEmptyPrompt, EuiTitle, EuiPanel } from '@elastic/eui';
import { Axis, BarSeries, Chart, Position, Settings, timeFormatter } from '@elastic/charts';
import { EuiEmptyPrompt, EuiPanel, EuiTitle } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useContext } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import moment from 'moment';
import { getChartDateLabel } from '../../../lib/helper';
import { withUptimeGraphQL, UptimeGraphQLQueryProps } from '../../higher_order';
import { snapshotHistogramQuery } from '../../../queries/snapshot_histogram_query';
import { ChartWrapper } from './chart_wrapper';
import { UptimeThemeContext } from '../../../contexts';
import { ResponsiveWrapperProps, withResponsiveWrapper } from '../../higher_order';
import { HistogramResult } from '../../../../common/domain_types';
import { HistogramResult } from '../../../../common/types';
interface HistogramProps {
export interface PingHistogramComponentProps {
/**
* The date/time for the start of the timespan.
*/
@ -32,29 +29,23 @@ interface HistogramProps {
* Height is needed, since by default charts takes height of 100%
*/
height?: string;
data?: HistogramResult;
loading?: boolean;
}
export type SnapshotHistogramProps = HistogramProps & ResponsiveWrapperProps;
interface SnapshotHistogramQueryResult {
queryResult?: HistogramResult;
}
type Props = UptimeGraphQLQueryProps<SnapshotHistogramQueryResult> &
SnapshotHistogramProps &
ResponsiveWrapperProps;
export const SnapshotHistogramComponent: React.FC<Props> = ({
export const PingHistogramComponent: React.FC<PingHistogramComponentProps> = ({
absoluteStartDate,
absoluteEndDate,
data,
loading = false,
height,
}: Props) => {
}) => {
const {
colors: { danger, gray },
} = useContext(UptimeThemeContext);
if (!data || !data.queryResult)
if (!data || !data.histogram)
/**
* TODO: the Fragment, EuiTitle, and EuiPanel should be extracted to a dumb component
* that we can reuse in the subsequent return statement at the bottom of this function.
@ -93,19 +84,15 @@ export const SnapshotHistogramComponent: React.FC<Props> = ({
</EuiPanel>
</>
);
const {
queryResult: { histogram, interval },
} = data;
const { histogram } = data;
const downMonitorsId = i18n.translate('xpack.uptime.snapshotHistogram.downMonitorsId', {
const downSpecId = i18n.translate('xpack.uptime.snapshotHistogram.downMonitorsId', {
defaultMessage: 'Down Monitors',
});
const downSpecId = downMonitorsId;
const upMonitorsId = i18n.translate('xpack.uptime.snapshotHistogram.series.upLabel', {
defaultMessage: 'Up',
});
const upSpecId = upMonitorsId;
return (
<>
<EuiTitle size="xs">
@ -131,7 +118,6 @@ export const SnapshotHistogramComponent: React.FC<Props> = ({
<Chart>
<Settings
xDomain={{
minInterval: interval,
min: absoluteStartDate,
max: absoluteEndDate,
}}
@ -139,7 +125,7 @@ export const SnapshotHistogramComponent: React.FC<Props> = ({
/>
<Axis
id={i18n.translate('xpack.uptime.snapshotHistogram.xAxisId', {
defaultMessage: 'Snapshot X Axis',
defaultMessage: 'Ping X Axis',
})}
position={Position.Bottom}
showOverlappingTicks={false}
@ -147,7 +133,7 @@ export const SnapshotHistogramComponent: React.FC<Props> = ({
/>
<Axis
id={i18n.translate('xpack.uptime.snapshotHistogram.yAxisId', {
defaultMessage: 'Snapshot Y Axis',
defaultMessage: 'Ping Y Axis',
})}
position="left"
title={i18n.translate('xpack.uptime.snapshotHistogram.yAxis.title', {
@ -173,7 +159,7 @@ export const SnapshotHistogramComponent: React.FC<Props> = ({
<BarSeries
customSeriesColors={[gray]}
data={histogram.map(({ x, upCount }) => [x, upCount || 0])}
id={upSpecId}
id={upMonitorsId}
name={upMonitorsId}
stackAccessors={[0]}
timeZone="local"
@ -187,8 +173,3 @@ export const SnapshotHistogramComponent: React.FC<Props> = ({
</>
);
};
export const SnapshotHistogram = withUptimeGraphQL<
SnapshotHistogramQueryResult,
SnapshotHistogramProps
>(withResponsiveWrapper<Props>(SnapshotHistogramComponent), snapshotHistogramQuery);

View file

@ -15,5 +15,5 @@ export { MonitorList } from './monitor_list';
export { OverviewPageParsingErrorCallout } from './overview_page_parsing_error_callout';
export { PingList } from './ping_list';
export { Snapshot } from './snapshot';
export { SnapshotHistogram } from './charts';
export { PingHistogramComponent } from './charts';
export { StatusPanel } from './status_panel';

View file

@ -11,8 +11,8 @@ import { MonitorChart } from '../../../common/graphql/types';
import { UptimeGraphQLQueryProps, withUptimeGraphQL } from '../higher_order';
import { monitorChartsQuery } from '../../queries';
import { DurationChart } from './charts';
import { SnapshotHistogram } from './charts/snapshot_histogram';
import { useUrlParams } from '../../hooks';
import { PingHistogram } from '../connected';
interface MonitorChartsQueryResult {
monitorChartsData?: MonitorChart;
@ -58,12 +58,14 @@ export const MonitorChartsComponent = ({
/>
</EuiFlexItem>
<EuiFlexItem>
<SnapshotHistogram
<PingHistogram
absoluteStartDate={absoluteDateRangeStart}
absoluteEndDate={absoluteDateRangeEnd}
height="400px"
isResponsive={false}
variables={{ dateRangeStart, dateRangeEnd, monitorId }}
dateStart={dateRangeStart}
dateEnd={dateRangeEnd}
monitorId={monitorId}
/>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -6,8 +6,8 @@
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
import React from 'react';
import { SnapshotHistogram } from './charts';
import { Snapshot } from './snapshot';
import { PingHistogram } from '../connected';
interface StatusPanelProps {
absoluteDateRangeStart: number;
@ -16,7 +16,6 @@ interface StatusPanelProps {
dateRangeEnd: string;
filters?: string;
statusFilter?: string;
sharedProps: { [key: string]: any };
}
const STATUS_CHART_HEIGHT = '160px';
@ -28,7 +27,6 @@ export const StatusPanel = ({
dateRangeEnd,
filters,
statusFilter,
sharedProps,
}: StatusPanelProps) => (
<EuiPanel>
<EuiFlexGroup gutterSize="l">
@ -42,12 +40,15 @@ export const StatusPanel = ({
/>
</EuiFlexItem>
<EuiFlexItem grow={10}>
<SnapshotHistogram
<PingHistogram
absoluteStartDate={absoluteDateRangeStart}
absoluteEndDate={absoluteDateRangeEnd}
dateStart={dateRangeStart}
dateEnd={dateRangeEnd}
filters={filters}
height={STATUS_CHART_HEIGHT}
statusFilter={statusFilter}
isResponsive={true}
variables={sharedProps}
/>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -135,7 +135,6 @@ export const OverviewPage = ({ autocomplete, setBreadcrumbs }: Props) => {
dateRangeEnd={dateRangeEnd}
filters={filters}
statusFilter={statusFilter}
sharedProps={sharedProps}
/>
<EuiSpacer size="s" />
<MonitorList

View file

@ -1,38 +0,0 @@
/*
* 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 gql from 'graphql-tag';
export const snapshotHistogramQueryString = `
query SnapshotHistogram(
$dateRangeStart: String!
$dateRangeEnd: String!
$filters: String
$monitorId: String
$statusFilter: String
) {
queryResult: getSnapshotHistogram(
dateRangeStart: $dateRangeStart
dateRangeEnd: $dateRangeEnd
filters: $filters
statusFilter: $statusFilter
monitorId: $monitorId
) {
histogram {
upCount
downCount
x
x0
y
}
interval
}
}
`;
export const snapshotHistogramQuery = gql`
${snapshotHistogramQueryString}
`;

View file

@ -8,3 +8,4 @@ export * from './overview_filters';
export * from './snapshot';
export * from './ui';
export * from './monitor_status';
export * from './ping';

View file

@ -0,0 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { createAction } from 'redux-actions';
import { GetPingHistogramParams, HistogramResult } from '../../../common/types';
export const getPingHistogram = createAction<GetPingHistogramParams>('GET_PING_HISTOGRAM');
export const getPingHistogramSuccess = createAction<HistogramResult>('GET_PING_HISTOGRAM_SUCCESS');
export const getPingHistogramFail = createAction<Error>('GET_PING_HISTOGRAM_FAIL');

View file

@ -8,3 +8,4 @@ export * from './monitor';
export * from './overview_filters';
export * from './snapshot';
export * from './monitor_status';
export * from './ping';

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { ThrowReporter } from 'io-ts/lib/ThrowReporter';
import { PathReporter } from 'io-ts/lib/PathReporter';
import { getApiPath } from '../../lib/helper';
import { BaseParams } from './types';
import {
@ -41,7 +41,7 @@ export const fetchMonitorDetails = async ({
throw new Error(response.statusText);
}
return response.json().then(data => {
ThrowReporter.report(MonitorDetailsType.decode(data));
PathReporter.report(MonitorDetailsType.decode(data));
return data;
});
};
@ -68,7 +68,7 @@ export const fetchMonitorLocations = async ({
throw new Error(response.statusText);
}
return response.json().then(data => {
ThrowReporter.report(MonitorLocationsType.decode(data));
PathReporter.report(MonitorLocationsType.decode(data));
return data;
});
};

View file

@ -0,0 +1,35 @@
/*
* 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 qs from 'querystring';
import { getApiPath } from '../../lib/helper';
import { APIFn } from './types';
import { GetPingHistogramParams, HistogramResult } from '../../../common/types';
export const fetchPingHistogram: APIFn<GetPingHistogramParams, HistogramResult> = async ({
basePath,
monitorId,
dateStart,
dateEnd,
statusFilter,
filters,
}) => {
const url = getApiPath(`/api/uptime/ping/histogram`, basePath);
const params = {
dateStart,
dateEnd,
...(monitorId && { monitorId }),
...(statusFilter && { statusFilter }),
...(filters && { filters }),
};
const urlParams = qs.stringify(params).toString();
const response = await fetch(`${url}?${urlParams}`);
if (!response.ok) {
throw new Error(response.statusText);
}
const responseData = await response.json();
return responseData;
};

View file

@ -12,3 +12,5 @@ export interface BaseParams {
statusFilter?: string;
location?: string;
}
export type APIFn<P, R = any> = (params: { basePath: string } & P) => Promise<R>;

View file

@ -9,10 +9,12 @@ import { fetchMonitorDetailsEffect } from './monitor';
import { fetchOverviewFiltersEffect } from './overview_filters';
import { fetchSnapshotCountEffect } from './snapshot';
import { fetchMonitorStatusEffect } from './monitor_status';
import { fetchPingHistogramEffect } from './ping';
export function* rootEffect() {
yield fork(fetchMonitorDetailsEffect);
yield fork(fetchSnapshotCountEffect);
yield fork(fetchOverviewFiltersEffect);
yield fork(fetchMonitorStatusEffect);
yield fork(fetchPingHistogramEffect);
}

View file

@ -0,0 +1,17 @@
/*
* 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 { takeLatest } from 'redux-saga/effects';
import { getPingHistogram, getPingHistogramSuccess, getPingHistogramFail } from '../actions';
import { fetchPingHistogram } from '../api';
import { fetchEffectFactory } from './fetch_effect';
export function* fetchPingHistogramEffect() {
yield takeLatest(
String(getPingHistogram),
fetchEffectFactory(fetchPingHistogram, getPingHistogramSuccess, getPingHistogramFail)
);
}

View file

@ -10,6 +10,7 @@ import { overviewFiltersReducer } from './overview_filters';
import { snapshotReducer } from './snapshot';
import { uiReducer } from './ui';
import { monitorStatusReducer } from './monitor_status';
import { pingReducer } from './ping';
export const rootReducer = combineReducers({
monitor: monitorReducer,
@ -17,4 +18,5 @@ export const rootReducer = combineReducers({
snapshot: snapshotReducer,
ui: uiReducer,
monitorStatus: monitorStatusReducer,
ping: pingReducer,
});

View file

@ -0,0 +1,45 @@
/*
* 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 { handleActions, Action } from 'redux-actions';
import { getPingHistogram, getPingHistogramSuccess, getPingHistogramFail } from '../actions';
import { HistogramResult } from '../../../common/types';
export interface PingState {
pingHistogram: HistogramResult | null;
errors: any[];
loading: boolean;
}
const initialState: PingState = {
pingHistogram: null,
loading: false,
errors: [],
};
type MonitorStatusPayload = HistogramResult & Error;
export const pingReducer = handleActions<PingState, MonitorStatusPayload>(
{
[String(getPingHistogram)]: state => ({
...state,
loading: true,
}),
[String(getPingHistogramSuccess)]: (state: PingState, action: Action<HistogramResult>) => ({
...state,
loading: false,
pingHistogram: { ...action.payload },
}),
[String(getPingHistogramFail)]: (state, action: Action<Error>) => ({
...state,
errors: [...state.errors, action.payload],
loading: false,
}),
},
initialState
);

View file

@ -45,6 +45,11 @@ describe('state selectors', () => {
monitor: null,
loading: false,
},
ping: {
pingHistogram: null,
loading: false,
errors: [],
},
};
it('selects base path from state', () => {

View file

@ -28,3 +28,7 @@ export const selectSelectedMonitor = (state: AppState) => {
export const selectMonitorStatus = (state: AppState) => {
return state.monitorStatus.status;
};
export const selectPingHistogram = ({ ping, ui }: AppState) => {
return { data: ping.pingHistogram, loading: ping.loading, lastRefresh: ui.lastRefresh };
};

View file

@ -4,19 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { UMGqlRange } from '../../../common/domain_types';
import { UMResolver } from '../../../common/graphql/resolver_types';
import {
GetFilterBarQueryArgs,
GetMonitorChartsDataQueryArgs,
MonitorChart,
GetSnapshotHistogramQueryArgs,
} from '../../../common/graphql/types';
import { GetMonitorChartsDataQueryArgs, MonitorChart } from '../../../common/graphql/types';
import { UMServerLibs } from '../../lib/lib';
import { CreateUMGraphQLResolvers, UMContext } from '../types';
import { HistogramResult } from '../../../common/domain_types';
export type UMMonitorsResolver = UMResolver<any | Promise<any>, any, UMGqlRange, UMContext>;
export type UMGetMonitorChartsResolver = UMResolver<
any | Promise<any>,
@ -25,49 +16,20 @@ export type UMGetMonitorChartsResolver = UMResolver<
UMContext
>;
export type UMGetFilterBarResolver = UMResolver<
any | Promise<any>,
any,
GetFilterBarQueryArgs,
UMContext
>;
export type UMGetSnapshotHistogram = UMResolver<
HistogramResult | Promise<HistogramResult>,
any,
GetSnapshotHistogramQueryArgs,
UMContext
>;
export const createMonitorsResolvers: CreateUMGraphQLResolvers = (
libs: UMServerLibs
): {
Query: {
getSnapshotHistogram: UMGetSnapshotHistogram;
getMonitorChartsData: UMGetMonitorChartsResolver;
};
} => ({
Query: {
async getSnapshotHistogram(
_resolver,
{ dateRangeStart, dateRangeEnd, filters, monitorId, statusFilter },
{ APICaller }
): Promise<HistogramResult> {
return await libs.pings.getPingHistogram({
callES: APICaller,
dateRangeStart,
dateRangeEnd,
filters,
monitorId,
statusFilter,
});
},
async getMonitorChartsData(
_resolver,
{ monitorId, dateRangeStart, dateRangeEnd, location },
{ APICaller }
): Promise<MonitorChart> {
return await libs.monitors.getMonitorChartsData({
return libs.monitors.getMonitorChartsData({
callES: APICaller,
monitorId,
dateRangeStart,

View file

@ -7,19 +7,6 @@
import gql from 'graphql-tag';
export const monitorsSchema = gql`
type HistogramDataPoint {
upCount: Int
downCount: Int
x: UnsignedInteger
x0: UnsignedInteger
y: UnsignedInteger
}
type DataPoint {
x: UnsignedInteger
y: Float
}
"Represents a bucket of monitor status information."
type StatusData {
"The timeseries point for this status data."
@ -93,11 +80,6 @@ export const monitorsSchema = gql`
monitors: [LatestMonitor!]
}
type HistogramResult {
histogram: [HistogramDataPoint]!
interval: UnsignedInteger!
}
extend type Query {
getMonitors(
dateRangeStart: String!
@ -106,14 +88,6 @@ export const monitorsSchema = gql`
statusFilter: String
): LatestMonitorsResult
getSnapshotHistogram(
dateRangeStart: String!
dateRangeEnd: String!
filters: String
statusFilter: String
monitorId: String
): HistogramResult
getMonitorChartsData(
monitorId: String!
dateRangeStart: String!

View file

@ -22,7 +22,7 @@ Object {
"y": 1,
},
],
"interval": 36000,
"interval": "1m",
}
`;
@ -48,7 +48,7 @@ Object {
"y": 1,
},
],
"interval": 5609564928000,
"interval": "1h",
}
`;
@ -68,7 +68,7 @@ Object {
"y": 1,
},
],
"interval": 5609564928000,
"interval": "1d",
}
`;
@ -88,7 +88,7 @@ Object {
"y": 1,
},
],
"interval": 5609564928000,
"interval": "1s",
}
`;
@ -102,7 +102,7 @@ Object {
"y": 1,
},
],
"interval": 36000,
"interval": "10s",
}
`;
@ -122,6 +122,6 @@ Object {
"y": 1,
},
],
"interval": 36000,
"interval": "1m",
}
`;

View file

@ -5,8 +5,7 @@
*/
import { set } from 'lodash';
import { elasticsearchPingsAdapter as adapter } from '../elasticsearch_pings_adapter';
import { assertCloseTo } from '../../../helper';
import { elasticsearchPingsAdapter as adapter } from '../es_pings';
describe('ElasticsearchPingsAdapter class', () => {
let mockHits: any[];
@ -35,6 +34,7 @@ describe('ElasticsearchPingsAdapter class', () => {
},
},
],
interval: '1s',
},
},
};
@ -98,12 +98,11 @@ describe('ElasticsearchPingsAdapter class', () => {
});
const result = await adapter.getPingHistogram({
callES: mockEsClient,
dateRangeStart: 'now-15m',
dateRangeEnd: 'now',
filters: null,
dateStart: 'now-15m',
dateEnd: 'now',
filters: '',
});
assertCloseTo(result.interval, 36000, 100);
result.interval = 36000;
result.interval = '10s';
expect(mockEsClient).toHaveBeenCalledTimes(1);
expect(result).toMatchSnapshot();
});
@ -116,12 +115,11 @@ describe('ElasticsearchPingsAdapter class', () => {
const result = await adapter.getPingHistogram({
callES: mockEsClient,
dateRangeStart: 'now-15m',
dateRangeEnd: 'now',
filters: null,
dateStart: 'now-15m',
dateEnd: 'now',
filters: '',
});
assertCloseTo(result.interval, 36000, 100);
result.interval = 36000;
result.interval = '1m';
expect(mockEsClient).toHaveBeenCalledTimes(1);
expect(result).toMatchSnapshot();
@ -175,14 +173,13 @@ describe('ElasticsearchPingsAdapter class', () => {
};
const result = await adapter.getPingHistogram({
callES: mockEsClient,
dateRangeStart: '1234',
dateRangeEnd: '5678',
dateStart: '1234',
dateEnd: '5678',
filters: JSON.stringify(searchFilter),
monitorId: undefined,
statusFilter: 'down',
});
assertCloseTo(result.interval, 5609564928000, 1000);
result.interval = 5609564928000;
result.interval = '1h';
expect(mockEsClient).toHaveBeenCalledTimes(1);
expect(result).toMatchSnapshot();
@ -229,13 +226,12 @@ describe('ElasticsearchPingsAdapter class', () => {
const filters = `{"bool":{"must":[{"simple_query_string":{"query":"http"}}]}}`;
const result = await adapter.getPingHistogram({
callES: mockEsClient,
dateRangeStart: 'now-15m',
dateRangeEnd: 'now',
dateStart: 'now-15m',
dateEnd: 'now',
filters,
});
assertCloseTo(result.interval, 36000, 100);
result.interval = 36000;
result.interval = '1m';
expect(mockEsClient).toHaveBeenCalledTimes(1);
expect(result).toMatchSnapshot();
});
@ -246,14 +242,14 @@ describe('ElasticsearchPingsAdapter class', () => {
mockEsClient.mockReturnValue(standardMockResponse);
const result = await adapter.getPingHistogram({
callES: mockEsClient,
dateRangeStart: '1234',
dateRangeEnd: '5678',
dateStart: '1234',
dateEnd: '5678',
filters: '',
monitorId: undefined,
statusFilter: 'down',
});
assertCloseTo(result.interval, 5609564928000, 1000);
result.interval = 5609564928000;
result.interval = '1d';
expect(mockEsClient).toHaveBeenCalledTimes(1);
expect(result).toMatchSnapshot();
@ -267,8 +263,8 @@ describe('ElasticsearchPingsAdapter class', () => {
const result = await adapter.getPingHistogram({
callES: mockEsClient,
dateRangeStart: '1234',
dateRangeEnd: '5678',
dateStart: '1234',
dateEnd: '5678',
filters: '',
monitorId: undefined,
statusFilter: 'up',

View file

@ -0,0 +1,82 @@
/*
* 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 { get } from 'lodash';
import { INDEX_NAMES, QUERY } from '../../../../common/constants';
import { parseFilterQuery, getFilterClause } from '../../helper';
import { UMElasticsearchQueryFn } from '../framework';
import { GetPingHistogramParams, HistogramResult } from '../../../../common/types';
import { HistogramQueryResult } from './types';
export const esGetPingHistogram: UMElasticsearchQueryFn<
GetPingHistogramParams,
HistogramResult
> = async ({ callES, dateStart, dateEnd, filters, monitorId, statusFilter }) => {
const boolFilters = parseFilterQuery(filters);
const additionalFilters = [];
if (monitorId) {
additionalFilters.push({ match: { 'monitor.id': monitorId } });
}
if (boolFilters) {
additionalFilters.push(boolFilters);
}
const filter = getFilterClause(dateStart, dateEnd, additionalFilters);
const params = {
index: INDEX_NAMES.HEARTBEAT,
body: {
query: {
bool: {
filter,
},
},
size: 0,
aggs: {
timeseries: {
auto_date_histogram: {
field: '@timestamp',
buckets: QUERY.DEFAULT_BUCKET_COUNT,
},
aggs: {
down: {
filter: {
term: {
'monitor.status': 'down',
},
},
},
up: {
filter: {
term: {
'monitor.status': 'up',
},
},
},
},
},
},
},
};
const result = await callES('search', params);
const interval = result.aggregations.timeseries?.interval;
const buckets: HistogramQueryResult[] = get(result, 'aggregations.timeseries.buckets', []);
const histogram = buckets.map(bucket => {
const x: number = get(bucket, 'key');
const downCount: number = get(bucket, 'down.doc_count');
const upCount: number = get(bucket, 'up.doc_count');
return {
x,
downCount: statusFilter && statusFilter !== 'down' ? 0 : downCount,
upCount: statusFilter && statusFilter !== 'up' ? 0 : upCount,
y: 1,
};
});
return {
histogram,
interval,
};
};

View file

@ -7,9 +7,8 @@
import { get } from 'lodash';
import { INDEX_NAMES } from '../../../../common/constants';
import { HttpBody, Ping, PingResults } from '../../../../common/graphql/types';
import { parseFilterQuery, getFilterClause, getHistogramIntervalFormatted } from '../../helper';
import { UMPingsAdapter, HistogramQueryResult } from './adapter_types';
import { getHistogramInterval } from '../../helper/get_histogram_interval';
import { UMPingsAdapter } from './types';
import { esGetPingHistogram } from './es_get_ping_historgram';
export const elasticsearchPingsAdapter: UMPingsAdapter = {
getAll: async ({
@ -174,80 +173,7 @@ export const elasticsearchPingsAdapter: UMPingsAdapter = {
return result.hits.hits[0]?._source;
},
getPingHistogram: async ({
callES,
dateRangeStart,
dateRangeEnd,
filters,
monitorId,
statusFilter,
}) => {
const boolFilters = parseFilterQuery(filters);
const additionalFilters = [];
if (monitorId) {
additionalFilters.push({ match: { 'monitor.id': monitorId } });
}
if (boolFilters) {
additionalFilters.push(boolFilters);
}
const filter = getFilterClause(dateRangeStart, dateRangeEnd, additionalFilters);
const interval = getHistogramInterval(dateRangeStart, dateRangeEnd);
const intervalFormatted = getHistogramIntervalFormatted(dateRangeStart, dateRangeEnd);
const params = {
index: INDEX_NAMES.HEARTBEAT,
body: {
query: {
bool: {
filter,
},
},
size: 0,
aggs: {
timeseries: {
date_histogram: {
field: '@timestamp',
fixed_interval: intervalFormatted,
},
aggs: {
down: {
filter: {
term: {
'monitor.status': 'down',
},
},
},
up: {
filter: {
term: {
'monitor.status': 'up',
},
},
},
},
},
},
},
};
const result = await callES('search', params);
const buckets: HistogramQueryResult[] = get(result, 'aggregations.timeseries.buckets', []);
const histogram = buckets.map(bucket => {
const x: number = get(bucket, 'key');
const downCount: number = get(bucket, 'down.doc_count');
const upCount: number = get(bucket, 'up.doc_count');
return {
x,
downCount: statusFilter && statusFilter !== 'down' ? 0 : downCount,
upCount: statusFilter && statusFilter !== 'up' ? 0 : upCount,
y: 1,
};
});
return {
histogram,
interval,
};
},
getPingHistogram: esGetPingHistogram,
getDocCount: async ({ callES }) => {
const { count } = await callES('count', { index: INDEX_NAMES.HEARTBEAT });

View file

@ -4,5 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/
export * from './adapter_types';
export { elasticsearchPingsAdapter } from './elasticsearch_pings_adapter';
export * from './types';
export { elasticsearchPingsAdapter } from './es_pings';

View file

@ -5,8 +5,8 @@
*/
import { DocCount, Ping, PingResults } from '../../../../common/graphql/types';
import { HistogramResult } from '../../../../common/domain_types';
import { UMElasticsearchQueryFn } from '../framework';
import { GetPingHistogramParams, HistogramResult } from '../../../../common/types';
export interface GetAllParams {
/** @member dateRangeStart timestamp bounds */
@ -42,19 +42,6 @@ export interface GetLatestMonitorDocsParams {
monitorId?: string | null;
}
export interface GetPingHistogramParams {
/** @member dateRangeStart timestamp bounds */
dateRangeStart: string;
/** @member dateRangeEnd timestamp bounds */
dateRangeEnd: string;
/** @member filters user-defined filters */
filters?: string | null;
/** @member monitorId optional limit to monitorId */
monitorId?: string | null;
/** @member statusFilter special filter targeting the latest status of each monitor */
statusFilter?: string | null;
}
/**
* Count the number of documents in heartbeat indices
*/

View file

@ -8,4 +8,9 @@ export const assertCloseTo = (actual: number, expected: number, precision: numbe
if (Math.abs(expected - actual) > precision) {
throw new Error(`expected [${expected}] to be within ${precision} of ${actual}`);
}
// if actual is undefined above math condition will be NAN and it will be always false
if (actual === undefined) {
throw new Error(`expected close to [${expected}] but got [${actual}]`);
}
};

View file

@ -5,7 +5,6 @@
*/
export { getFilterClause } from './get_filter_clause';
export { getHistogramInterval } from './get_histogram_interval';
export { getHistogramIntervalFormatted } from './get_histogram_interval_formatted';
export { parseFilterQuery } from './parse_filter_query';
export { assertCloseTo } from './assert_close_to';

View file

@ -16,6 +16,7 @@ import {
createGetMonitorLocationsRoute,
createGetStatusBarRoute,
} from './monitors';
import { createGetPingHistogramRoute } from './pings/get_ping_histogram';
export * from './types';
export { createRouteWithAuth } from './create_route_with_auth';
@ -31,4 +32,5 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [
createGetSnapshotCount,
createLogMonitorPageRoute,
createLogOverviewPageRoute,
createGetPingHistogramRoute,
];

View file

@ -0,0 +1,44 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { UMServerLibs } from '../../lib/lib';
import { UMRestApiRouteFactory } from '../types';
export const createGetPingHistogramRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({
method: 'GET',
path: '/api/uptime/ping/histogram',
validate: {
query: schema.object({
dateStart: schema.string(),
dateEnd: schema.string(),
monitorId: schema.maybe(schema.string()),
statusFilter: schema.maybe(schema.string()),
filters: schema.maybe(schema.string()),
}),
},
options: {
tags: ['access:uptime'],
},
handler: async ({ callES }, _context, request, response): Promise<any> => {
const { dateStart, dateEnd, statusFilter, monitorId, filters } = request.query;
const result = await libs.pings.getPingHistogram({
callES,
dateStart,
dateEnd,
monitorId,
statusFilter,
filters,
});
return response.ok({
body: {
...result,
},
});
},
});

View file

@ -1,188 +0,0 @@
{
"queryResult": {
"histogram": [
{
"upCount": 93,
"downCount": 7,
"x": 1568172657286,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172680087,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172702888,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172725689,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172748490,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172771291,
"x0": null,
"y": 1
},
{
"upCount": 92,
"downCount": 8,
"x": 1568172794092,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172816893,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172839694,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172862495,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172885296,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172908097,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172930898,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172953699,
"x0": null,
"y": 1
},
{
"upCount": 92,
"downCount": 8,
"x": 1568172976500,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172999301,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173022102,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173044903,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173067704,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568173090505,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173113306,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173136107,
"x0": null,
"y": 1
},
{
"upCount": 92,
"downCount": 8,
"x": 1568173158908,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173181709,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568173204510,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173227311,
"x0": null,
"y": 1
}
]
}
}

View file

@ -1,188 +0,0 @@
{
"queryResult": {
"histogram": [
{
"upCount": 93,
"downCount": 0,
"x": 1568172657286,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568172680087,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568172702888,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172725689,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568172748490,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568172771291,
"x0": null,
"y": 1
},
{
"upCount": 92,
"downCount": 0,
"x": 1568172794092,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172816893,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568172839694,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568172862495,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568172885296,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172908097,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568172930898,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568172953699,
"x0": null,
"y": 1
},
{
"upCount": 92,
"downCount": 0,
"x": 1568172976500,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172999301,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568173022102,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568173044903,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568173067704,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568173090505,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568173113306,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568173136107,
"x0": null,
"y": 1
},
{
"upCount": 92,
"downCount": 0,
"x": 1568173158908,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568173181709,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568173204510,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 0,
"x": 1568173227311,
"x0": null,
"y": 1
}
]
}
}

View file

@ -1,188 +0,0 @@
{
"queryResult": {
"histogram": [
{
"upCount": 93,
"downCount": 7,
"x": 1568172657286,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172680087,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172702888,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172725689,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172748490,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172771291,
"x0": null,
"y": 1
},
{
"upCount": 92,
"downCount": 8,
"x": 1568172794092,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172816893,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172839694,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172862495,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172885296,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172908097,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172930898,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568172953699,
"x0": null,
"y": 1
},
{
"upCount": 92,
"downCount": 8,
"x": 1568172976500,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568172999301,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173022102,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173044903,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173067704,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568173090505,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173113306,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173136107,
"x0": null,
"y": 1
},
{
"upCount": 92,
"downCount": 8,
"x": 1568173158908,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173181709,
"x0": null,
"y": 1
},
{
"upCount": 0,
"downCount": 0,
"x": 1568173204510,
"x0": null,
"y": 1
},
{
"upCount": 93,
"downCount": 7,
"x": 1568173227311,
"x0": null,
"y": 1
}
]
}
}

View file

@ -14,6 +14,5 @@ export default function({ loadTestFile }) {
loadTestFile(require.resolve('./monitor_charts'));
loadTestFile(require.resolve('./monitor_states'));
loadTestFile(require.resolve('./ping_list'));
loadTestFile(require.resolve('./snapshot_histogram'));
});
}

View file

@ -1,88 +0,0 @@
/*
* 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 { snapshotHistogramQueryString } from '../../../../../legacy/plugins/uptime/public/queries/snapshot_histogram_query';
import { expectFixtureEql } from './helpers/expect_fixture_eql';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { assertCloseTo } from '../../../../../legacy/plugins/uptime/server/lib/helper';
export default function({ getService }: FtrProviderContext) {
describe('snapshotHistogram', () => {
before('load heartbeat data', () => getService('esArchiver').load('uptime/full_heartbeat'));
after('unload heartbeat index', () => getService('esArchiver').unload('uptime/full_heartbeat'));
const supertest = getService('supertest');
it('will fetch histogram data for all monitors', async () => {
const getSnapshotHistogramQuery = {
operationName: 'SnapshotHistogram',
query: snapshotHistogramQueryString,
variables: {
dateRangeStart: '2019-09-11T03:31:04.380Z',
dateRangeEnd: '2019-09-11T03:40:34.410Z',
},
};
const {
body: { data },
} = await supertest
.post('/api/uptime/graphql')
.set('kbn-xsrf', 'foo')
.send({ ...getSnapshotHistogramQuery });
// manually testing this value and then removing it to avoid flakiness
const { interval } = data.queryResult;
assertCloseTo(interval, 22801, 100);
delete data.queryResult.interval;
expectFixtureEql(data, 'snapshot_histogram');
});
it('will fetch histogram data for a given monitor id', async () => {
const getSnapshotHistogramQuery = {
operationName: 'SnapshotHistogram',
query: snapshotHistogramQueryString,
variables: {
dateRangeStart: '2019-09-11T03:31:04.380Z',
dateRangeEnd: '2019-09-11T03:40:34.410Z',
},
};
const {
body: { data },
} = await supertest
.post('/api/uptime/graphql')
.set('kbn-xsrf', 'foo')
.send({ ...getSnapshotHistogramQuery });
const { interval } = data.queryResult;
assertCloseTo(interval, 22801, 100);
delete data.queryResult.interval;
expectFixtureEql(data, 'snapshot_histogram_by_id');
});
it('will fetch histogram data for a given filter', async () => {
const getSnapshotHistogramQuery = {
operationName: 'SnapshotHistogram',
query: snapshotHistogramQueryString,
variables: {
dateRangeStart: '2019-09-11T03:31:04.380Z',
dateRangeEnd: '2019-09-11T03:40:34.410Z',
filters:
'{"bool":{"must":[{"match":{"monitor.status":{"query":"up","operator":"and"}}}]}}',
},
};
const {
body: { data },
} = await supertest
.post('/api/uptime/graphql')
.set('kbn-xsrf', 'foo')
.send({ ...getSnapshotHistogramQuery });
const { interval } = data.queryResult;
assertCloseTo(interval, 22801, 100);
delete data.queryResult.interval;
expectFixtureEql(data, 'snapshot_histogram_by_filter');
});
});
}

View file

@ -0,0 +1,24 @@
{
"histogram": [
{ "x": 1568172664000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568172694000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568172724000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568172754000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568172784000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568172814000, "downCount": 8, "upCount": 92, "y": 1 },
{ "x": 1568172844000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568172874000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568172904000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568172934000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568172964000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568172994000, "downCount": 8, "upCount": 92, "y": 1 },
{ "x": 1568173024000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568173054000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568173084000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568173114000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568173144000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568173174000, "downCount": 8, "upCount": 92, "y": 1 },
{ "x": 1568173204000, "downCount": 7, "upCount": 93, "y": 1 },
{ "x": 1568173234000, "downCount": 7, "upCount": 93, "y": 1 }
]
}

View file

@ -0,0 +1,24 @@
{
"histogram": [
{ "x": 1568172664000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568172694000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568172724000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568172754000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568172784000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568172814000, "downCount": 0, "upCount": 92, "y": 1 },
{ "x": 1568172844000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568172874000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568172904000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568172934000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568172964000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568172994000, "downCount": 0, "upCount": 92, "y": 1 },
{ "x": 1568173024000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568173054000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568173084000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568173114000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568173144000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568173174000, "downCount": 0, "upCount": 92, "y": 1 },
{ "x": 1568173204000, "downCount": 0, "upCount": 93, "y": 1 },
{ "x": 1568173234000, "downCount": 0, "upCount": 93, "y": 1 }
]
}

View file

@ -0,0 +1,24 @@
{
"histogram": [
{ "x": 1568172664000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568172694000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568172724000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568172754000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568172784000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568172814000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568172844000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568172874000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568172904000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568172934000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568172964000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568172994000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568173024000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568173054000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568173084000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568173114000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568173144000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568173174000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568173204000, "downCount": 0, "upCount": 1, "y": 1 },
{ "x": 1568173234000, "downCount": 0, "upCount": 1, "y": 1 }
]
}

View file

@ -19,6 +19,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) {
after('unload', () => esArchiver.unload('uptime/full_heartbeat'));
loadTestFile(require.resolve('./monitor_latest_status'));
loadTestFile(require.resolve('./selected_monitor'));
loadTestFile(require.resolve('./ping_histogram'));
});
});
}

View file

@ -0,0 +1,64 @@
/*
* 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 { expectFixtureEql } from '../graphql/helpers/expect_fixture_eql';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { assertCloseTo } from '../../../../../legacy/plugins/uptime/server/lib/helper';
export default function({ getService }: FtrProviderContext) {
describe('pingHistogram', () => {
const supertest = getService('supertest');
it('will fetch histogram data for all monitors', async () => {
const dateStart = '2019-09-11T03:31:04.380Z';
const dateEnd = '2019-09-11T03:40:34.410Z';
const apiResponse = await supertest.get(
`/api/uptime/ping/histogram?dateStart=${dateStart}&dateEnd=${dateEnd}`
);
const data = apiResponse.body;
// manually testing this value and then removing it to avoid flakiness
const { interval } = data;
assertCloseTo(interval, 22801, 100);
delete data.interval;
expectFixtureEql(data, 'ping_histogram');
});
it('will fetch histogram data for a given monitor id', async () => {
const dateStart = '2019-09-11T03:31:04.380Z';
const dateEnd = '2019-09-11T03:40:34.410Z';
const monitorId = '0002-up';
const apiResponse = await supertest.get(
`/api/uptime/ping/histogram?monitorId=${monitorId}&dateStart=${dateStart}&dateEnd=${dateEnd}`
);
const data = apiResponse.body;
const { interval } = data;
assertCloseTo(interval, 22801, 100);
delete data.interval;
expectFixtureEql(data, 'ping_histogram_by_id');
});
it('will fetch histogram data for a given filter', async () => {
const dateStart = '2019-09-11T03:31:04.380Z';
const dateEnd = '2019-09-11T03:40:34.410Z';
const filters =
'{"bool":{"must":[{"match":{"monitor.status":{"query":"up","operator":"and"}}}]}}';
const apiResponse = await supertest.get(
`/api/uptime/ping/histogram?dateStart=${dateStart}&dateEnd=${dateEnd}&filters=${filters}`
);
const data = apiResponse.body;
const { interval } = data;
assertCloseTo(interval, 22801, 100);
delete data.interval;
expectFixtureEql(data, 'ping_histogram_by_filter');
});
});
}