mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Dario Gieselaar <dario.gieselaar@elastic.co> Co-authored-by: Cauê Marcondes <55978943+cauemarcondes@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
656d844e80
commit
15bb592c02
25 changed files with 1456 additions and 184 deletions
|
@ -1,29 +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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { dateAsStringRt } from './index';
|
||||
import { isLeft, isRight } from 'fp-ts/lib/Either';
|
||||
|
||||
describe('dateAsStringRt', () => {
|
||||
it('validates whether a string is a valid date', () => {
|
||||
expect(isLeft(dateAsStringRt.decode(1566299881499))).toBe(true);
|
||||
|
||||
expect(isRight(dateAsStringRt.decode('2019-08-20T11:18:31.407Z'))).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the string it was given', () => {
|
||||
const either = dateAsStringRt.decode('2019-08-20T11:18:31.407Z');
|
||||
|
||||
if (isRight(either)) {
|
||||
expect(either.right).toBe('2019-08-20T11:18:31.407Z');
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
});
|
|
@ -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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isoToEpochRt } from './index';
|
||||
import { isRight } from 'fp-ts/lib/Either';
|
||||
|
||||
describe('isoToEpochRt', () => {
|
||||
it('validates whether its input is a valid ISO timestamp', () => {
|
||||
expect(isRight(isoToEpochRt.decode(1566299881499))).toBe(false);
|
||||
|
||||
expect(isRight(isoToEpochRt.decode('2019-08-20T11:18:31.407Z'))).toBe(true);
|
||||
});
|
||||
|
||||
it('decodes valid ISO timestamps to epoch time', () => {
|
||||
const iso = '2019-08-20T11:18:31.407Z';
|
||||
const result = isoToEpochRt.decode(iso);
|
||||
|
||||
if (isRight(result)) {
|
||||
expect(result.right).toBe(new Date(iso).getTime());
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
|
||||
it('encodes epoch time to ISO string', () => {
|
||||
expect(isoToEpochRt.encode(1566299911407)).toBe('2019-08-20T11:18:31.407Z');
|
||||
});
|
||||
});
|
|
@ -9,15 +9,20 @@ import * as t from 'io-ts';
|
|||
import { either } from 'fp-ts/lib/Either';
|
||||
|
||||
// Checks whether a string is a valid ISO timestamp,
|
||||
// but doesn't convert it into a Date object when decoding
|
||||
// and returns an epoch timestamp
|
||||
|
||||
export const dateAsStringRt = new t.Type<string, string, unknown>(
|
||||
'DateAsString',
|
||||
t.string.is,
|
||||
export const isoToEpochRt = new t.Type<number, string, unknown>(
|
||||
'isoToEpochRt',
|
||||
t.number.is,
|
||||
(input, context) =>
|
||||
either.chain(t.string.validate(input, context), (str) => {
|
||||
const date = new Date(str);
|
||||
return isNaN(date.getTime()) ? t.failure(input, context) : t.success(str);
|
||||
const epochDate = new Date(str).getTime();
|
||||
return isNaN(epochDate)
|
||||
? t.failure(input, context)
|
||||
: t.success(epochDate);
|
||||
}),
|
||||
t.identity
|
||||
(a) => {
|
||||
const d = new Date(a);
|
||||
return d.toISOString();
|
||||
}
|
||||
);
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
|
||||
export const toBooleanRt = new t.Type<boolean, unknown, unknown>(
|
||||
'ToBoolean',
|
||||
t.boolean.is,
|
||||
(input) => {
|
||||
let value: boolean;
|
||||
if (typeof input === 'string') {
|
||||
value = input === 'true';
|
||||
} else {
|
||||
value = !!input;
|
||||
}
|
||||
|
||||
return t.success(value);
|
||||
},
|
||||
t.identity
|
||||
);
|
|
@ -9,7 +9,7 @@ import * as t from 'io-ts';
|
|||
|
||||
export const toNumberRt = new t.Type<number, unknown, unknown>(
|
||||
'ToNumber',
|
||||
t.any.is,
|
||||
t.number.is,
|
||||
(input, context) => {
|
||||
const number = Number(input);
|
||||
return !isNaN(number) ? t.success(number) : t.failure(input, context);
|
||||
|
|
|
@ -20,10 +20,12 @@ import {
|
|||
MockApmPluginContextWrapper,
|
||||
} from '../../../context/apm_plugin/mock_apm_plugin_context';
|
||||
import { FETCH_STATUS } from '../../../hooks/use_fetcher';
|
||||
import { clearCache } from '../../../services/rest/callApi';
|
||||
import * as useDynamicIndexPatternHooks from '../../../hooks/use_dynamic_index_pattern';
|
||||
import { SessionStorageMock } from '../../../services/__mocks__/SessionStorageMock';
|
||||
import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider';
|
||||
import * as hook from './use_anomaly_detection_jobs_fetcher';
|
||||
import { TimeRangeComparisonType } from '../../shared/time_comparison/get_time_range_comparison';
|
||||
|
||||
const KibanaReactContext = createKibanaReactContext({
|
||||
usageCollection: { reportUiCounter: () => {} },
|
||||
|
@ -55,10 +57,10 @@ function wrapper({ children }: { children?: ReactNode }) {
|
|||
params={{
|
||||
rangeFrom: 'now-15m',
|
||||
rangeTo: 'now',
|
||||
start: 'mystart',
|
||||
end: 'myend',
|
||||
start: '2021-02-12T13:20:43.344Z',
|
||||
end: '2021-02-12T13:20:58.344Z',
|
||||
comparisonEnabled: true,
|
||||
comparisonType: 'yesterday',
|
||||
comparisonType: TimeRangeComparisonType.DayBefore,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -74,6 +76,7 @@ describe('ServiceInventory', () => {
|
|||
beforeEach(() => {
|
||||
// @ts-expect-error
|
||||
global.sessionStorage = new SessionStorageMock();
|
||||
clearCache();
|
||||
|
||||
jest.spyOn(hook, 'useAnomalyDetectionJobsFetcher').mockReturnValue({
|
||||
anomalyDetectionJobsStatus: FETCH_STATUS.SUCCESS,
|
||||
|
|
|
@ -15,6 +15,15 @@ import { useTheme } from '../../../hooks/use_theme';
|
|||
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
|
||||
import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context';
|
||||
import { TimeseriesChart } from '../../shared/charts/timeseries_chart';
|
||||
import {
|
||||
getTimeRangeComparison,
|
||||
getComparisonChartTheme,
|
||||
} from '../../shared/time_comparison/get_time_range_comparison';
|
||||
|
||||
const INITIAL_STATE = {
|
||||
currentPeriod: [],
|
||||
previousPeriod: [],
|
||||
};
|
||||
|
||||
export function ServiceOverviewThroughputChart({
|
||||
height,
|
||||
|
@ -25,9 +34,20 @@ export function ServiceOverviewThroughputChart({
|
|||
const { serviceName } = useParams<{ serviceName?: string }>();
|
||||
const { urlParams, uiFilters } = useUrlParams();
|
||||
const { transactionType } = useApmServiceContext();
|
||||
const { start, end } = urlParams;
|
||||
const { start, end, comparisonEnabled, comparisonType } = urlParams;
|
||||
const comparisonChartTheme = getComparisonChartTheme(theme);
|
||||
const {
|
||||
comparisonStart = undefined,
|
||||
comparisonEnd = undefined,
|
||||
} = comparisonType
|
||||
? getTimeRangeComparison({
|
||||
start,
|
||||
end,
|
||||
comparisonType,
|
||||
})
|
||||
: {};
|
||||
|
||||
const { data, status } = useFetcher(
|
||||
const { data = INITIAL_STATE, status } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (serviceName && transactionType && start && end) {
|
||||
return callApmApi({
|
||||
|
@ -41,12 +61,22 @@ export function ServiceOverviewThroughputChart({
|
|||
end,
|
||||
transactionType,
|
||||
uiFilters: JSON.stringify(uiFilters),
|
||||
comparisonStart,
|
||||
comparisonEnd,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
[serviceName, start, end, uiFilters, transactionType]
|
||||
[
|
||||
serviceName,
|
||||
start,
|
||||
end,
|
||||
uiFilters,
|
||||
transactionType,
|
||||
comparisonStart,
|
||||
comparisonEnd,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -63,9 +93,10 @@ export function ServiceOverviewThroughputChart({
|
|||
height={height}
|
||||
showAnnotations={false}
|
||||
fetchStatus={status}
|
||||
customTheme={comparisonChartTheme}
|
||||
timeseries={[
|
||||
{
|
||||
data: data?.throughput ?? [],
|
||||
data: data.currentPeriod,
|
||||
type: 'linemark',
|
||||
color: theme.eui.euiColorVis0,
|
||||
title: i18n.translate(
|
||||
|
@ -73,6 +104,21 @@ export function ServiceOverviewThroughputChart({
|
|||
{ defaultMessage: 'Throughput' }
|
||||
),
|
||||
},
|
||||
...(comparisonEnabled
|
||||
? [
|
||||
{
|
||||
data: data.previousPeriod,
|
||||
type: 'area',
|
||||
color: theme.eui.euiColorLightestShade,
|
||||
title: i18n.translate(
|
||||
'xpack.apm.serviceOverview.throughtputChart.previousPeriodLabel',
|
||||
{
|
||||
defaultMessage: 'Previous period',
|
||||
}
|
||||
),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
yLabelFormat={asTransactionRate}
|
||||
/>
|
||||
|
|
|
@ -131,7 +131,7 @@ describe('TransactionOverview', () => {
|
|||
});
|
||||
|
||||
expect(history.location.search).toEqual(
|
||||
'?transactionType=secondType&rangeFrom=now-15m&rangeTo=now&comparisonEnabled=true&comparisonType=yesterday'
|
||||
'?transactionType=secondType&rangeFrom=now-15m&rangeTo=now&comparisonEnabled=true&comparisonType=day'
|
||||
);
|
||||
expect(getByText(container, 'firstType')).toBeInTheDocument();
|
||||
expect(getByText(container, 'secondType')).toBeInTheDocument();
|
||||
|
@ -142,7 +142,7 @@ describe('TransactionOverview', () => {
|
|||
|
||||
expect(history.push).toHaveBeenCalled();
|
||||
expect(history.location.search).toEqual(
|
||||
'?transactionType=firstType&rangeFrom=now-15m&rangeTo=now&comparisonEnabled=true&comparisonType=yesterday'
|
||||
'?transactionType=firstType&rangeFrom=now-15m&rangeTo=now&comparisonEnabled=true&comparisonType=day'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -59,6 +59,7 @@ interface Props {
|
|||
anomalyTimeseries?: ReturnType<
|
||||
typeof getLatencyChartSelector
|
||||
>['anomalyTimeseries'];
|
||||
customTheme?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export function TimeseriesChart({
|
||||
|
@ -72,13 +73,14 @@ export function TimeseriesChart({
|
|||
showAnnotations = true,
|
||||
yDomain,
|
||||
anomalyTimeseries,
|
||||
customTheme = {},
|
||||
}: Props) {
|
||||
const history = useHistory();
|
||||
const { annotations } = useAnnotationsContext();
|
||||
const chartTheme = useChartTheme();
|
||||
const { setPointerEvent, chartRef } = useChartPointerEventContext();
|
||||
const { urlParams } = useUrlParams();
|
||||
const theme = useTheme();
|
||||
const chartTheme = useChartTheme();
|
||||
|
||||
const { start, end } = urlParams;
|
||||
|
||||
|
@ -103,6 +105,7 @@ export function TimeseriesChart({
|
|||
areaSeriesStyle: {
|
||||
line: { visible: false },
|
||||
},
|
||||
...customTheme,
|
||||
}}
|
||||
onPointerUpdate={setPointerEvent}
|
||||
externalPointerEvents={{
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import {
|
||||
getTimeRangeComparison,
|
||||
TimeRangeComparisonType,
|
||||
} from './get_time_range_comparison';
|
||||
|
||||
describe('getTimeRangeComparison', () => {
|
||||
describe('return empty object', () => {
|
||||
it('when start is not defined', () => {
|
||||
const end = '2021-01-28T15:00:00.000Z';
|
||||
const result = getTimeRangeComparison({
|
||||
start: undefined,
|
||||
end,
|
||||
comparisonType: TimeRangeComparisonType.DayBefore,
|
||||
});
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it('when end is not defined', () => {
|
||||
const start = '2021-01-28T14:45:00.000Z';
|
||||
const result = getTimeRangeComparison({
|
||||
start,
|
||||
end: undefined,
|
||||
comparisonType: TimeRangeComparisonType.DayBefore,
|
||||
});
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Time range is between 0 - 24 hours', () => {
|
||||
describe('when day before is selected', () => {
|
||||
it('returns the correct time range - 15 min', () => {
|
||||
const start = '2021-01-28T14:45:00.000Z';
|
||||
const end = '2021-01-28T15:00:00.000Z';
|
||||
const result = getTimeRangeComparison({
|
||||
comparisonType: TimeRangeComparisonType.DayBefore,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
expect(result.comparisonStart).toEqual('2021-01-27T14:45:00.000Z');
|
||||
expect(result.comparisonEnd).toEqual('2021-01-27T15:00:00.000Z');
|
||||
});
|
||||
});
|
||||
describe('when a week before is selected', () => {
|
||||
it('returns the correct time range - 15 min', () => {
|
||||
const start = '2021-01-28T14:45:00.000Z';
|
||||
const end = '2021-01-28T15:00:00.000Z';
|
||||
const result = getTimeRangeComparison({
|
||||
comparisonType: TimeRangeComparisonType.WeekBefore,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
expect(result.comparisonStart).toEqual('2021-01-21T14:45:00.000Z');
|
||||
expect(result.comparisonEnd).toEqual('2021-01-21T15:00:00.000Z');
|
||||
});
|
||||
});
|
||||
describe('when previous period is selected', () => {
|
||||
it('returns the correct time range - 15 min', () => {
|
||||
const start = '2021-02-09T14:40:01.087Z';
|
||||
const end = '2021-02-09T14:56:00.000Z';
|
||||
const result = getTimeRangeComparison({
|
||||
start,
|
||||
end,
|
||||
comparisonType: TimeRangeComparisonType.PeriodBefore,
|
||||
});
|
||||
expect(result).toEqual({
|
||||
comparisonStart: '2021-02-09T14:24:02.174Z',
|
||||
comparisonEnd: '2021-02-09T14:40:01.087Z',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Time range is between 24 hours - 1 week', () => {
|
||||
describe('when a week before is selected', () => {
|
||||
it('returns the correct time range - 2 days', () => {
|
||||
const start = '2021-01-26T15:00:00.000Z';
|
||||
const end = '2021-01-28T15:00:00.000Z';
|
||||
const result = getTimeRangeComparison({
|
||||
comparisonType: TimeRangeComparisonType.WeekBefore,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
expect(result.comparisonStart).toEqual('2021-01-19T15:00:00.000Z');
|
||||
expect(result.comparisonEnd).toEqual('2021-01-21T15:00:00.000Z');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Time range is greater than 7 days', () => {
|
||||
it('uses the date difference to calculate the time range - 8 days', () => {
|
||||
const start = '2021-01-10T15:00:00.000Z';
|
||||
const end = '2021-01-18T15:00:00.000Z';
|
||||
const result = getTimeRangeComparison({
|
||||
comparisonType: TimeRangeComparisonType.PeriodBefore,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
expect(result.comparisonStart).toEqual('2021-01-02T15:00:00.000Z');
|
||||
expect(result.comparisonEnd).toEqual('2021-01-10T15:00:00.000Z');
|
||||
});
|
||||
|
||||
it('uses the date difference to calculate the time range - 30 days', () => {
|
||||
const start = '2021-01-01T15:00:00.000Z';
|
||||
const end = '2021-01-31T15:00:00.000Z';
|
||||
const result = getTimeRangeComparison({
|
||||
comparisonType: TimeRangeComparisonType.PeriodBefore,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
expect(result.comparisonStart).toEqual('2020-12-02T15:00:00.000Z');
|
||||
expect(result.comparisonEnd).toEqual('2021-01-01T15:00:00.000Z');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import { EuiTheme } from 'src/plugins/kibana_react/common';
|
||||
import { getDateDifference } from '../../../../common/utils/formatters';
|
||||
|
||||
export enum TimeRangeComparisonType {
|
||||
WeekBefore = 'week',
|
||||
DayBefore = 'day',
|
||||
PeriodBefore = 'period',
|
||||
}
|
||||
|
||||
export function getComparisonChartTheme(theme: EuiTheme) {
|
||||
return {
|
||||
areaSeriesStyle: {
|
||||
area: {
|
||||
fill: theme.eui.euiColorLightestShade,
|
||||
visible: true,
|
||||
opacity: 1,
|
||||
},
|
||||
line: {
|
||||
stroke: theme.eui.euiColorMediumShade,
|
||||
strokeWidth: 1,
|
||||
visible: true,
|
||||
},
|
||||
point: {
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const oneDayInMilliseconds = moment.duration(1, 'day').asMilliseconds();
|
||||
const oneWeekInMilliseconds = moment.duration(1, 'week').asMilliseconds();
|
||||
|
||||
export function getTimeRangeComparison({
|
||||
comparisonType,
|
||||
start,
|
||||
end,
|
||||
}: {
|
||||
comparisonType: TimeRangeComparisonType;
|
||||
start?: string;
|
||||
end?: string;
|
||||
}) {
|
||||
if (!start || !end) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const startMoment = moment(start);
|
||||
const endMoment = moment(end);
|
||||
|
||||
const startEpoch = startMoment.valueOf();
|
||||
const endEpoch = endMoment.valueOf();
|
||||
|
||||
let diff: number;
|
||||
|
||||
switch (comparisonType) {
|
||||
case TimeRangeComparisonType.DayBefore:
|
||||
diff = oneDayInMilliseconds;
|
||||
break;
|
||||
|
||||
case TimeRangeComparisonType.WeekBefore:
|
||||
diff = oneWeekInMilliseconds;
|
||||
break;
|
||||
|
||||
case TimeRangeComparisonType.PeriodBefore:
|
||||
diff = getDateDifference({
|
||||
start: startMoment,
|
||||
end: endMoment,
|
||||
unitOfTime: 'milliseconds',
|
||||
precise: true,
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error('Unknown comparisonType');
|
||||
}
|
||||
|
||||
return {
|
||||
comparisonStart: new Date(startEpoch - diff).toISOString(),
|
||||
comparisonEnd: new Date(endEpoch - diff).toISOString(),
|
||||
};
|
||||
}
|
|
@ -18,6 +18,7 @@ import {
|
|||
import { TimeComparison } from './';
|
||||
import * as urlHelpers from '../../shared/Links/url_helpers';
|
||||
import moment from 'moment';
|
||||
import { TimeRangeComparisonType } from './get_time_range_comparison';
|
||||
|
||||
function getWrapper(params?: IUrlParams) {
|
||||
return ({ children }: { children?: ReactNode }) => {
|
||||
|
@ -53,22 +54,22 @@ describe('TimeComparison', () => {
|
|||
expect(spy).toHaveBeenCalledWith(expect.anything(), {
|
||||
query: {
|
||||
comparisonEnabled: 'true',
|
||||
comparisonType: 'yesterday',
|
||||
comparisonType: TimeRangeComparisonType.DayBefore,
|
||||
},
|
||||
});
|
||||
});
|
||||
it('selects yesterday and enables comparison', () => {
|
||||
it('selects day before and enables comparison', () => {
|
||||
const Wrapper = getWrapper({
|
||||
start: '2021-01-28T14:45:00.000Z',
|
||||
end: '2021-01-28T15:00:00.000Z',
|
||||
comparisonEnabled: true,
|
||||
comparisonType: 'yesterday',
|
||||
comparisonType: TimeRangeComparisonType.DayBefore,
|
||||
rangeTo: 'now',
|
||||
});
|
||||
const component = render(<TimeComparison />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
expectTextsInDocument(component, ['Yesterday', 'A week ago']);
|
||||
expectTextsInDocument(component, ['Day before', 'Week before']);
|
||||
expect(
|
||||
(component.getByTestId('comparisonSelect') as HTMLSelectElement)
|
||||
.selectedIndex
|
||||
|
@ -80,13 +81,13 @@ describe('TimeComparison', () => {
|
|||
start: '2021-01-28T10:00:00.000Z',
|
||||
end: '2021-01-29T10:00:00.000Z',
|
||||
comparisonEnabled: true,
|
||||
comparisonType: 'yesterday',
|
||||
comparisonType: TimeRangeComparisonType.DayBefore,
|
||||
rangeTo: 'now',
|
||||
});
|
||||
const component = render(<TimeComparison />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
expectTextsInDocument(component, ['Yesterday', 'A week ago']);
|
||||
expectTextsInDocument(component, ['Day before', 'Week before']);
|
||||
expect(
|
||||
(component.getByTestId('comparisonSelect') as HTMLSelectElement)
|
||||
.selectedIndex
|
||||
|
@ -98,13 +99,13 @@ describe('TimeComparison', () => {
|
|||
start: '2021-01-28T10:00:00.000Z',
|
||||
end: '2021-01-29T10:00:00.000Z',
|
||||
comparisonEnabled: true,
|
||||
comparisonType: 'previousPeriod',
|
||||
comparisonType: TimeRangeComparisonType.PeriodBefore,
|
||||
rangeTo: 'now-15m',
|
||||
});
|
||||
const component = render(<TimeComparison />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
expectTextsInDocument(component, ['28/01 11:00 - 29/01 11:00']);
|
||||
expectTextsInDocument(component, ['27/01 11:00 - 28/01 11:00']);
|
||||
expect(
|
||||
(component.getByTestId('comparisonSelect') as HTMLSelectElement)
|
||||
.selectedIndex
|
||||
|
@ -118,14 +119,14 @@ describe('TimeComparison', () => {
|
|||
start: '2021-01-28T10:00:00.000Z',
|
||||
end: '2021-01-29T11:00:00.000Z',
|
||||
comparisonEnabled: true,
|
||||
comparisonType: 'week',
|
||||
comparisonType: TimeRangeComparisonType.WeekBefore,
|
||||
rangeTo: 'now',
|
||||
});
|
||||
const component = render(<TimeComparison />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
expectTextsNotInDocument(component, ['Yesterday']);
|
||||
expectTextsInDocument(component, ['A week ago']);
|
||||
expectTextsNotInDocument(component, ['Day before']);
|
||||
expectTextsInDocument(component, ['Week before']);
|
||||
});
|
||||
it('sets default values', () => {
|
||||
const Wrapper = getWrapper({
|
||||
|
@ -139,7 +140,7 @@ describe('TimeComparison', () => {
|
|||
expect(spy).toHaveBeenCalledWith(expect.anything(), {
|
||||
query: {
|
||||
comparisonEnabled: 'true',
|
||||
comparisonType: 'week',
|
||||
comparisonType: TimeRangeComparisonType.WeekBefore,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@ -148,14 +149,14 @@ describe('TimeComparison', () => {
|
|||
start: '2021-01-26T15:00:00.000Z',
|
||||
end: '2021-01-28T15:00:00.000Z',
|
||||
comparisonEnabled: true,
|
||||
comparisonType: 'week',
|
||||
comparisonType: TimeRangeComparisonType.WeekBefore,
|
||||
rangeTo: 'now',
|
||||
});
|
||||
const component = render(<TimeComparison />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
expectTextsNotInDocument(component, ['Yesterday']);
|
||||
expectTextsInDocument(component, ['A week ago']);
|
||||
expectTextsNotInDocument(component, ['Day before']);
|
||||
expectTextsInDocument(component, ['Week before']);
|
||||
expect(
|
||||
(component.getByTestId('comparisonSelect') as HTMLSelectElement)
|
||||
.selectedIndex
|
||||
|
@ -167,13 +168,13 @@ describe('TimeComparison', () => {
|
|||
start: '2021-01-26T15:00:00.000Z',
|
||||
end: '2021-01-28T15:00:00.000Z',
|
||||
comparisonEnabled: true,
|
||||
comparisonType: 'previousPeriod',
|
||||
comparisonType: TimeRangeComparisonType.PeriodBefore,
|
||||
rangeTo: '2021-01-28T15:00:00.000Z',
|
||||
});
|
||||
const component = render(<TimeComparison />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
expectTextsInDocument(component, ['26/01 16:00 - 28/01 16:00']);
|
||||
expectTextsInDocument(component, ['24/01 16:00 - 26/01 16:00']);
|
||||
expect(
|
||||
(component.getByTestId('comparisonSelect') as HTMLSelectElement)
|
||||
.selectedIndex
|
||||
|
@ -187,14 +188,14 @@ describe('TimeComparison', () => {
|
|||
start: '2021-01-20T15:00:00.000Z',
|
||||
end: '2021-01-28T15:00:00.000Z',
|
||||
comparisonEnabled: true,
|
||||
comparisonType: 'previousPeriod',
|
||||
comparisonType: TimeRangeComparisonType.PeriodBefore,
|
||||
rangeTo: 'now',
|
||||
});
|
||||
const component = render(<TimeComparison />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
expectTextsInDocument(component, ['20/01 16:00 - 28/01 16:00']);
|
||||
expectTextsInDocument(component, ['12/01 16:00 - 20/01 16:00']);
|
||||
expect(
|
||||
(component.getByTestId('comparisonSelect') as HTMLSelectElement)
|
||||
.selectedIndex
|
||||
|
@ -206,14 +207,14 @@ describe('TimeComparison', () => {
|
|||
start: '2020-12-20T15:00:00.000Z',
|
||||
end: '2021-01-28T15:00:00.000Z',
|
||||
comparisonEnabled: true,
|
||||
comparisonType: 'previousPeriod',
|
||||
comparisonType: TimeRangeComparisonType.PeriodBefore,
|
||||
rangeTo: 'now',
|
||||
});
|
||||
const component = render(<TimeComparison />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
expectTextsInDocument(component, ['20/12/20 16:00 - 28/01/21 16:00']);
|
||||
expectTextsInDocument(component, ['11/11/20 16:00 - 20/12/20 16:00']);
|
||||
expect(
|
||||
(component.getByTestId('comparisonSelect') as HTMLSelectElement)
|
||||
.selectedIndex
|
||||
|
|
|
@ -16,6 +16,10 @@ import { useUrlParams } from '../../../context/url_params_context/use_url_params
|
|||
import { px, unit } from '../../../style/variables';
|
||||
import * as urlHelpers from '../../shared/Links/url_helpers';
|
||||
import { useBreakPoints } from '../../../hooks/use_break_points';
|
||||
import {
|
||||
getTimeRangeComparison,
|
||||
TimeRangeComparisonType,
|
||||
} from './get_time_range_comparison';
|
||||
|
||||
const PrependContainer = euiStyled.div`
|
||||
display: flex;
|
||||
|
@ -25,15 +29,32 @@ const PrependContainer = euiStyled.div`
|
|||
padding: 0 ${px(unit)};
|
||||
`;
|
||||
|
||||
function formatPreviousPeriodDates({
|
||||
momentStart,
|
||||
momentEnd,
|
||||
function getDateFormat({
|
||||
previousPeriodStart,
|
||||
currentPeriodEnd,
|
||||
}: {
|
||||
momentStart: moment.Moment;
|
||||
momentEnd: moment.Moment;
|
||||
previousPeriodStart?: string;
|
||||
currentPeriodEnd?: string;
|
||||
}) {
|
||||
const isDifferentYears = momentStart.get('year') !== momentEnd.get('year');
|
||||
const dateFormat = isDifferentYears ? 'DD/MM/YY HH:mm' : 'DD/MM HH:mm';
|
||||
const momentPreviousPeriodStart = moment(previousPeriodStart);
|
||||
const momentCurrentPeriodEnd = moment(currentPeriodEnd);
|
||||
const isDifferentYears =
|
||||
momentPreviousPeriodStart.get('year') !==
|
||||
momentCurrentPeriodEnd.get('year');
|
||||
return isDifferentYears ? 'DD/MM/YY HH:mm' : 'DD/MM HH:mm';
|
||||
}
|
||||
|
||||
function formatDate({
|
||||
dateFormat,
|
||||
previousPeriodStart,
|
||||
previousPeriodEnd,
|
||||
}: {
|
||||
dateFormat: string;
|
||||
previousPeriodStart?: string;
|
||||
previousPeriodEnd?: string;
|
||||
}) {
|
||||
const momentStart = moment(previousPeriodStart);
|
||||
const momentEnd = moment(previousPeriodEnd);
|
||||
return `${momentStart.format(dateFormat)} - ${momentEnd.format(dateFormat)}`;
|
||||
}
|
||||
|
||||
|
@ -49,17 +70,17 @@ function getSelectOptions({
|
|||
const momentStart = moment(start);
|
||||
const momentEnd = moment(end);
|
||||
|
||||
const yesterdayOption = {
|
||||
value: 'yesterday',
|
||||
text: i18n.translate('xpack.apm.timeComparison.select.yesterday', {
|
||||
defaultMessage: 'Yesterday',
|
||||
const dayBeforeOption = {
|
||||
value: TimeRangeComparisonType.DayBefore,
|
||||
text: i18n.translate('xpack.apm.timeComparison.select.dayBefore', {
|
||||
defaultMessage: 'Day before',
|
||||
}),
|
||||
};
|
||||
|
||||
const aWeekAgoOption = {
|
||||
value: 'week',
|
||||
text: i18n.translate('xpack.apm.timeComparison.select.weekAgo', {
|
||||
defaultMessage: 'A week ago',
|
||||
const weekBeforeOption = {
|
||||
value: TimeRangeComparisonType.WeekBefore,
|
||||
text: i18n.translate('xpack.apm.timeComparison.select.weekBefore', {
|
||||
defaultMessage: 'Week before',
|
||||
}),
|
||||
};
|
||||
|
||||
|
@ -69,23 +90,39 @@ function getSelectOptions({
|
|||
unitOfTime: 'days',
|
||||
precise: true,
|
||||
});
|
||||
|
||||
const isRangeToNow = rangeTo === 'now';
|
||||
|
||||
if (isRangeToNow) {
|
||||
// Less than or equals to one day
|
||||
if (dateDiff <= 1) {
|
||||
return [yesterdayOption, aWeekAgoOption];
|
||||
return [dayBeforeOption, weekBeforeOption];
|
||||
}
|
||||
|
||||
// Less than or equals to one week
|
||||
if (dateDiff <= 7) {
|
||||
return [aWeekAgoOption];
|
||||
return [weekBeforeOption];
|
||||
}
|
||||
}
|
||||
|
||||
const { comparisonStart, comparisonEnd } = getTimeRangeComparison({
|
||||
comparisonType: TimeRangeComparisonType.PeriodBefore,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
|
||||
const dateFormat = getDateFormat({
|
||||
previousPeriodStart: comparisonStart,
|
||||
currentPeriodEnd: end,
|
||||
});
|
||||
|
||||
const prevPeriodOption = {
|
||||
value: 'previousPeriod',
|
||||
text: formatPreviousPeriodDates({ momentStart, momentEnd }),
|
||||
value: TimeRangeComparisonType.PeriodBefore,
|
||||
text: formatDate({
|
||||
dateFormat,
|
||||
previousPeriodStart: comparisonStart,
|
||||
previousPeriodEnd: comparisonEnd,
|
||||
}),
|
||||
};
|
||||
|
||||
// above one week or when rangeTo is not "now"
|
||||
|
|
|
@ -11,6 +11,7 @@ import { pickKeys } from '../../../common/utils/pick_keys';
|
|||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { localUIFilterNames } from '../../../server/lib/ui_filters/local_ui_filters/config';
|
||||
import { toQuery } from '../../components/shared/Links/url_helpers';
|
||||
import { TimeRangeComparisonType } from '../../components/shared/time_comparison/get_time_range_comparison';
|
||||
import {
|
||||
getDateRange,
|
||||
removeUndefinedProps,
|
||||
|
@ -84,8 +85,7 @@ export function resolveUrlParams(location: Location, state: TimeUrlParams) {
|
|||
comparisonEnabled: comparisonEnabled
|
||||
? toBoolean(comparisonEnabled)
|
||||
: undefined,
|
||||
comparisonType,
|
||||
|
||||
comparisonType: comparisonType as TimeRangeComparisonType | undefined,
|
||||
// ui filters
|
||||
environment,
|
||||
...localUIFilters,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { LatencyAggregationType } from '../../../common/latency_aggregation_types';
|
||||
import { LocalUIFilterName } from '../../../common/ui_filter';
|
||||
import { TimeRangeComparisonType } from '../../components/shared/time_comparison/get_time_range_comparison';
|
||||
|
||||
export type IUrlParams = {
|
||||
detailTab?: string;
|
||||
|
@ -32,5 +33,5 @@ export type IUrlParams = {
|
|||
percentile?: number;
|
||||
latencyAggregationType?: LatencyAggregationType;
|
||||
comparisonEnabled?: boolean;
|
||||
comparisonType?: string;
|
||||
comparisonType?: TimeRangeComparisonType;
|
||||
} & Partial<Record<LocalUIFilterName, string>>;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { Logger } from 'kibana/server';
|
||||
import moment from 'moment';
|
||||
import { isActivePlatinumLicense } from '../../../common/license_check';
|
||||
import { APMConfig } from '../..';
|
||||
import { KibanaRequest } from '../../../../../../src/core/server';
|
||||
|
@ -54,19 +53,19 @@ interface SetupRequestParams {
|
|||
/**
|
||||
* Timestamp in ms since epoch
|
||||
*/
|
||||
start?: string;
|
||||
start?: number;
|
||||
|
||||
/**
|
||||
* Timestamp in ms since epoch
|
||||
*/
|
||||
end?: string;
|
||||
end?: number;
|
||||
uiFilters?: string;
|
||||
};
|
||||
}
|
||||
|
||||
type InferSetup<TParams extends SetupRequestParams> = Setup &
|
||||
(TParams extends { query: { start: string } } ? { start: number } : {}) &
|
||||
(TParams extends { query: { end: string } } ? { end: number } : {});
|
||||
(TParams extends { query: { start: number } } ? { start: number } : {}) &
|
||||
(TParams extends { query: { end: number } } ? { end: number } : {});
|
||||
|
||||
export async function setupRequest<TParams extends SetupRequestParams>(
|
||||
context: APMRequestHandlerContext<TParams>,
|
||||
|
@ -115,8 +114,8 @@ export async function setupRequest<TParams extends SetupRequestParams>(
|
|||
};
|
||||
|
||||
return {
|
||||
...('start' in query ? { start: moment.utc(query.start).valueOf() } : {}),
|
||||
...('end' in query ? { end: moment.utc(query.end).valueOf() } : {}),
|
||||
...('start' in query ? { start: query.start } : {}),
|
||||
...('end' in query ? { end: query.end } : {}),
|
||||
...coreSetupRequest,
|
||||
} as InferSetup<TParams>;
|
||||
});
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { ESFilter } from '../../../../../typings/elasticsearch';
|
||||
import { PromiseReturnType } from '../../../../observability/typings/common';
|
||||
import {
|
||||
SERVICE_NAME,
|
||||
TRANSACTION_TYPE,
|
||||
|
@ -17,38 +16,27 @@ import {
|
|||
getProcessorEventForAggregatedTransactions,
|
||||
} from '../helpers/aggregated_transactions';
|
||||
import { getBucketSize } from '../helpers/get_bucket_size';
|
||||
import { calculateThroughput } from '../helpers/calculate_throughput';
|
||||
import { Setup, SetupTimeRange } from '../helpers/setup_request';
|
||||
import { Setup } from '../helpers/setup_request';
|
||||
import { withApmSpan } from '../../utils/with_apm_span';
|
||||
|
||||
interface Options {
|
||||
searchAggregatedTransactions: boolean;
|
||||
serviceName: string;
|
||||
setup: Setup & SetupTimeRange;
|
||||
setup: Setup;
|
||||
transactionType: string;
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
type ESResponse = PromiseReturnType<typeof fetcher>;
|
||||
|
||||
function transform(options: Options, response: ESResponse) {
|
||||
if (response.hits.total.value === 0) {
|
||||
return [];
|
||||
}
|
||||
const { start, end } = options.setup;
|
||||
const buckets = response.aggregations?.throughput.buckets ?? [];
|
||||
return buckets.map(({ key: x, doc_count: value }) => ({
|
||||
x,
|
||||
y: calculateThroughput({ start, end, value }),
|
||||
}));
|
||||
}
|
||||
|
||||
async function fetcher({
|
||||
function fetcher({
|
||||
searchAggregatedTransactions,
|
||||
serviceName,
|
||||
setup,
|
||||
transactionType,
|
||||
start,
|
||||
end,
|
||||
}: Options) {
|
||||
const { start, end, apmEventClient } = setup;
|
||||
const { apmEventClient } = setup;
|
||||
const { intervalString } = getBucketSize({ start, end });
|
||||
const filter: ESFilter[] = [
|
||||
{ term: { [SERVICE_NAME]: serviceName } },
|
||||
|
@ -72,13 +60,20 @@ async function fetcher({
|
|||
size: 0,
|
||||
query: { bool: { filter } },
|
||||
aggs: {
|
||||
throughput: {
|
||||
timeseries: {
|
||||
date_histogram: {
|
||||
field: '@timestamp',
|
||||
fixed_interval: intervalString,
|
||||
min_doc_count: 0,
|
||||
extended_bounds: { min: start, max: end },
|
||||
},
|
||||
aggs: {
|
||||
throughput: {
|
||||
rate: {
|
||||
unit: 'minute' as const,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -89,8 +84,15 @@ async function fetcher({
|
|||
|
||||
export function getThroughput(options: Options) {
|
||||
return withApmSpan('get_throughput_for_service', async () => {
|
||||
return {
|
||||
throughput: transform(options, await fetcher(options)),
|
||||
};
|
||||
const response = await fetcher(options);
|
||||
|
||||
return (
|
||||
response.aggregations?.timeseries.buckets.map((bucket) => {
|
||||
return {
|
||||
x: bucket.key,
|
||||
y: bucket.throughput.value,
|
||||
};
|
||||
}) ?? []
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,11 +6,16 @@
|
|||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import { dateAsStringRt } from '../../common/runtime_types/date_as_string_rt';
|
||||
import { isoToEpochRt } from '../../common/runtime_types/iso_to_epoch_rt';
|
||||
|
||||
export const rangeRt = t.type({
|
||||
start: dateAsStringRt,
|
||||
end: dateAsStringRt,
|
||||
start: isoToEpochRt,
|
||||
end: isoToEpochRt,
|
||||
});
|
||||
|
||||
export const comparisonRangeRt = t.partial({
|
||||
comparisonStart: isoToEpochRt,
|
||||
comparisonEnd: isoToEpochRt,
|
||||
});
|
||||
|
||||
export const uiFiltersRt = t.type({ uiFilters: t.string });
|
||||
|
|
|
@ -5,26 +5,27 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import * as t from 'io-ts';
|
||||
import Boom from '@hapi/boom';
|
||||
import * as t from 'io-ts';
|
||||
import { uniq } from 'lodash';
|
||||
import { setupRequest } from '../lib/helpers/setup_request';
|
||||
import { getServiceAgentName } from '../lib/services/get_service_agent_name';
|
||||
import { getServices } from '../lib/services/get_services';
|
||||
import { getServiceTransactionTypes } from '../lib/services/get_service_transaction_types';
|
||||
import { getServiceNodeMetadata } from '../lib/services/get_service_node_metadata';
|
||||
import { createRoute } from './create_route';
|
||||
import { uiFiltersRt, rangeRt } from './default_api_types';
|
||||
import { getServiceAnnotations } from '../lib/services/annotations';
|
||||
import { dateAsStringRt } from '../../common/runtime_types/date_as_string_rt';
|
||||
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
|
||||
import { getServiceErrorGroups } from '../lib/services/get_service_error_groups';
|
||||
import { getServiceDependencies } from '../lib/services/get_service_dependencies';
|
||||
import { isoToEpochRt } from '../../common/runtime_types/iso_to_epoch_rt';
|
||||
import { toNumberRt } from '../../common/runtime_types/to_number_rt';
|
||||
import { getThroughput } from '../lib/services/get_throughput';
|
||||
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
|
||||
import { setupRequest } from '../lib/helpers/setup_request';
|
||||
import { getServiceAnnotations } from '../lib/services/annotations';
|
||||
import { getServices } from '../lib/services/get_services';
|
||||
import { getServiceAgentName } from '../lib/services/get_service_agent_name';
|
||||
import { getServiceDependencies } from '../lib/services/get_service_dependencies';
|
||||
import { getServiceErrorGroups } from '../lib/services/get_service_error_groups';
|
||||
import { getServiceInstances } from '../lib/services/get_service_instances';
|
||||
import { getServiceMetadataDetails } from '../lib/services/get_service_metadata_details';
|
||||
import { getServiceMetadataIcons } from '../lib/services/get_service_metadata_icons';
|
||||
import { getServiceNodeMetadata } from '../lib/services/get_service_node_metadata';
|
||||
import { getServiceTransactionTypes } from '../lib/services/get_service_transaction_types';
|
||||
import { getThroughput } from '../lib/services/get_throughput';
|
||||
import { offsetPreviousPeriodCoordinates } from '../utils/offset_previous_period_coordinate';
|
||||
import { createRoute } from './create_route';
|
||||
import { comparisonRangeRt, rangeRt, uiFiltersRt } from './default_api_types';
|
||||
import { withApmSpan } from '../utils/with_apm_span';
|
||||
|
||||
export const servicesRoute = createRoute({
|
||||
|
@ -216,7 +217,7 @@ export const serviceAnnotationsCreateRoute = createRoute({
|
|||
}),
|
||||
body: t.intersection([
|
||||
t.type({
|
||||
'@timestamp': dateAsStringRt,
|
||||
'@timestamp': isoToEpochRt,
|
||||
service: t.intersection([
|
||||
t.type({
|
||||
version: t.string,
|
||||
|
@ -251,6 +252,7 @@ export const serviceAnnotationsCreateRoute = createRoute({
|
|||
annotationsClient.create({
|
||||
message: body.service.version,
|
||||
...body,
|
||||
'@timestamp': new Date(body['@timestamp']).toISOString(),
|
||||
annotation: {
|
||||
type: 'deployment',
|
||||
},
|
||||
|
@ -325,23 +327,56 @@ export const serviceThroughputRoute = createRoute({
|
|||
t.type({ transactionType: t.string }),
|
||||
uiFiltersRt,
|
||||
rangeRt,
|
||||
comparisonRangeRt,
|
||||
]),
|
||||
}),
|
||||
options: { tags: ['access:apm'] },
|
||||
handler: async ({ context, request }) => {
|
||||
const setup = await setupRequest(context, request);
|
||||
const { serviceName } = context.params.path;
|
||||
const { transactionType } = context.params.query;
|
||||
const {
|
||||
transactionType,
|
||||
comparisonStart,
|
||||
comparisonEnd,
|
||||
} = context.params.query;
|
||||
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
|
||||
setup
|
||||
);
|
||||
|
||||
return getThroughput({
|
||||
const { start, end } = setup;
|
||||
|
||||
const commonProps = {
|
||||
searchAggregatedTransactions,
|
||||
serviceName,
|
||||
setup,
|
||||
transactionType,
|
||||
});
|
||||
};
|
||||
|
||||
const [currentPeriod, previousPeriod] = await Promise.all([
|
||||
getThroughput({
|
||||
...commonProps,
|
||||
start,
|
||||
end,
|
||||
}),
|
||||
comparisonStart && comparisonEnd
|
||||
? getThroughput({
|
||||
...commonProps,
|
||||
start: comparisonStart,
|
||||
end: comparisonEnd,
|
||||
}).then((coordinates) =>
|
||||
offsetPreviousPeriodCoordinates({
|
||||
currentPeriodStart: start,
|
||||
previousPeriodStart: comparisonStart,
|
||||
previousPeriodTimeseries: coordinates,
|
||||
})
|
||||
)
|
||||
: [],
|
||||
]);
|
||||
|
||||
return {
|
||||
currentPeriod,
|
||||
previousPeriod,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import * as t from 'io-ts';
|
||||
import Boom from '@hapi/boom';
|
||||
import { toBooleanRt } from '../../../common/runtime_types/to_boolean_rt';
|
||||
import { setupRequest } from '../../lib/helpers/setup_request';
|
||||
import { getServiceNames } from '../../lib/settings/agent_configuration/get_service_names';
|
||||
import { createOrUpdateConfiguration } from '../../lib/settings/agent_configuration/create_or_update_configuration';
|
||||
|
@ -22,7 +23,6 @@ import {
|
|||
serviceRt,
|
||||
agentConfigurationIntakeRt,
|
||||
} from '../../../common/agent_configuration/runtime_types/agent_configuration_intake_rt';
|
||||
import { jsonRt } from '../../../common/runtime_types/json_rt';
|
||||
import { getSearchAggregatedTransactions } from '../../lib/helpers/aggregated_transactions';
|
||||
|
||||
// get list of configurations
|
||||
|
@ -103,7 +103,7 @@ export const createOrUpdateAgentConfigurationRoute = createRoute({
|
|||
tags: ['access:apm', 'access:apm_write'],
|
||||
},
|
||||
params: t.intersection([
|
||||
t.partial({ query: t.partial({ overwrite: jsonRt.pipe(t.boolean) }) }),
|
||||
t.partial({ query: t.partial({ overwrite: toBooleanRt }) }),
|
||||
t.type({ body: agentConfigurationIntakeRt }),
|
||||
]),
|
||||
handler: async ({ context, request }) => {
|
||||
|
|
|
@ -143,7 +143,7 @@ export type Client<
|
|||
forceCache?: boolean;
|
||||
endpoint: TEndpoint;
|
||||
} & (TRouteState[TEndpoint] extends { params: t.Any }
|
||||
? MaybeOptional<{ params: t.TypeOf<TRouteState[TEndpoint]['params']> }>
|
||||
? MaybeOptional<{ params: t.OutputOf<TRouteState[TEndpoint]['params']> }>
|
||||
: {}) &
|
||||
(TOptions extends { abortable: true } ? { signal: AbortSignal | null } : {})
|
||||
) => Promise<
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { Coordinate } from '../../typings/timeseries';
|
||||
import { offsetPreviousPeriodCoordinates } from './offset_previous_period_coordinate';
|
||||
|
||||
const previousPeriodStart = new Date('2021-01-27T14:45:00.000Z').valueOf();
|
||||
const currentPeriodStart = new Date('2021-01-28T14:45:00.000Z').valueOf();
|
||||
|
||||
describe('mergePeriodsTimeseries', () => {
|
||||
describe('returns empty array', () => {
|
||||
it('when previous timeseries is not defined', () => {
|
||||
expect(
|
||||
offsetPreviousPeriodCoordinates({
|
||||
currentPeriodStart,
|
||||
previousPeriodStart,
|
||||
previousPeriodTimeseries: undefined,
|
||||
})
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it('when previous timeseries is empty', () => {
|
||||
expect(
|
||||
offsetPreviousPeriodCoordinates({
|
||||
currentPeriodStart,
|
||||
previousPeriodStart,
|
||||
previousPeriodTimeseries: [],
|
||||
})
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
it('offsets previous period timeseries', () => {
|
||||
const previousPeriodTimeseries: Coordinate[] = [
|
||||
{ x: new Date('2021-01-27T14:45:00.000Z').valueOf(), y: 1 },
|
||||
{ x: new Date('2021-01-27T15:00:00.000Z').valueOf(), y: 2 },
|
||||
{ x: new Date('2021-01-27T15:15:00.000Z').valueOf(), y: 2 },
|
||||
{ x: new Date('2021-01-27T15:30:00.000Z').valueOf(), y: 3 },
|
||||
];
|
||||
|
||||
expect(
|
||||
offsetPreviousPeriodCoordinates({
|
||||
currentPeriodStart,
|
||||
previousPeriodStart,
|
||||
previousPeriodTimeseries,
|
||||
})
|
||||
).toEqual([
|
||||
{ x: new Date('2021-01-28T14:45:00.000Z').valueOf(), y: 1 },
|
||||
{ x: new Date('2021-01-28T15:00:00.000Z').valueOf(), y: 2 },
|
||||
{ x: new Date('2021-01-28T15:15:00.000Z').valueOf(), y: 2 },
|
||||
{ x: new Date('2021-01-28T15:30:00.000Z').valueOf(), y: 3 },
|
||||
]);
|
||||
});
|
||||
});
|
|
@ -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
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
import { Coordinate } from '../../typings/timeseries';
|
||||
|
||||
export function offsetPreviousPeriodCoordinates({
|
||||
currentPeriodStart,
|
||||
previousPeriodStart,
|
||||
previousPeriodTimeseries,
|
||||
}: {
|
||||
currentPeriodStart: number;
|
||||
previousPeriodStart: number;
|
||||
previousPeriodTimeseries?: Coordinate[];
|
||||
}) {
|
||||
if (!previousPeriodTimeseries) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const dateOffset = moment(currentPeriodStart).diff(
|
||||
moment(previousPeriodStart)
|
||||
);
|
||||
|
||||
return previousPeriodTimeseries.map(({ x, y }) => {
|
||||
const offsetX = moment(x).add(dateOffset).valueOf();
|
||||
return {
|
||||
x: offsetX,
|
||||
y,
|
||||
};
|
||||
});
|
||||
}
|
|
@ -8,7 +8,7 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607435880000,
|
||||
"y": 0.133333333333333,
|
||||
"y": 8,
|
||||
},
|
||||
Object {
|
||||
"x": 1607435910000,
|
||||
|
@ -16,7 +16,7 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607435940000,
|
||||
"y": 0.0666666666666667,
|
||||
"y": 4,
|
||||
},
|
||||
Object {
|
||||
"x": 1607435970000,
|
||||
|
@ -24,11 +24,11 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607436000000,
|
||||
"y": 0.1,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436030000,
|
||||
"y": 0.0333333333333333,
|
||||
"y": 2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436060000,
|
||||
|
@ -40,7 +40,7 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607436120000,
|
||||
"y": 0.133333333333333,
|
||||
"y": 8,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436150000,
|
||||
|
@ -56,7 +56,7 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607436240000,
|
||||
"y": 0.2,
|
||||
"y": 12,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436270000,
|
||||
|
@ -68,15 +68,15 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607436330000,
|
||||
"y": 0.0333333333333333,
|
||||
"y": 2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436360000,
|
||||
"y": 0.166666666666667,
|
||||
"y": 10,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436390000,
|
||||
"y": 0.0333333333333333,
|
||||
"y": 2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436420000,
|
||||
|
@ -88,11 +88,11 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607436480000,
|
||||
"y": 0.0666666666666667,
|
||||
"y": 4,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436510000,
|
||||
"y": 0.166666666666667,
|
||||
"y": 10,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436540000,
|
||||
|
@ -104,11 +104,11 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607436600000,
|
||||
"y": 0.0666666666666667,
|
||||
"y": 4,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436630000,
|
||||
"y": 0.233333333333333,
|
||||
"y": 14,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436660000,
|
||||
|
@ -124,7 +124,7 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607436750000,
|
||||
"y": 0.0666666666666667,
|
||||
"y": 4,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436780000,
|
||||
|
@ -132,15 +132,15 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607436810000,
|
||||
"y": 0.0333333333333333,
|
||||
"y": 2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436840000,
|
||||
"y": 0.0333333333333333,
|
||||
"y": 2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436870000,
|
||||
"y": 0.0666666666666667,
|
||||
"y": 4,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436900000,
|
||||
|
@ -152,11 +152,11 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607436960000,
|
||||
"y": 0.0666666666666667,
|
||||
"y": 4,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436990000,
|
||||
"y": 0.133333333333333,
|
||||
"y": 8,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437020000,
|
||||
|
@ -168,11 +168,11 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607437080000,
|
||||
"y": 0.0333333333333333,
|
||||
"y": 2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437110000,
|
||||
"y": 0.0333333333333333,
|
||||
"y": 2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437140000,
|
||||
|
@ -184,15 +184,15 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607437200000,
|
||||
"y": 0.0666666666666667,
|
||||
"y": 4,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437230000,
|
||||
"y": 0.233333333333333,
|
||||
"y": 14,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437260000,
|
||||
"y": 0.0333333333333333,
|
||||
"y": 2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437290000,
|
||||
|
@ -200,11 +200,11 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607437320000,
|
||||
"y": 0.0333333333333333,
|
||||
"y": 2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437350000,
|
||||
"y": 0.0666666666666667,
|
||||
"y": 4,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437380000,
|
||||
|
@ -216,11 +216,11 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607437440000,
|
||||
"y": 0.0333333333333333,
|
||||
"y": 2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437470000,
|
||||
"y": 0.1,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437500000,
|
||||
|
@ -232,7 +232,7 @@ Array [
|
|||
},
|
||||
Object {
|
||||
"x": 1607437560000,
|
||||
"y": 0.0333333333333333,
|
||||
"y": 2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437590000,
|
||||
|
@ -248,3 +248,740 @@ Array [
|
|||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`APM API tests basic apm_8.0.0 Throughput when data is loaded with time comparison has the correct throughput 1`] = `
|
||||
Object {
|
||||
"currentPeriod": Array [
|
||||
Object {
|
||||
"x": 1607436770000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436780000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436790000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436800000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436810000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436820000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436830000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436840000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436850000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436860000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436870000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436880000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436890000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436900000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436910000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436920000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436930000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436940000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436950000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436960000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436970000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436980000,
|
||||
"y": 12,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436990000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437000000,
|
||||
"y": 18,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437010000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437020000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437030000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437040000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437050000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437060000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437070000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437080000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437090000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437100000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437110000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437120000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437130000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437140000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437150000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437160000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437170000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437180000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437190000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437200000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437210000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437220000,
|
||||
"y": 12,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437230000,
|
||||
"y": 30,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437240000,
|
||||
"y": 12,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437250000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437260000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437270000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437280000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437290000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437300000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437310000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437320000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437330000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437340000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437350000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437360000,
|
||||
"y": 12,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437370000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437380000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437390000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437400000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437410000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437420000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437430000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437440000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437450000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437460000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437470000,
|
||||
"y": 12,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437480000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437490000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437500000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437510000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437520000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437530000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437540000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437550000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437560000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437570000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437580000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437590000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437600000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437610000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437620000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437630000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437640000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437650000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437660000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437670000,
|
||||
"y": 0,
|
||||
},
|
||||
],
|
||||
"previousPeriod": Array [
|
||||
Object {
|
||||
"x": 1607436770000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436780000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436790000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436800000,
|
||||
"y": 24,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436810000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436820000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436830000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436840000,
|
||||
"y": 12,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436850000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436860000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436870000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436880000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436890000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436900000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436910000,
|
||||
"y": 12,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436920000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436930000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436940000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436950000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436960000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436970000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436980000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436990000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437000000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437010000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437020000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437030000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437040000,
|
||||
"y": 18,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437050000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437060000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437070000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437080000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437090000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437100000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437110000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437120000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437130000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437140000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437150000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437160000,
|
||||
"y": 36,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437170000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437180000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437190000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437200000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437210000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437220000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437230000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437240000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437250000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437260000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437270000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437280000,
|
||||
"y": 30,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437290000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437300000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437310000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437320000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437330000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437340000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437350000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437360000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437370000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437380000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437390000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437400000,
|
||||
"y": 12,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437410000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437420000,
|
||||
"y": 24,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437430000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437440000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437450000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437460000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437470000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437480000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437490000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437500000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437510000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437520000,
|
||||
"y": 12,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437530000,
|
||||
"y": 30,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437540000,
|
||||
"y": 12,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437550000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437560000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437570000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437580000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437590000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437600000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437610000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437620000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437630000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437640000,
|
||||
"y": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437650000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437660000,
|
||||
"y": 6,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437670000,
|
||||
"y": 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -8,10 +8,15 @@
|
|||
import expect from '@kbn/expect';
|
||||
import qs from 'querystring';
|
||||
import { first, last } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { isFiniteNumber } from '../../../../plugins/apm/common/utils/is_finite_number';
|
||||
import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi';
|
||||
import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import { registry } from '../../common/registry';
|
||||
|
||||
type ThroughputReturn = APIReturnType<'GET /api/apm/services/{serviceName}/throughput'>;
|
||||
|
||||
export default function ApiTest({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
|
||||
|
@ -29,17 +34,16 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
})}`
|
||||
);
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body.throughput.length).to.be(0);
|
||||
expect(response.body.currentPeriod.length).to.be(0);
|
||||
expect(response.body.previousPeriod.length).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
let throughputResponse: ThroughputReturn;
|
||||
registry.when(
|
||||
'Throughput when data is loaded',
|
||||
{ config: 'basic', archives: [archiveName] },
|
||||
() => {
|
||||
let throughputResponse: {
|
||||
throughput: Array<{ x: number; y: number | null }>;
|
||||
};
|
||||
before(async () => {
|
||||
const response = await supertest.get(
|
||||
`/api/apm/services/opbeans-java/throughput?${qs.stringify({
|
||||
|
@ -53,31 +57,98 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('returns some data', () => {
|
||||
expect(throughputResponse.throughput.length).to.be.greaterThan(0);
|
||||
expect(throughputResponse.currentPeriod.length).to.be.greaterThan(0);
|
||||
expect(throughputResponse.previousPeriod.length).not.to.be.greaterThan(0);
|
||||
|
||||
const nonNullDataPoints = throughputResponse.throughput.filter(({ y }) => y !== null);
|
||||
const nonNullDataPoints = throughputResponse.currentPeriod.filter(({ y }) =>
|
||||
isFiniteNumber(y)
|
||||
);
|
||||
|
||||
expect(nonNullDataPoints.length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('has the correct start date', () => {
|
||||
expectSnapshot(
|
||||
new Date(first(throughputResponse.throughput)?.x ?? NaN).toISOString()
|
||||
new Date(first(throughputResponse.currentPeriod)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2020-12-08T13:57:30.000Z"`);
|
||||
});
|
||||
|
||||
it('has the correct end date', () => {
|
||||
expectSnapshot(
|
||||
new Date(last(throughputResponse.throughput)?.x ?? NaN).toISOString()
|
||||
new Date(last(throughputResponse.currentPeriod)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2020-12-08T14:27:30.000Z"`);
|
||||
});
|
||||
|
||||
it('has the correct number of buckets', () => {
|
||||
expectSnapshot(throughputResponse.throughput.length).toMatchInline(`61`);
|
||||
expectSnapshot(throughputResponse.currentPeriod.length).toMatchInline(`61`);
|
||||
});
|
||||
|
||||
it('has the correct throughput', () => {
|
||||
expectSnapshot(throughputResponse.throughput).toMatch();
|
||||
expectSnapshot(throughputResponse.currentPeriod).toMatch();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
registry.when(
|
||||
'Throughput when data is loaded with time comparison',
|
||||
{ config: 'basic', archives: [archiveName] },
|
||||
() => {
|
||||
before(async () => {
|
||||
const response = await supertest.get(
|
||||
`/api/apm/services/opbeans-java/throughput?${qs.stringify({
|
||||
uiFilters: encodeURIComponent('{}'),
|
||||
transactionType: 'request',
|
||||
start: moment(metadata.end).subtract(15, 'minutes').toISOString(),
|
||||
end: metadata.end,
|
||||
comparisonStart: metadata.start,
|
||||
comparisonEnd: moment(metadata.start).add(15, 'minutes').toISOString(),
|
||||
})}`
|
||||
);
|
||||
throughputResponse = response.body;
|
||||
});
|
||||
|
||||
it('returns some data', () => {
|
||||
expect(throughputResponse.currentPeriod.length).to.be.greaterThan(0);
|
||||
expect(throughputResponse.previousPeriod.length).to.be.greaterThan(0);
|
||||
|
||||
const currentPeriodNonNullDataPoints = throughputResponse.currentPeriod.filter(({ y }) =>
|
||||
isFiniteNumber(y)
|
||||
);
|
||||
const previousPeriodNonNullDataPoints = throughputResponse.previousPeriod.filter(({ y }) =>
|
||||
isFiniteNumber(y)
|
||||
);
|
||||
|
||||
expect(currentPeriodNonNullDataPoints.length).to.be.greaterThan(0);
|
||||
expect(previousPeriodNonNullDataPoints.length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('has the correct start date', () => {
|
||||
expectSnapshot(
|
||||
new Date(first(throughputResponse.currentPeriod)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2020-12-08T14:12:50.000Z"`);
|
||||
|
||||
expectSnapshot(
|
||||
new Date(first(throughputResponse.previousPeriod)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2020-12-08T14:12:50.000Z"`);
|
||||
});
|
||||
|
||||
it('has the correct end date', () => {
|
||||
expectSnapshot(
|
||||
new Date(last(throughputResponse.currentPeriod)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2020-12-08T14:27:50.000Z"`);
|
||||
|
||||
expectSnapshot(
|
||||
new Date(last(throughputResponse.previousPeriod)?.x ?? NaN).toISOString()
|
||||
).toMatchInline(`"2020-12-08T14:27:50.000Z"`);
|
||||
});
|
||||
|
||||
it('has the correct number of buckets', () => {
|
||||
expectSnapshot(throughputResponse.currentPeriod.length).toMatchInline(`91`);
|
||||
expectSnapshot(throughputResponse.previousPeriod.length).toMatchInline(`91`);
|
||||
});
|
||||
|
||||
it('has the correct throughput', () => {
|
||||
expectSnapshot(throughputResponse).toMatch();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue