mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* adding time comparison to latency chart * adding time comparison to latency chart * fixing TS * fixing api test * addressing PR comments * adding api test * addressing PR comments * fixing api test * rounding date diff * addressing PR comments * fixing api test * refactoring * fixing ts issue * fixing offset function * fixing offset function * addressing PR comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
0c1d56217d
commit
773c11c61e
14 changed files with 690 additions and 287 deletions
|
@ -13,6 +13,7 @@ import { LatencyAggregationType } from '../../../../../common/latency_aggregatio
|
|||
import { getDurationFormatter } from '../../../../../common/utils/formatters';
|
||||
import { useLicenseContext } from '../../../../context/license/use_license_context';
|
||||
import { useUrlParams } from '../../../../context/url_params_context/use_url_params';
|
||||
import { useTheme } from '../../../../hooks/use_theme';
|
||||
import { useTransactionLatencyChartsFetcher } from '../../../../hooks/use_transaction_latency_chart_fetcher';
|
||||
import { TimeseriesChart } from '../../../shared/charts/timeseries_chart';
|
||||
import {
|
||||
|
@ -21,6 +22,7 @@ import {
|
|||
} from '../../../shared/charts/transaction_charts/helper';
|
||||
import { MLHeader } from '../../../shared/charts/transaction_charts/ml_header';
|
||||
import * as urlHelpers from '../../../shared/Links/url_helpers';
|
||||
import { getComparisonChartTheme } from '../../time_comparison/get_time_range_comparison';
|
||||
|
||||
interface Props {
|
||||
height?: number;
|
||||
|
@ -32,10 +34,16 @@ const options: Array<{ value: LatencyAggregationType; text: string }> = [
|
|||
{ value: LatencyAggregationType.p99, text: '99th percentile' },
|
||||
];
|
||||
|
||||
function filterNil<T>(value: T | null | undefined): value is T {
|
||||
return value != null;
|
||||
}
|
||||
|
||||
export function LatencyChart({ height }: Props) {
|
||||
const history = useHistory();
|
||||
const theme = useTheme();
|
||||
const comparisonChartTheme = getComparisonChartTheme(theme);
|
||||
const { urlParams } = useUrlParams();
|
||||
const { latencyAggregationType } = urlParams;
|
||||
const { latencyAggregationType, comparisonEnabled } = urlParams;
|
||||
const license = useLicenseContext();
|
||||
|
||||
const {
|
||||
|
@ -43,9 +51,19 @@ export function LatencyChart({ height }: Props) {
|
|||
latencyChartsStatus,
|
||||
} = useTransactionLatencyChartsFetcher();
|
||||
|
||||
const { latencyTimeseries, anomalyTimeseries, mlJobId } = latencyChartsData;
|
||||
const {
|
||||
currentPeriod,
|
||||
previousPeriod,
|
||||
anomalyTimeseries,
|
||||
mlJobId,
|
||||
} = latencyChartsData;
|
||||
|
||||
const latencyMaxY = getMaxY(latencyTimeseries);
|
||||
const timeseries = [
|
||||
currentPeriod,
|
||||
comparisonEnabled ? previousPeriod : undefined,
|
||||
].filter(filterNil);
|
||||
|
||||
const latencyMaxY = getMaxY(timeseries);
|
||||
const latencyFormatter = getDurationFormatter(latencyMaxY);
|
||||
|
||||
return (
|
||||
|
@ -99,7 +117,8 @@ export function LatencyChart({ height }: Props) {
|
|||
height={height}
|
||||
fetchStatus={latencyChartsStatus}
|
||||
id="latencyChart"
|
||||
timeseries={latencyTimeseries}
|
||||
customTheme={comparisonChartTheme}
|
||||
timeseries={timeseries}
|
||||
yLabelFormat={getResponseTimeTickFormatter(latencyFormatter)}
|
||||
anomalyTimeseries={anomalyTimeseries}
|
||||
/>
|
||||
|
|
|
@ -84,12 +84,14 @@ function getSelectOptions({
|
|||
}),
|
||||
};
|
||||
|
||||
const dateDiff = getDateDifference({
|
||||
start: momentStart,
|
||||
end: momentEnd,
|
||||
unitOfTime: 'days',
|
||||
precise: true,
|
||||
});
|
||||
const dateDiff = Number(
|
||||
getDateDifference({
|
||||
start: momentStart,
|
||||
end: momentEnd,
|
||||
unitOfTime: 'days',
|
||||
precise: true,
|
||||
}).toFixed(2)
|
||||
);
|
||||
|
||||
const isRangeToNow = rangeTo === 'now';
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import { useUrlParams } from '../context/url_params_context/use_url_params';
|
|||
import { useApmServiceContext } from '../context/apm_service/use_apm_service_context';
|
||||
import { getLatencyChartSelector } from '../selectors/latency_chart_selectors';
|
||||
import { useTheme } from './use_theme';
|
||||
import { getTimeRangeComparison } from '../components/shared/time_comparison/get_time_range_comparison';
|
||||
|
||||
export function useTransactionLatencyChartsFetcher() {
|
||||
const { serviceName } = useParams<{ serviceName?: string }>();
|
||||
|
@ -25,9 +26,16 @@ export function useTransactionLatencyChartsFetcher() {
|
|||
end,
|
||||
transactionName,
|
||||
latencyAggregationType,
|
||||
comparisonType,
|
||||
},
|
||||
} = useUrlParams();
|
||||
|
||||
const { comparisonStart, comparisonEnd } = getTimeRangeComparison({
|
||||
start,
|
||||
end,
|
||||
comparisonType,
|
||||
});
|
||||
|
||||
const { data, error, status } = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (
|
||||
|
@ -50,6 +58,8 @@ export function useTransactionLatencyChartsFetcher() {
|
|||
transactionType,
|
||||
transactionName,
|
||||
latencyAggregationType,
|
||||
comparisonStart,
|
||||
comparisonEnd,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -64,6 +74,8 @@ export function useTransactionLatencyChartsFetcher() {
|
|||
transactionName,
|
||||
transactionType,
|
||||
latencyAggregationType,
|
||||
comparisonStart,
|
||||
comparisonEnd,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -18,12 +18,19 @@ const theme = {
|
|||
euiColorVis5: 'red',
|
||||
euiColorVis7: 'black',
|
||||
euiColorVis9: 'yellow',
|
||||
euiColorLightestShade: 'green',
|
||||
},
|
||||
} as EuiTheme;
|
||||
|
||||
const latencyChartData = {
|
||||
overallAvgDuration: 1,
|
||||
latencyTimeseries: [{ x: 1, y: 10 }],
|
||||
currentPeriod: {
|
||||
overallAvgDuration: 1,
|
||||
latencyTimeseries: [{ x: 1, y: 10 }],
|
||||
},
|
||||
previousPeriod: {
|
||||
overallAvgDuration: 1,
|
||||
latencyTimeseries: [{ x: 1, y: 10 }],
|
||||
},
|
||||
anomalyTimeseries: {
|
||||
jobId: '1',
|
||||
anomalyBoundaries: [{ x: 1, y: 2, y0: 1 }],
|
||||
|
@ -36,69 +43,84 @@ describe('getLatencyChartSelector', () => {
|
|||
it('returns default values when data is undefined', () => {
|
||||
const latencyChart = getLatencyChartSelector({ theme });
|
||||
expect(latencyChart).toEqual({
|
||||
latencyTimeseries: [],
|
||||
currentPeriod: undefined,
|
||||
previousPeriod: undefined,
|
||||
mlJobId: undefined,
|
||||
anomalyTimeseries: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns average timeseries', () => {
|
||||
const { anomalyTimeseries, ...latencyWithouAnomaly } = latencyChartData;
|
||||
const { anomalyTimeseries, ...latencyWithoutAnomaly } = latencyChartData;
|
||||
const latencyTimeseries = getLatencyChartSelector({
|
||||
latencyChart: latencyWithouAnomaly as LatencyChartsResponse,
|
||||
latencyChart: latencyWithoutAnomaly as LatencyChartsResponse,
|
||||
theme,
|
||||
latencyAggregationType: LatencyAggregationType.avg,
|
||||
});
|
||||
expect(latencyTimeseries).toEqual({
|
||||
latencyTimeseries: [
|
||||
{
|
||||
title: 'Average',
|
||||
data: [{ x: 1, y: 10 }],
|
||||
legendValue: '1 μs',
|
||||
type: 'linemark',
|
||||
color: 'blue',
|
||||
},
|
||||
],
|
||||
currentPeriod: {
|
||||
title: 'Average',
|
||||
data: [{ x: 1, y: 10 }],
|
||||
legendValue: '1 μs',
|
||||
type: 'linemark',
|
||||
color: 'blue',
|
||||
},
|
||||
|
||||
previousPeriod: {
|
||||
color: 'green',
|
||||
data: [{ x: 1, y: 10 }],
|
||||
type: 'area',
|
||||
title: 'Previous period',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 95th percentile timeseries', () => {
|
||||
const { anomalyTimeseries, ...latencyWithouAnomaly } = latencyChartData;
|
||||
const { anomalyTimeseries, ...latencyWithoutAnomaly } = latencyChartData;
|
||||
const latencyTimeseries = getLatencyChartSelector({
|
||||
latencyChart: latencyWithouAnomaly as LatencyChartsResponse,
|
||||
latencyChart: latencyWithoutAnomaly as LatencyChartsResponse,
|
||||
theme,
|
||||
latencyAggregationType: LatencyAggregationType.p95,
|
||||
});
|
||||
expect(latencyTimeseries).toEqual({
|
||||
latencyTimeseries: [
|
||||
{
|
||||
title: '95th percentile',
|
||||
data: [{ x: 1, y: 10 }],
|
||||
titleShort: '95th',
|
||||
type: 'linemark',
|
||||
color: 'red',
|
||||
},
|
||||
],
|
||||
currentPeriod: {
|
||||
title: '95th percentile',
|
||||
titleShort: '95th',
|
||||
data: [{ x: 1, y: 10 }],
|
||||
type: 'linemark',
|
||||
color: 'red',
|
||||
},
|
||||
previousPeriod: {
|
||||
data: [{ x: 1, y: 10 }],
|
||||
type: 'area',
|
||||
color: 'green',
|
||||
title: 'Previous period',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 99th percentile timeseries', () => {
|
||||
const { anomalyTimeseries, ...latencyWithouAnomaly } = latencyChartData;
|
||||
const { anomalyTimeseries, ...latencyWithoutAnomaly } = latencyChartData;
|
||||
const latencyTimeseries = getLatencyChartSelector({
|
||||
latencyChart: latencyWithouAnomaly as LatencyChartsResponse,
|
||||
latencyChart: latencyWithoutAnomaly as LatencyChartsResponse,
|
||||
theme,
|
||||
latencyAggregationType: LatencyAggregationType.p99,
|
||||
});
|
||||
|
||||
expect(latencyTimeseries).toEqual({
|
||||
latencyTimeseries: [
|
||||
{
|
||||
title: '99th percentile',
|
||||
data: [{ x: 1, y: 10 }],
|
||||
titleShort: '99th',
|
||||
type: 'linemark',
|
||||
color: 'black',
|
||||
},
|
||||
],
|
||||
currentPeriod: {
|
||||
title: '99th percentile',
|
||||
titleShort: '99th',
|
||||
data: [{ x: 1, y: 10 }],
|
||||
type: 'linemark',
|
||||
color: 'black',
|
||||
},
|
||||
previousPeriod: {
|
||||
data: [{ x: 1, y: 10 }],
|
||||
type: 'area',
|
||||
color: 'green',
|
||||
title: 'Previous period',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -111,76 +133,52 @@ describe('getLatencyChartSelector', () => {
|
|||
latencyAggregationType: LatencyAggregationType.p99,
|
||||
});
|
||||
expect(latencyTimeseries).toEqual({
|
||||
currentPeriod: {
|
||||
title: '99th percentile',
|
||||
titleShort: '99th',
|
||||
data: [{ x: 1, y: 10 }],
|
||||
type: 'linemark',
|
||||
color: 'black',
|
||||
},
|
||||
previousPeriod: {
|
||||
data: [{ x: 1, y: 10 }],
|
||||
type: 'area',
|
||||
color: 'green',
|
||||
title: 'Previous period',
|
||||
},
|
||||
mlJobId: '1',
|
||||
anomalyTimeseries: {
|
||||
boundaries: [
|
||||
{
|
||||
color: 'rgba(0,0,0,0)',
|
||||
areaSeriesStyle: {
|
||||
point: {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{
|
||||
x: 1,
|
||||
y: 1,
|
||||
},
|
||||
],
|
||||
type: 'area',
|
||||
fit: 'lookahead',
|
||||
hideLegend: true,
|
||||
hideTooltipValue: true,
|
||||
stackAccessors: ['y'],
|
||||
areaSeriesStyle: { point: { opacity: 0 } },
|
||||
title: 'anomalyBoundariesLower',
|
||||
type: 'area',
|
||||
data: [{ x: 1, y: 1 }],
|
||||
color: 'rgba(0,0,0,0)',
|
||||
},
|
||||
{
|
||||
color: 'rgba(0,0,255,0.5)',
|
||||
areaSeriesStyle: {
|
||||
point: {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{
|
||||
x: 1,
|
||||
y: 1,
|
||||
},
|
||||
],
|
||||
type: 'area',
|
||||
fit: 'lookahead',
|
||||
hideLegend: true,
|
||||
hideTooltipValue: true,
|
||||
stackAccessors: ['y'],
|
||||
areaSeriesStyle: { point: { opacity: 0 } },
|
||||
title: 'anomalyBoundariesUpper',
|
||||
type: 'area',
|
||||
data: [{ x: 1, y: 1 }],
|
||||
color: 'rgba(0,0,255,0.5)',
|
||||
},
|
||||
],
|
||||
scores: {
|
||||
color: 'yellow',
|
||||
data: [
|
||||
{
|
||||
x: 1,
|
||||
x0: 2,
|
||||
},
|
||||
],
|
||||
title: 'anomalyScores',
|
||||
type: 'rectAnnotation',
|
||||
data: [{ x: 1, x0: 2 }],
|
||||
color: 'yellow',
|
||||
},
|
||||
},
|
||||
latencyTimeseries: [
|
||||
{
|
||||
color: 'black',
|
||||
data: [
|
||||
{
|
||||
x: 1,
|
||||
y: 10,
|
||||
},
|
||||
],
|
||||
title: '99th percentile',
|
||||
titleShort: '99th',
|
||||
type: 'linemark',
|
||||
},
|
||||
],
|
||||
mlJobId: '1',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,7 +16,8 @@ import { APIReturnType } from '../services/rest/createCallApmApi';
|
|||
export type LatencyChartsResponse = APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/latency'>;
|
||||
|
||||
export interface LatencyChartData {
|
||||
latencyTimeseries: Array<APMChartSpec<Coordinate>>;
|
||||
currentPeriod?: APMChartSpec<Coordinate>;
|
||||
previousPeriod?: APMChartSpec<Coordinate>;
|
||||
mlJobId?: string;
|
||||
anomalyTimeseries?: { boundaries: APMChartSpec[]; scores: APMChartSpec };
|
||||
}
|
||||
|
@ -29,20 +30,23 @@ export function getLatencyChartSelector({
|
|||
latencyChart?: LatencyChartsResponse;
|
||||
theme: EuiTheme;
|
||||
latencyAggregationType?: string;
|
||||
}): LatencyChartData {
|
||||
if (!latencyChart?.latencyTimeseries || !latencyAggregationType) {
|
||||
return {
|
||||
latencyTimeseries: [],
|
||||
mlJobId: undefined,
|
||||
anomalyTimeseries: undefined,
|
||||
};
|
||||
}): Partial<LatencyChartData> {
|
||||
if (
|
||||
!latencyChart?.currentPeriod.latencyTimeseries ||
|
||||
!latencyAggregationType
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
latencyTimeseries: getLatencyTimeseries({
|
||||
latencyChart,
|
||||
currentPeriod: getLatencyTimeseries({
|
||||
latencyChart: latencyChart.currentPeriod,
|
||||
theme,
|
||||
latencyAggregationType,
|
||||
}),
|
||||
previousPeriod: getPreviousPeriodTimeseries({
|
||||
previousPeriod: latencyChart.previousPeriod,
|
||||
theme,
|
||||
}),
|
||||
mlJobId: latencyChart.anomalyTimeseries?.jobId,
|
||||
anomalyTimeseries: getAnomalyTimeseries({
|
||||
anomalyTimeseries: latencyChart.anomalyTimeseries,
|
||||
|
@ -51,12 +55,30 @@ export function getLatencyChartSelector({
|
|||
};
|
||||
}
|
||||
|
||||
function getPreviousPeriodTimeseries({
|
||||
previousPeriod,
|
||||
theme,
|
||||
}: {
|
||||
previousPeriod: LatencyChartsResponse['previousPeriod'];
|
||||
theme: EuiTheme;
|
||||
}) {
|
||||
return {
|
||||
data: previousPeriod.latencyTimeseries ?? [],
|
||||
type: 'area',
|
||||
color: theme.eui.euiColorLightestShade,
|
||||
title: i18n.translate(
|
||||
'xpack.apm.serviceOverview.latencyChartTitle.previousPeriodLabel',
|
||||
{ defaultMessage: 'Previous period' }
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function getLatencyTimeseries({
|
||||
latencyChart,
|
||||
theme,
|
||||
latencyAggregationType,
|
||||
}: {
|
||||
latencyChart: LatencyChartsResponse;
|
||||
latencyChart: LatencyChartsResponse['currentPeriod'];
|
||||
theme: EuiTheme;
|
||||
latencyAggregationType: string;
|
||||
}) {
|
||||
|
@ -65,49 +87,42 @@ function getLatencyTimeseries({
|
|||
|
||||
switch (latencyAggregationType) {
|
||||
case 'avg': {
|
||||
return [
|
||||
{
|
||||
title: i18n.translate(
|
||||
'xpack.apm.transactions.latency.chart.averageLabel',
|
||||
{ defaultMessage: 'Average' }
|
||||
),
|
||||
data: latencyTimeseries,
|
||||
legendValue: asDuration(overallAvgDuration),
|
||||
type: 'linemark',
|
||||
color: theme.eui.euiColorVis1,
|
||||
},
|
||||
];
|
||||
return {
|
||||
title: i18n.translate(
|
||||
'xpack.apm.transactions.latency.chart.averageLabel',
|
||||
{ defaultMessage: 'Average' }
|
||||
),
|
||||
data: latencyTimeseries,
|
||||
legendValue: asDuration(overallAvgDuration),
|
||||
type: 'linemark',
|
||||
color: theme.eui.euiColorVis1,
|
||||
};
|
||||
}
|
||||
case 'p95': {
|
||||
return [
|
||||
{
|
||||
title: i18n.translate(
|
||||
'xpack.apm.transactions.latency.chart.95thPercentileLabel',
|
||||
{ defaultMessage: '95th percentile' }
|
||||
),
|
||||
titleShort: '95th',
|
||||
data: latencyTimeseries,
|
||||
type: 'linemark',
|
||||
color: theme.eui.euiColorVis5,
|
||||
},
|
||||
];
|
||||
return {
|
||||
title: i18n.translate(
|
||||
'xpack.apm.transactions.latency.chart.95thPercentileLabel',
|
||||
{ defaultMessage: '95th percentile' }
|
||||
),
|
||||
titleShort: '95th',
|
||||
data: latencyTimeseries,
|
||||
type: 'linemark',
|
||||
color: theme.eui.euiColorVis5,
|
||||
};
|
||||
}
|
||||
case 'p99': {
|
||||
return [
|
||||
{
|
||||
title: i18n.translate(
|
||||
'xpack.apm.transactions.latency.chart.99thPercentileLabel',
|
||||
{ defaultMessage: '99th percentile' }
|
||||
),
|
||||
titleShort: '99th',
|
||||
data: latencyTimeseries,
|
||||
type: 'linemark',
|
||||
color: theme.eui.euiColorVis7,
|
||||
},
|
||||
];
|
||||
return {
|
||||
title: i18n.translate(
|
||||
'xpack.apm.transactions.latency.chart.99thPercentileLabel',
|
||||
{ defaultMessage: '99th percentile' }
|
||||
),
|
||||
titleShort: '99th',
|
||||
data: latencyTimeseries,
|
||||
type: 'linemark',
|
||||
color: theme.eui.euiColorVis7,
|
||||
};
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function getAnomalyTimeseries({
|
||||
|
|
|
@ -47,7 +47,6 @@ export async function getServiceTransactionGroupComparisonStatistics({
|
|||
latencyAggregationType,
|
||||
start,
|
||||
end,
|
||||
getOffsetXCoordinate,
|
||||
}: {
|
||||
environment?: string;
|
||||
kuery?: string;
|
||||
|
@ -60,7 +59,6 @@ export async function getServiceTransactionGroupComparisonStatistics({
|
|||
latencyAggregationType: LatencyAggregationType;
|
||||
start: number;
|
||||
end: number;
|
||||
getOffsetXCoordinate?: (timeseries: Coordinate[]) => Coordinate[];
|
||||
}): Promise<
|
||||
Array<{
|
||||
transactionName: string;
|
||||
|
@ -175,15 +173,9 @@ export async function getServiceTransactionGroupComparisonStatistics({
|
|||
bucket.transaction_group_total_duration.value || 0;
|
||||
return {
|
||||
transactionName,
|
||||
latency: getOffsetXCoordinate
|
||||
? getOffsetXCoordinate(latency)
|
||||
: latency,
|
||||
throughput: getOffsetXCoordinate
|
||||
? getOffsetXCoordinate(throughput)
|
||||
: throughput,
|
||||
errorRate: getOffsetXCoordinate
|
||||
? getOffsetXCoordinate(errorRate)
|
||||
: errorRate,
|
||||
latency,
|
||||
throughput,
|
||||
errorRate,
|
||||
impact: totalDuration
|
||||
? (transactionGroupTotalDuration * 100) / totalDuration
|
||||
: 0,
|
||||
|
@ -244,12 +236,6 @@ export async function getServiceTransactionGroupComparisonStatisticsPeriods({
|
|||
...commonProps,
|
||||
start: comparisonStart,
|
||||
end: comparisonEnd,
|
||||
getOffsetXCoordinate: (timeseries: Coordinate[]) =>
|
||||
offsetPreviousPeriodCoordinates({
|
||||
currentPeriodStart: start,
|
||||
previousPeriodStart: comparisonStart,
|
||||
previousPeriodTimeseries: timeseries,
|
||||
}),
|
||||
})
|
||||
: [];
|
||||
|
||||
|
@ -258,8 +244,29 @@ export async function getServiceTransactionGroupComparisonStatisticsPeriods({
|
|||
previousPeriodPromise,
|
||||
]);
|
||||
|
||||
const firtCurrentPeriod = currentPeriod.length ? currentPeriod[0] : undefined;
|
||||
|
||||
return {
|
||||
currentPeriod: keyBy(currentPeriod, 'transactionName'),
|
||||
previousPeriod: keyBy(previousPeriod, 'transactionName'),
|
||||
previousPeriod: keyBy(
|
||||
previousPeriod.map((data) => {
|
||||
return {
|
||||
...data,
|
||||
errorRate: offsetPreviousPeriodCoordinates({
|
||||
currentPeriodTimeseries: firtCurrentPeriod?.errorRate,
|
||||
previousPeriodTimeseries: data.errorRate,
|
||||
}),
|
||||
throughput: offsetPreviousPeriodCoordinates({
|
||||
currentPeriodTimeseries: firtCurrentPeriod?.throughput,
|
||||
previousPeriodTimeseries: data.throughput,
|
||||
}),
|
||||
latency: offsetPreviousPeriodCoordinates({
|
||||
currentPeriodTimeseries: firtCurrentPeriod?.latency,
|
||||
previousPeriodTimeseries: data.latency,
|
||||
}),
|
||||
};
|
||||
}),
|
||||
'transactionName'
|
||||
),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
} from '../../../lib/helpers/aggregated_transactions';
|
||||
import { getBucketSize } from '../../../lib/helpers/get_bucket_size';
|
||||
import { Setup, SetupTimeRange } from '../../../lib/helpers/setup_request';
|
||||
import { offsetPreviousPeriodCoordinates } from '../../../utils/offset_previous_period_coordinate';
|
||||
import { withApmSpan } from '../../../utils/with_apm_span';
|
||||
import {
|
||||
getLatencyAggregation,
|
||||
|
@ -43,17 +44,21 @@ function searchLatency({
|
|||
setup,
|
||||
searchAggregatedTransactions,
|
||||
latencyAggregationType,
|
||||
start,
|
||||
end,
|
||||
}: {
|
||||
environment?: string;
|
||||
kuery?: string;
|
||||
serviceName: string;
|
||||
transactionType: string | undefined;
|
||||
transactionName: string | undefined;
|
||||
setup: Setup & SetupTimeRange;
|
||||
setup: Setup;
|
||||
searchAggregatedTransactions: boolean;
|
||||
latencyAggregationType: LatencyAggregationType;
|
||||
start: number;
|
||||
end: number;
|
||||
}) {
|
||||
const { start, end, apmEventClient } = setup;
|
||||
const { apmEventClient } = setup;
|
||||
const { intervalString } = getBucketSize({ start, end });
|
||||
|
||||
const filter: ESFilter[] = [
|
||||
|
@ -119,15 +124,19 @@ export function getLatencyTimeseries({
|
|||
setup,
|
||||
searchAggregatedTransactions,
|
||||
latencyAggregationType,
|
||||
start,
|
||||
end,
|
||||
}: {
|
||||
environment?: string;
|
||||
kuery?: string;
|
||||
serviceName: string;
|
||||
transactionType: string | undefined;
|
||||
transactionName: string | undefined;
|
||||
setup: Setup & SetupTimeRange;
|
||||
setup: Setup;
|
||||
searchAggregatedTransactions: boolean;
|
||||
latencyAggregationType: LatencyAggregationType;
|
||||
start: number;
|
||||
end: number;
|
||||
}) {
|
||||
return withApmSpan('get_latency_charts', async () => {
|
||||
const response = await searchLatency({
|
||||
|
@ -139,6 +148,8 @@ export function getLatencyTimeseries({
|
|||
setup,
|
||||
searchAggregatedTransactions,
|
||||
latencyAggregationType,
|
||||
start,
|
||||
end,
|
||||
});
|
||||
|
||||
if (!response.aggregations) {
|
||||
|
@ -162,3 +173,65 @@ export function getLatencyTimeseries({
|
|||
};
|
||||
});
|
||||
}
|
||||
|
||||
export async function getLatencyPeriods({
|
||||
serviceName,
|
||||
transactionType,
|
||||
transactionName,
|
||||
setup,
|
||||
searchAggregatedTransactions,
|
||||
latencyAggregationType,
|
||||
comparisonStart,
|
||||
comparisonEnd,
|
||||
}: {
|
||||
serviceName: string;
|
||||
transactionType: string | undefined;
|
||||
transactionName: string | undefined;
|
||||
setup: Setup & SetupTimeRange;
|
||||
searchAggregatedTransactions: boolean;
|
||||
latencyAggregationType: LatencyAggregationType;
|
||||
comparisonStart?: number;
|
||||
comparisonEnd?: number;
|
||||
}) {
|
||||
const { start, end } = setup;
|
||||
const options = {
|
||||
serviceName,
|
||||
transactionType,
|
||||
transactionName,
|
||||
setup,
|
||||
searchAggregatedTransactions,
|
||||
};
|
||||
|
||||
const currentPeriodPromise = getLatencyTimeseries({
|
||||
...options,
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: latencyAggregationType as LatencyAggregationType,
|
||||
});
|
||||
|
||||
const previousPeriodPromise =
|
||||
comparisonStart && comparisonEnd
|
||||
? getLatencyTimeseries({
|
||||
...options,
|
||||
start: comparisonStart,
|
||||
end: comparisonEnd,
|
||||
latencyAggregationType: latencyAggregationType as LatencyAggregationType,
|
||||
})
|
||||
: { latencyTimeseries: [], overallAvgDuration: null };
|
||||
|
||||
const [currentPeriod, previousPeriod] = await Promise.all([
|
||||
currentPeriodPromise,
|
||||
previousPeriodPromise,
|
||||
]);
|
||||
|
||||
return {
|
||||
currentPeriod,
|
||||
previousPeriod: {
|
||||
...previousPeriod,
|
||||
latencyTimeseries: offsetPreviousPeriodCoordinates({
|
||||
currentPeriodTimeseries: currentPeriod.latencyTimeseries,
|
||||
previousPeriodTimeseries: previousPeriod.latencyTimeseries,
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -408,19 +408,16 @@ export const serviceThroughputRoute = createRoute({
|
|||
...commonProps,
|
||||
start: comparisonStart,
|
||||
end: comparisonEnd,
|
||||
}).then((coordinates) =>
|
||||
offsetPreviousPeriodCoordinates({
|
||||
currentPeriodStart: start,
|
||||
previousPeriodStart: comparisonStart,
|
||||
previousPeriodTimeseries: coordinates,
|
||||
})
|
||||
)
|
||||
})
|
||||
: [],
|
||||
]);
|
||||
|
||||
return {
|
||||
currentPeriod,
|
||||
previousPeriod,
|
||||
previousPeriod: offsetPreviousPeriodCoordinates({
|
||||
currentPeriodTimeseries: currentPeriod,
|
||||
previousPeriodTimeseries: previousPeriod,
|
||||
}),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -19,14 +19,14 @@ import { getServiceTransactionGroupComparisonStatisticsPeriods } from '../lib/se
|
|||
import { getTransactionBreakdown } from '../lib/transactions/breakdown';
|
||||
import { getTransactionDistribution } from '../lib/transactions/distribution';
|
||||
import { getAnomalySeries } from '../lib/transactions/get_anomaly_data';
|
||||
import { getLatencyTimeseries } from '../lib/transactions/get_latency_charts';
|
||||
import { getLatencyPeriods } from '../lib/transactions/get_latency_charts';
|
||||
import { getThroughputCharts } from '../lib/transactions/get_throughput_charts';
|
||||
import { getTransactionGroupList } from '../lib/transaction_groups';
|
||||
import { getErrorRate } from '../lib/transaction_groups/get_error_rate';
|
||||
import { createRoute } from './create_route';
|
||||
import {
|
||||
environmentRt,
|
||||
comparisonRangeRt,
|
||||
environmentRt,
|
||||
rangeRt,
|
||||
kueryRt,
|
||||
} from './default_api_types';
|
||||
|
@ -179,16 +179,12 @@ export const transactionLatencyChartsRoute = createRoute({
|
|||
serviceName: t.string,
|
||||
}),
|
||||
query: t.intersection([
|
||||
t.partial({
|
||||
transactionName: t.string,
|
||||
}),
|
||||
t.type({
|
||||
transactionType: t.string,
|
||||
latencyAggregationType: latencyAggregationTypeRt,
|
||||
}),
|
||||
environmentRt,
|
||||
kueryRt,
|
||||
rangeRt,
|
||||
t.partial({ transactionName: t.string }),
|
||||
t.intersection([environmentRt, kueryRt, rangeRt, comparisonRangeRt]),
|
||||
]),
|
||||
}),
|
||||
options: { tags: ['access:apm'] },
|
||||
|
@ -202,6 +198,8 @@ export const transactionLatencyChartsRoute = createRoute({
|
|||
transactionType,
|
||||
transactionName,
|
||||
latencyAggregationType,
|
||||
comparisonStart,
|
||||
comparisonEnd,
|
||||
} = context.params.query;
|
||||
|
||||
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
|
||||
|
@ -219,10 +217,15 @@ export const transactionLatencyChartsRoute = createRoute({
|
|||
logger,
|
||||
};
|
||||
|
||||
const [latencyData, anomalyTimeseries] = await Promise.all([
|
||||
getLatencyTimeseries({
|
||||
const [
|
||||
{ currentPeriod, previousPeriod },
|
||||
anomalyTimeseries,
|
||||
] = await Promise.all([
|
||||
getLatencyPeriods({
|
||||
...options,
|
||||
latencyAggregationType: latencyAggregationType as LatencyAggregationType,
|
||||
comparisonStart,
|
||||
comparisonEnd,
|
||||
}),
|
||||
getAnomalySeries(options).catch((error) => {
|
||||
logger.warn(`Unable to retrieve anomalies for latency charts.`);
|
||||
|
@ -231,11 +234,9 @@ export const transactionLatencyChartsRoute = createRoute({
|
|||
}),
|
||||
]);
|
||||
|
||||
const { latencyTimeseries, overallAvgDuration } = latencyData;
|
||||
|
||||
return {
|
||||
latencyTimeseries,
|
||||
overallAvgDuration,
|
||||
currentPeriod,
|
||||
previousPeriod,
|
||||
anomalyTimeseries,
|
||||
};
|
||||
},
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
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();
|
||||
const currentPeriodTimeseries: Coordinate[] = [
|
||||
{ x: new Date('2021-01-28T14:45:00.000Z').valueOf(), y: 0 },
|
||||
];
|
||||
|
||||
describe('mergePeriodsTimeseries', () => {
|
||||
describe('returns empty array', () => {
|
||||
it('when previous timeseries is not defined', () => {
|
||||
expect(
|
||||
offsetPreviousPeriodCoordinates({
|
||||
currentPeriodStart,
|
||||
previousPeriodStart,
|
||||
currentPeriodTimeseries,
|
||||
previousPeriodTimeseries: undefined,
|
||||
})
|
||||
).toEqual([]);
|
||||
|
@ -25,8 +25,7 @@ describe('mergePeriodsTimeseries', () => {
|
|||
it('when previous timeseries is empty', () => {
|
||||
expect(
|
||||
offsetPreviousPeriodCoordinates({
|
||||
currentPeriodStart,
|
||||
previousPeriodStart,
|
||||
currentPeriodTimeseries,
|
||||
previousPeriodTimeseries: [],
|
||||
})
|
||||
).toEqual([]);
|
||||
|
@ -43,8 +42,7 @@ describe('mergePeriodsTimeseries', () => {
|
|||
|
||||
expect(
|
||||
offsetPreviousPeriodCoordinates({
|
||||
currentPeriodStart,
|
||||
previousPeriodStart,
|
||||
currentPeriodTimeseries,
|
||||
previousPeriodTimeseries,
|
||||
})
|
||||
).toEqual([
|
||||
|
|
|
@ -9,19 +9,20 @@ import moment from 'moment';
|
|||
import { Coordinate } from '../../typings/timeseries';
|
||||
|
||||
export function offsetPreviousPeriodCoordinates({
|
||||
currentPeriodStart,
|
||||
previousPeriodStart,
|
||||
currentPeriodTimeseries,
|
||||
previousPeriodTimeseries,
|
||||
}: {
|
||||
currentPeriodStart: number;
|
||||
previousPeriodStart: number;
|
||||
currentPeriodTimeseries?: Coordinate[];
|
||||
previousPeriodTimeseries?: Coordinate[];
|
||||
}) {
|
||||
if (!previousPeriodTimeseries) {
|
||||
if (!previousPeriodTimeseries?.length) {
|
||||
return [];
|
||||
}
|
||||
const currentPeriodStart = currentPeriodTimeseries?.length
|
||||
? currentPeriodTimeseries[0].x
|
||||
: 0;
|
||||
|
||||
const dateDiff = currentPeriodStart - previousPeriodStart;
|
||||
const dateDiff = currentPeriodStart - previousPeriodTimeseries[0].x;
|
||||
|
||||
return previousPeriodTimeseries.map(({ x, y }) => {
|
||||
const offsetX = moment(x).add(dateDiff).valueOf();
|
||||
|
|
|
@ -1,31 +1,182 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`APM API tests trial apm_8.0.0 Transaction latency with a trial license when data is loaded when not defined environment is selected should return the correct anomaly boundaries 1`] = `
|
||||
exports[`APM API tests basic apm_8.0.0 Latency with a basic license when data is loaded time comparison returns some data 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"x": 1607436000000,
|
||||
"y": 0,
|
||||
"y0": 0,
|
||||
"x": 1607436780000,
|
||||
"y": 51029,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436900000,
|
||||
"y": 0,
|
||||
"y0": 0,
|
||||
"x": 1607436820000,
|
||||
"y": 38124,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436860000,
|
||||
"y": 16327,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436980000,
|
||||
"y": 35617.5,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436990000,
|
||||
"y": 34599.75,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437100000,
|
||||
"y": 26980,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437110000,
|
||||
"y": 42808,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437130000,
|
||||
"y": 22230.5,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437220000,
|
||||
"y": 34973,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437230000,
|
||||
"y": 19284.2,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437240000,
|
||||
"y": 9280,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437250000,
|
||||
"y": 42777,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437260000,
|
||||
"y": 10702,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437340000,
|
||||
"y": 22452,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437470000,
|
||||
"y": 14495.5,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437480000,
|
||||
"y": 11644.5714285714,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437570000,
|
||||
"y": 17359.6666666667,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437590000,
|
||||
"y": 11394.2,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`APM API tests trial apm_8.0.0 Transaction latency with a trial license when data is loaded with environment selected and empty kuery filter should return a non-empty anomaly series 1`] = `
|
||||
exports[`APM API tests basic apm_8.0.0 Latency with a basic license when data is loaded time comparison returns some data 2`] = `
|
||||
Array [
|
||||
Object {
|
||||
"x": 1607436800000,
|
||||
"y": 23448.25,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436820000,
|
||||
"y": 25181,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436840000,
|
||||
"y": 16834,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436910000,
|
||||
"y": 21582,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437040000,
|
||||
"y": 31800,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437050000,
|
||||
"y": 21341,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437060000,
|
||||
"y": 21108.5,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437150000,
|
||||
"y": 12147.3333333333,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437160000,
|
||||
"y": 23941.5,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437180000,
|
||||
"y": 18244,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437240000,
|
||||
"y": 24359.5,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437280000,
|
||||
"y": 27767,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437290000,
|
||||
"y": 21909.6666666667,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437390000,
|
||||
"y": 31521,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437410000,
|
||||
"y": 20227.5,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437420000,
|
||||
"y": 18664,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437510000,
|
||||
"y": 14197.5,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437520000,
|
||||
"y": 19199.8571428571,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437540000,
|
||||
"y": 63745.75,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437640000,
|
||||
"y": 63220,
|
||||
},
|
||||
Object {
|
||||
"x": 1607437660000,
|
||||
"y": 20040,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`APM API tests trial apm_8.0.0 Transaction latency with a trial license when data is loaded when not defined environments is seleted should return the correct anomaly boundaries 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"x": 1607436000000,
|
||||
"y": 1625128.56211579,
|
||||
"y0": 7533.02707532227,
|
||||
"y": 0,
|
||||
"y0": 0,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436900000,
|
||||
"y": 1660982.24115757,
|
||||
"y0": 5732.00699123528,
|
||||
"y": 0,
|
||||
"y0": 0,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
@ -34,13 +185,13 @@ exports[`APM API tests trial apm_8.0.0 Transaction latency with a trial license
|
|||
Array [
|
||||
Object {
|
||||
"x": 1607436000000,
|
||||
"y": 1625128.56211579,
|
||||
"y0": 7533.02707532227,
|
||||
"y": 136610.507897203,
|
||||
"y0": 22581.5157631454,
|
||||
},
|
||||
Object {
|
||||
"x": 1607436900000,
|
||||
"y": 1660982.24115757,
|
||||
"y0": 5732.00699123528,
|
||||
"y": 136610.507897203,
|
||||
"y0": 22581.5157631454,
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -6,21 +6,22 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import url from 'url';
|
||||
import moment from 'moment';
|
||||
import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi';
|
||||
import { PromiseReturnType } from '../../../../plugins/observability/typings/common';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata';
|
||||
import { registry } from '../../common/registry';
|
||||
|
||||
type LatencyChartReturnType = APIReturnType<'GET /api/apm/services/{serviceName}/transactions/charts/latency'>;
|
||||
|
||||
export default function ApiTest({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
|
||||
const archiveName = 'apm_8.0.0';
|
||||
|
||||
const range = archives_metadata[archiveName];
|
||||
|
||||
// url parameters
|
||||
const start = encodeURIComponent(range.start);
|
||||
const end = encodeURIComponent(range.end);
|
||||
const { start, end } = archives_metadata[archiveName];
|
||||
|
||||
registry.when(
|
||||
'Latency with a basic license when data is not loaded ',
|
||||
|
@ -28,7 +29,15 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
() => {
|
||||
it('returns 400 when latencyAggregationType is not informed', async () => {
|
||||
const response = await supertest.get(
|
||||
`/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=request`
|
||||
url.format({
|
||||
pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
transactionType: 'request',
|
||||
environment: 'testing',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(response.status).to.be(400);
|
||||
|
@ -36,7 +45,15 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
it('returns 400 when transactionType is not informed', async () => {
|
||||
const response = await supertest.get(
|
||||
`/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&latencyAggregationType=avg`
|
||||
url.format({
|
||||
pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: 'avg',
|
||||
environment: 'testing',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(response.status).to.be(400);
|
||||
|
@ -44,13 +61,25 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
it('handles the empty state', async () => {
|
||||
const response = await supertest.get(
|
||||
`/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&latencyAggregationType=avg&transactionType=request`
|
||||
url.format({
|
||||
pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: 'avg',
|
||||
transactionType: 'request',
|
||||
environment: 'testing',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(response.status).to.be(200);
|
||||
|
||||
expect(response.body.overallAvgDuration).to.be(null);
|
||||
expect(response.body.latencyTimeseries.length).to.be(0);
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).to.be(null);
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.length).to.be(0);
|
||||
expect(latencyChartReturn.previousPeriod.latencyTimeseries.length).to.be(0);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
@ -64,42 +93,113 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
describe('average latency type', () => {
|
||||
before(async () => {
|
||||
response = await supertest.get(
|
||||
`/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=request&latencyAggregationType=avg`
|
||||
url.format({
|
||||
pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: 'avg',
|
||||
transactionType: 'request',
|
||||
environment: 'testing',
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns average duration and timeseries', async () => {
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body.overallAvgDuration).not.to.be(null);
|
||||
expect(response.body.latencyTimeseries.length).to.be.eql(61);
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).not.to.be(null);
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.length).to.be.eql(61);
|
||||
});
|
||||
});
|
||||
|
||||
describe('95th percentile latency type', () => {
|
||||
before(async () => {
|
||||
response = await supertest.get(
|
||||
`/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=request&latencyAggregationType=p95`
|
||||
url.format({
|
||||
pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: 'p95',
|
||||
transactionType: 'request',
|
||||
environment: 'testing',
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns average duration and timeseries', async () => {
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body.overallAvgDuration).not.to.be(null);
|
||||
expect(response.body.latencyTimeseries.length).to.be.eql(61);
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).not.to.be(null);
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.length).to.be.eql(61);
|
||||
});
|
||||
});
|
||||
|
||||
describe('99th percentile latency type', () => {
|
||||
before(async () => {
|
||||
response = await supertest.get(
|
||||
`/api/apm/services/opbeans-node/transactions/charts/latency?environment=testing&start=${start}&end=${end}&transactionType=request&latencyAggregationType=p99`
|
||||
url.format({
|
||||
pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: 'p99',
|
||||
transactionType: 'request',
|
||||
environment: 'testing',
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns average duration and timeseries', async () => {
|
||||
expect(response.status).to.be(200);
|
||||
expect(response.body.overallAvgDuration).not.to.be(null);
|
||||
expect(response.body.latencyTimeseries.length).to.be.eql(61);
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn.currentPeriod.overallAvgDuration).not.to.be(null);
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.length).to.be.eql(61);
|
||||
});
|
||||
});
|
||||
|
||||
describe('time comparison', () => {
|
||||
before(async () => {
|
||||
response = await supertest.get(
|
||||
url.format({
|
||||
pathname: `/api/apm/services/opbeans-node/transactions/charts/latency`,
|
||||
query: {
|
||||
latencyAggregationType: 'avg',
|
||||
transactionType: 'request',
|
||||
start: moment(end).subtract(15, 'minutes').toISOString(),
|
||||
end,
|
||||
comparisonStart: start,
|
||||
comparisonEnd: moment(start).add(15, 'minutes').toISOString(),
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('returns some data', async () => {
|
||||
expect(response.status).to.be(200);
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
const currentPeriodNonNullDataPoints = latencyChartReturn.currentPeriod.latencyTimeseries.filter(
|
||||
({ y }) => y !== null
|
||||
);
|
||||
expect(currentPeriodNonNullDataPoints.length).to.be.greaterThan(0);
|
||||
const previousPeriodNonNullDataPoints = latencyChartReturn.previousPeriod.latencyTimeseries.filter(
|
||||
({ y }) => y !== null
|
||||
);
|
||||
expect(previousPeriodNonNullDataPoints.length).to.be.greaterThan(0);
|
||||
|
||||
expectSnapshot(currentPeriodNonNullDataPoints).toMatch();
|
||||
expectSnapshot(previousPeriodNonNullDataPoints).toMatch();
|
||||
});
|
||||
|
||||
it('matches x-axis on current period and previous period', () => {
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn.currentPeriod.latencyTimeseries.map(({ x }) => x)).to.be.eql(
|
||||
latencyChartReturn.previousPeriod.latencyTimeseries.map(({ x }) => x)
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -116,7 +216,15 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
describe('without an environment', () => {
|
||||
before(async () => {
|
||||
response = await supertest.get(
|
||||
`/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg`
|
||||
url.format({
|
||||
pathname: `/api/apm/services/opbeans-java/transactions/charts/latency`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: 'avg',
|
||||
transactionType,
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -128,7 +236,16 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
describe('with environment selected', () => {
|
||||
before(async () => {
|
||||
response = await supertest.get(
|
||||
`/api/apm/services/opbeans-java/transactions/charts/latency?environment=production&start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg`
|
||||
url.format({
|
||||
pathname: `/api/apm/services/opbeans-python/transactions/charts/latency`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: 'avg',
|
||||
transactionType,
|
||||
environment: 'production',
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -137,24 +254,37 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('should return the ML job id for anomalies of the selected environment', () => {
|
||||
expect(response.body).to.have.property('anomalyTimeseries');
|
||||
expect(response.body.anomalyTimeseries).to.have.property('jobId');
|
||||
expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline(
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn).to.have.property('anomalyTimeseries');
|
||||
expect(latencyChartReturn.anomalyTimeseries).to.have.property('jobId');
|
||||
expectSnapshot(latencyChartReturn.anomalyTimeseries?.jobId).toMatchInline(
|
||||
`"apm-production-1369-high_mean_transaction_duration"`
|
||||
);
|
||||
});
|
||||
|
||||
it('should return a non-empty anomaly series', () => {
|
||||
expect(response.body).to.have.property('anomalyTimeseries');
|
||||
expect(response.body.anomalyTimeseries.anomalyBoundaries?.length).to.be.greaterThan(0);
|
||||
expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch();
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn).to.have.property('anomalyTimeseries');
|
||||
expect(latencyChartReturn.anomalyTimeseries?.anomalyBoundaries?.length).to.be.greaterThan(
|
||||
0
|
||||
);
|
||||
expectSnapshot(latencyChartReturn.anomalyTimeseries?.anomalyBoundaries).toMatch();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when not defined environment is selected', () => {
|
||||
describe('when not defined environments is seleted', () => {
|
||||
before(async () => {
|
||||
response = await supertest.get(
|
||||
`/api/apm/services/opbeans-python/transactions/charts/latency?environment=ENVIRONMENT_NOT_DEFINED&start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg`
|
||||
url.format({
|
||||
pathname: `/api/apm/services/opbeans-python/transactions/charts/latency`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: 'avg',
|
||||
transactionType,
|
||||
environment: 'ENVIRONMENT_NOT_DEFINED',
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -163,23 +293,34 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('should return the ML job id for anomalies with no defined environment', () => {
|
||||
expect(response.body).to.have.property('anomalyTimeseries');
|
||||
expect(response.body.anomalyTimeseries).to.have.property('jobId');
|
||||
expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline(
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn).to.have.property('anomalyTimeseries');
|
||||
expect(latencyChartReturn.anomalyTimeseries).to.have.property('jobId');
|
||||
expectSnapshot(latencyChartReturn.anomalyTimeseries?.jobId).toMatchInline(
|
||||
`"apm-environment_not_defined-5626-high_mean_transaction_duration"`
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the correct anomaly boundaries', () => {
|
||||
expect(response.body).to.have.property('anomalyTimeseries');
|
||||
expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch();
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn).to.have.property('anomalyTimeseries');
|
||||
expectSnapshot(latencyChartReturn.anomalyTimeseries?.anomalyBoundaries).toMatch();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with all environments selected', () => {
|
||||
before(async () => {
|
||||
response = await supertest.get(
|
||||
`/api/apm/services/opbeans-java/transactions/charts/latency?environment=ENVIRONMENT_ALL&start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg`
|
||||
url.format({
|
||||
pathname: `/api/apm/services/opbeans-java/transactions/charts/latency`,
|
||||
query: {
|
||||
start,
|
||||
end,
|
||||
latencyAggregationType: 'avg',
|
||||
transactionType,
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -188,33 +329,8 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('should not return anomaly timeseries data', () => {
|
||||
expect(response.body).to.not.have.property('anomalyTimeseries');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with environment selected and empty kuery filter', () => {
|
||||
before(async () => {
|
||||
response = await supertest.get(
|
||||
`/api/apm/services/opbeans-java/transactions/charts/latency?environment=production&start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg`
|
||||
);
|
||||
});
|
||||
|
||||
it('should have a successful response', () => {
|
||||
expect(response.status).to.eql(200);
|
||||
});
|
||||
|
||||
it('should return the ML job id for anomalies of the selected environment', () => {
|
||||
expect(response.body).to.have.property('anomalyTimeseries');
|
||||
expect(response.body.anomalyTimeseries).to.have.property('jobId');
|
||||
expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline(
|
||||
`"apm-production-1369-high_mean_transaction_duration"`
|
||||
);
|
||||
});
|
||||
|
||||
it('should return a non-empty anomaly series', () => {
|
||||
expect(response.body).to.have.property('anomalyTimeseries');
|
||||
expect(response.body.anomalyTimeseries.anomalyBoundaries?.length).to.be.greaterThan(0);
|
||||
expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch();
|
||||
const latencyChartReturn = response.body as LatencyChartReturnType;
|
||||
expect(latencyChartReturn).to.not.have.property('anomalyTimeseries');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -77,8 +77,8 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
expect(Object.keys(currentPeriod).sort()).to.be.eql(transactionNames.sort());
|
||||
|
||||
const currentPeriodItems = Object.values(currentPeriod).map((data) => data);
|
||||
const previousPeriodItems = Object.values(previousPeriod).map((data) => data);
|
||||
const currentPeriodItems = Object.values(currentPeriod);
|
||||
const previousPeriodItems = Object.values(previousPeriod);
|
||||
|
||||
expect(previousPeriodItems.length).to.be.eql(0);
|
||||
|
||||
|
@ -210,8 +210,8 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('returns correct latency data', () => {
|
||||
const currentPeriodItems = Object.values(currentPeriod).map((data) => data);
|
||||
const previousPeriodItems = Object.values(previousPeriod).map((data) => data);
|
||||
const currentPeriodItems = Object.values(currentPeriod);
|
||||
const previousPeriodItems = Object.values(previousPeriod);
|
||||
|
||||
const currentPeriodFirstItem = currentPeriodItems[0];
|
||||
const previousPeriodFirstItem = previousPeriodItems[0];
|
||||
|
@ -227,8 +227,8 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('returns correct throughput data', () => {
|
||||
const currentPeriodItems = Object.values(currentPeriod).map((data) => data);
|
||||
const previousPeriodItems = Object.values(previousPeriod).map((data) => data);
|
||||
const currentPeriodItems = Object.values(currentPeriod);
|
||||
const previousPeriodItems = Object.values(previousPeriod);
|
||||
|
||||
const currentPeriodFirstItem = currentPeriodItems[0];
|
||||
const previousPeriodFirstItem = previousPeriodItems[0];
|
||||
|
@ -244,8 +244,8 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('returns correct error rate data', () => {
|
||||
const currentPeriodItems = Object.values(currentPeriod).map((data) => data);
|
||||
const previousPeriodItems = Object.values(previousPeriod).map((data) => data);
|
||||
const currentPeriodItems = Object.values(currentPeriod);
|
||||
const previousPeriodItems = Object.values(previousPeriod);
|
||||
|
||||
const currentPeriodFirstItem = currentPeriodItems[0];
|
||||
const previousPeriodFirstItem = previousPeriodItems[0];
|
||||
|
@ -256,13 +256,26 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
expect(
|
||||
removeEmptyCoordinates(previousPeriodFirstItem.errorRate).length
|
||||
).to.be.greaterThan(0);
|
||||
|
||||
expectSnapshot(currentPeriodFirstItem.errorRate).toMatch();
|
||||
expectSnapshot(previousPeriodFirstItem.errorRate).toMatch();
|
||||
});
|
||||
|
||||
it('matches x-axis on current period and previous period', () => {
|
||||
const currentPeriodItems = Object.values(currentPeriod);
|
||||
const previousPeriodItems = Object.values(previousPeriod);
|
||||
|
||||
const currentPeriodFirstItem = currentPeriodItems[0];
|
||||
const previousPeriodFirstItem = previousPeriodItems[0];
|
||||
|
||||
expect(currentPeriodFirstItem.errorRate.map(({ x }) => x)).to.be.eql(
|
||||
previousPeriodFirstItem.errorRate.map(({ x }) => x)
|
||||
);
|
||||
});
|
||||
|
||||
it('returns correct impact data', () => {
|
||||
const currentPeriodItems = Object.values(currentPeriod).map((data) => data);
|
||||
const previousPeriodItems = Object.values(previousPeriod).map((data) => data);
|
||||
const currentPeriodItems = Object.values(currentPeriod);
|
||||
const previousPeriodItems = Object.values(previousPeriod);
|
||||
|
||||
const currentPeriodFirstItem = currentPeriodItems[0];
|
||||
const previousPeriodFirstItem = previousPeriodItems[0];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue